diff --git a/Code/Mantid/Framework/API/inc/MantidAPI/MultipleFileProperty.h b/Code/Mantid/Framework/API/inc/MantidAPI/MultipleFileProperty.h index 35d94fe3a9b7b940bff18776fb8da0b19de11b20..4eeb02eeeab2f5df851b2e26bcde15d6fed818c4 100644 --- a/Code/Mantid/Framework/API/inc/MantidAPI/MultipleFileProperty.h +++ b/Code/Mantid/Framework/API/inc/MantidAPI/MultipleFileProperty.h @@ -1,6 +1,7 @@ #ifndef MANTID_API_MULTIPLEFILEPROPERTY_H_ #define MANTID_API_MULTIPLEFILEPROPERTY_H_ +#include "MantidKernel/Logger.h" #include "MantidKernel/PropertyWithValue.h" #include "MantidKernel/System.h" #include "MantidKernel/MultiFileNameParser.h" @@ -12,8 +13,70 @@ namespace Mantid namespace API { - /** A property to allow a user to select multiple files to load. + /** + A property to allow a user to specify multiple files to load. + + The current functionality is such that there are two basic forms of syntax. For the puposes + of documentation we'll call these the "Long Form" and "Short Form". + + ------------------------------------------------------------------------------------------------------ + [A] Short Form + + These strings are of the format "[dir][inst][under][runs][ext]" where: + + [dir] (Optional) = The OS-specific file directory, e.g. "c:/data/" + [inst] (Optional) = The instrument name, e.g. "IRS" or "PG3". + [under] (Optional) = Some instrument filenames require an underscore. + [runs] (Required) = The run numbers, e.g. "0102, 0110-0115, 0120, 0130:0140:2" + [ext] (Optional) = The file extension, e.g. ".raw" + + For optional values, defaults or user settings are used where necessary. + For [runs], users specify lists and ranges of runs using comma, plus, minus and colon. Some examples: + + "TSC0001,0002" = Runs 1 and 2 of the TOSCA instrument are to be loaded. + "0003+0004" = Runs 3 and 4 of the default instrument are to be loaded and added together. + "0005:0009.raw" = The raw files containing runs 5 to 9 of the default instrument are to be loaded. + "c:/data/0010-0014" = The files in "c:/data/" containing runs 10 to 14 of the default instrument are + to be loaded and added together. + "IRS0020:0028:2.nxs" = The nexus files containing runs 20, 22, 24, 26 and 28 for IRIS are to be loaded. + "INST_0030-0038:3" = Runs 30, 33, and 36 of INST are to be loaded and added together. + + ------------------------------------------------------------------------------------------------------ + [B] Long Form + + These strings are of the format "[[short_form][operator]]...[short_form]" where: + + [short_form] = [dir][inst][under][runs][ext], which is the "Short Form" outlined above. + [operator] = Either a comma or a plus. + + Some examples: + + "TSC0001,TSC0002+0003" = Runs 1, 2 and 3 of the TOSCA instrument should be loaded, but 2 and 3 + are added together. + "TSC0005+TSC0006,TSC0007.raw" = Runs 5 and 6 as well as the raw file containing run 7 of the TOSCA + instrument should be loaded, but 5 and 6 are added together. + + ------------------------------------------------------------------------------------------------------ + NOTES: + + [1] Presently, we disallow more complex algebra such as "TSC0005,0006+TSC0007". In such a case it is + ambiguous whether or not the user wishes to just add run 7 to 6, or add run 7 to both 5 and 6. + + [2] The "Short Form" is parsed by the Kernel::MultiFileNameParsing::Parser class, whereas this class is + responsible for splitting up the Long Form. + + [3] The functionality of this class is such that all strings are stored only after being converted to + the Long Form, and all filenames are fully resolved. For example "0005,0006+0007" is stored as + "[dir][inst][under]0005[ext],[dir][inst][under]0006[ext]+[dir][inst][under]0007[ext]". + + [4] The default functionality of this Property can be changed to emulate a simple FileProperty - to do + this, the user must change the properties file. Disabling multi file loading in this way will allow + users to use "," and "+" in their filenames, and in this case we use the dummy "" delimiters to call + toValue and toString. + + ------------------------------------------------------------------------------------------------------ + Copyright © 2011 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory This file is part of Mantid. @@ -45,8 +108,10 @@ namespace API /// 'Virtual copy constructor virtual MultipleFileProperty* clone() const { return new MultipleFileProperty(*this); } - /// Overridden setValue method + /// Overridden functions to accomodate std::vector<std::vector<std::string>>> structure of this property. virtual std::string setValue(const std::string & propValue); + virtual std::string value() const; + virtual std::string getDefault() const; /// @return the vector of suggested extensions. For use in GUIs showing files. std::set<std::string> getExts() const @@ -65,12 +130,17 @@ namespace API static std::vector<std::string> flattenFileNames(const std::vector<std::vector<std::string> > & fileNames); private: + /// Whether or not the user has turned on multifile loading. + bool m_multiFileLoadingEnabled; + /// Suggested extensions std::vector<std::string> m_exts; /// Parser used to parse multi-file strings. Kernel::MultiFileNameParsing::Parser m_parser; ///The default file extension associated with the type of file this property will handle std::string m_defaultExt; + /// Reference to the logger class + Kernel::Logger& g_log; }; diff --git a/Code/Mantid/Framework/API/src/MultipleFileProperty.cpp b/Code/Mantid/Framework/API/src/MultipleFileProperty.cpp index 952db89287819bed62c211c80254224ac79caa36..536968a94169453d570c9f3351c429e7a53fa5d4 100644 --- a/Code/Mantid/Framework/API/src/MultipleFileProperty.cpp +++ b/Code/Mantid/Framework/API/src/MultipleFileProperty.cpp @@ -1,15 +1,18 @@ -#include "MantidAPI/MultipleFileProperty.h" + #include "MantidAPI/FileProperty.h" -#include "MantidKernel/System.h" -#include "MantidKernel/MultiFileValidator.h" -#include "MantidKernel/Property.h" -#include <Poco/Path.h> #include "MantidAPI/FileFinder.h" +#include "MantidAPI/MultipleFileProperty.h" -#include <ctype.h> +#include "MantidKernel/ConfigService.h" +#include "MantidKernel/MultiFileValidator.h" +#include "MantidKernel/Property.h" +#include "MantidKernel/System.h" +#include <Poco/Path.h> #include <boost/algorithm/string.hpp> +#include <boost/regex.hpp> +#include <ctype.h> #include <functional> #include <numeric> @@ -23,6 +26,10 @@ namespace API // Forward declarations namespace { + /** + * A functor that stores a list of extensions and then accumulates the full, resolved file + * names that are passed to it on to an output string. Used with the accumulate STL algorithm. + */ class AppendFullFileName { public: @@ -37,35 +44,48 @@ namespace API std::vector<std::vector<std::string> > unflattenFileNames( const std::vector<std::string> & flattenedFileNames); + + std::string toSingleString(const std::vector<std::vector<std::string>> & filenames); } - /** Constructor + /** + * Constructor * * @param name :: The name of the property * @param exts :: The allowed/suggested extensions - * @param optional :: If ture, the property is optional + * @param optional :: If true, the property is optional */ MultipleFileProperty::MultipleFileProperty( const std::string & name, const std::vector<std::string> & exts - ) : PropertyWithValue<std::vector<std::vector<std::string> > >( - name, - std::vector<std::vector<std::string> >(), - boost::make_shared<MultiFileValidator>(exts), - Direction::Input), + ) : PropertyWithValue<std::vector<std::vector<std::string> > >( name, + std::vector<std::vector<std::string> >(), boost::make_shared<MultiFileValidator>(exts), Direction::Input), + m_multiFileLoadingEnabled(), m_exts(exts), m_parser(), - m_defaultExt("") - {} + m_defaultExt(""), + g_log(Kernel::Logger::get("MultipleFileProperty")) + { + std::string allowMultiFileLoading = Kernel::ConfigService::Instance().getString("loading.multifile"); - //---------------------------------------------------------------------------------------------- - /** Destructor + if( boost::iequals(allowMultiFileLoading, "On") ) + m_multiFileLoadingEnabled = true; + else + m_multiFileLoadingEnabled = false; + } + + /** + * Destructor */ MultipleFileProperty::~MultipleFileProperty() {} - /** Set the value, with a comma- and plus-separated string of filenames + /** + * Convert the given propValue into a comma and plus separated list of full filenames, and pass to the parent's + * setValue method to store as a vector of vector of strings. + * + * READ HEADER FILE DOCUMENTATION FOR A MORE DETAILED OVERVIEW. * - * @param propValue :: comma- and plus-separated string of filenames + * @param propValue :: A string of the allowed format, indicating the user's choice of files. * @return A string indicating the outcome of the attempt to set the property. An empty string indicates success. */ std::string MultipleFileProperty::setValue(const std::string & propValue) @@ -74,74 +94,163 @@ namespace API if( propValue.empty()) return "No file(s) specified."; - std::string value = propValue; + if( ! m_multiFileLoadingEnabled ) + { + g_log.debug("MultiFile loading is not enabled, acting as standard FileProperty."); + + // Use a slave FileProperty to do the job for us. + FileProperty slaveFileProp( "Slave", "", FileProperty::Load, m_exts, Direction::Input); + + std::string error = slaveFileProp.setValue(propValue); + + if(!error.empty()) + return error; + + // Store. + try + { + std::vector<std::vector<std::string>> result; + toValue(slaveFileProp(), result, "", ""); + PropertyWithValue<std::vector<std::vector<std::string> > >::operator=(result); + return ""; + } + catch ( std::invalid_argument& except) + { + g_log.debug() << "Could not set property " << name() << ": " << except.what(); + return except.what(); + } + return ""; + } + + const std::string INVALID = "\\+\\+|,,|\\+,|,\\+"; + boost::smatch invalid_substring; + if( boost::regex_search( + propValue.begin(), propValue.end(), + invalid_substring, + boost::regex(INVALID)) ) + return "Unable to parse filename due to an empty token."; + + // Else if multifile loading *is* enabled, then users make the concession that they cannot use "," or "+" in + // directory names; they are used as operators only. + const std::string NUM_COMMA_ALPHA = "(?<=\\d)\\s*,\\s*(?=\\D)"; + const std::string ALPHA_COMMA_ALPHA = "(?<=\\D)\\s*,\\s*(?=\\D)"; + const std::string NUM_PLUS_ALPHA = "(?<=\\d)\\s*\\+\\s*(?=\\D)"; + const std::string ALPHA_PLUS_ALPHA = "(?<=\\D)\\s*\\+\\s*(?=\\D)"; + const std::string COMMA_OPERATORS = NUM_COMMA_ALPHA + "|" + ALPHA_COMMA_ALPHA; + const std::string PLUS_OPERATORS = NUM_PLUS_ALPHA + "|" + ALPHA_PLUS_ALPHA; std::stringstream errorMsg; - // Assume a format of "dir/inst_1,2,...n.raw", and try to parse using parser. + // Tokenise on allowed comma operators, and iterate over each token. + boost::sregex_token_iterator end; + boost::sregex_token_iterator commaToken( + propValue.begin(), propValue.end(), + boost::regex(COMMA_OPERATORS), -1); + + std::vector<std::vector<std::string> > fileNames; + try { - m_parser.parse(value); + for(; commaToken != end; ++commaToken) + { + const std::string comma = commaToken->str(); + + // Tokenise on allowed plus operators, and iterate over each token. + boost::sregex_token_iterator plusToken( + comma.begin(), comma.end(), + boost::regex(PLUS_OPERATORS, boost::regex_constants::perl), -1); + + std::vector<std::vector<std::vector<std::string>>> temp; + + for(; plusToken != end; ++plusToken) + { + const std::string plus = plusToken->str(); + + try + { + m_parser.parse(plus); + } + catch(const std::runtime_error & re) + { + errorMsg << "Unable to parse runs: \"" << re.what() << "\". "; + } + + std::vector<std::vector<std::string>> f = m_parser.fileNames(); + + // If there are no files, then we should use this token as it was passed to the property, + // in its untampered form. This will enable us to deal with the case where a user is trying to + // load a single (and possibly existing) file within a token, but which has unexpected zero + // padding, or some other anomaly. + if( flattenFileNames(f).size() == 0 ) + f.push_back(std::vector<std::string>(1, plus)); + + temp.push_back(f); + } + + // See [3] in header documentation. Basically, for reasons of ambiguity, we cant add + // together plusTokens if they contain more than one file. Throw on any instances of this. + if( temp.size() > 1 ) + { + for(auto tempFiles = temp.begin(); tempFiles != temp.end(); ++tempFiles) + if( flattenFileNames(*tempFiles).size() > 1 ) + throw std::runtime_error("Adding a range of files to another file(s) is not currently supported."); + } + + for( auto multifile = temp.begin(); multifile != temp.end(); ++multifile ) + fileNames.insert( + fileNames.end(), + multifile->begin(), multifile->end()); + } } catch(const std::runtime_error & re) { - errorMsg << "Unable to parse multi file runs: \"" << re.what() << "\". "; + errorMsg << "Unable to parse runs: \"" << re.what() << "\". "; + return errorMsg.str(); } - std::vector<std::vector<std::string> > fileNames = m_parser.fileNames(); + if(fileNames.size() == 1 && fileNames[0].size() == 1) + fileNames[0][0] = propValue; - AppendFullFileName appendFullFileName(m_exts); - std::string fullFileNames(""); - - // If unsuccessful, then assume a format of: - // - // "dir/inst_1.raw, dir/inst_2.raw, ... dir/inst_n.raw" (where n may equal 1). - // - // Tokenise on commas, and and try to find full files names of each token. - if(fileNames.empty()) + + std::string fullFileNames = ""; + try { - std::vector<std::string> tokens; - tokens = boost::split(tokens, value, boost::is_any_of(",")); - fileNames = unflattenFileNames(tokens); - try - { - fullFileNames = std::accumulate( - fileNames.begin(), fileNames.end(), - std::string(""), - appendFullFileName); - } - catch(const std::runtime_error & re) - { - errorMsg << "Tried to find as single file(s), but also failed: \"" << re.what() << "\"."; - return errorMsg.str(); - } + // Use an AppendFullFileName functor object with std::accumulate to append + // full filenames to a single string. + AppendFullFileName appendFullFileName(m_exts); + fullFileNames = std::accumulate( + fileNames.begin(), fileNames.end(), + std::string(""), + appendFullFileName); } - // Else, for each file name in the vector, change it into a full file name where possible, - // then append it onto a comma- and plus-separated string. - else + catch(const std::runtime_error & re) { - // If there is only one file, then we should use the string passed to the property, which - // has not been tampered with. This will enable us to deal with the case where a user is - // trying to load a single file with incorrect zero padding, or some other anomaly. - if(fileNames.size() == 1 && fileNames[0].size() == 1) - fileNames[0][0] = propValue; - - try - { - fullFileNames = std::accumulate( - fileNames.begin(), fileNames.end(), - std::string(""), - appendFullFileName); - } - catch(const std::runtime_error & re) - { - return re.what(); - } - } + return re.what(); + } // Now re-set the value using the full paths found. return PropertyWithValue<std::vector<std::vector<std::string> > >::setValue(fullFileNames); } + + std::string MultipleFileProperty::value() const + { + if( ! m_multiFileLoadingEnabled ) + return toString(m_value, "", ""); + + return toString(m_value); + } + + /** + * Get the value the property was initialised with -its default value + * @return The default value + */ + std::string MultipleFileProperty::getDefault() const + { + if( ! m_multiFileLoadingEnabled ) + return toString(m_initialValue, "", ""); + + return toString(m_initialValue); + } /** * A convenience function for the cases where we dont use the MultiFileProperty to @@ -186,12 +295,14 @@ namespace API m_exts(exts) {} - /** Takes in a vector of filenames, tries to find their full path if possible, then cumulatively appends - * them to the result string. - * @param result :: the cumulative result so far - * @param fileNames :: the name to look for, and append to the result - * @return the cumulative result, after the filenames have been appended. - */ + /** + * Takes in a vector of filenames, tries to find their full path if possible, then cumulatively appends + * them to the result string. + * + * @param result :: the cumulative result so far + * @param fileNames :: the name to look for, and append to the result + * @return the cumulative result, after the filenames have been appended. + */ std::string & AppendFullFileName::operator()(std::string & result, const std::vector<std::string> & fileNames) { // Append nothing if there are no file names to add. @@ -215,12 +326,14 @@ namespace API return result; } - /** Takes in a filename, tries to find it's full path if possible, then cumulatively appends it to a result string. - * @param result :: the cumulative result so far - * @param fileName :: the name to look for, and append to the result - * @return the cumulative result, after the filename has been appended. - * @throws std::runtime_error if an individual filename could not be set to the FileProperty object - */ + /** + * Takes in a filename, tries to find it's full path if possible, then cumulatively appends it to a result string. + * + * @param result :: the cumulative result so far + * @param fileName :: the name to look for, and append to the result + * @return the cumulative result, after the filename has been appended. + * @throws std::runtime_error if an individual filename could not be set to the FileProperty object + */ std::string & AppendFullFileName::operator()(std::string & result, const std::string & fileName) { // Append nothing if there is no file name to add. @@ -235,12 +348,7 @@ namespace API boost::algorithm::trim(value); // Initialise a "slave" FileProperty object to do all the work. - FileProperty slaveFileProp( - "Slave", - "", - FileProperty::Load, - m_exts, - Direction::Input); + FileProperty slaveFileProp("Slave", "", FileProperty::Load, m_exts, Direction::Input); std::string error = slaveFileProp.setValue(value); @@ -272,6 +380,37 @@ namespace API return unflattenedFileNames; } + + /** + * Converts a vector of vector of strings into a single comma and plus separated string. + * For example [["a", "b"],["x", "y", "z"]] into "a+b,x+y+z". + * + * @param - vector of vector of strings (filenames). + * + * @returns a single comma and plus separated string. + */ + std::string toSingleString(const std::vector<std::vector<std::string>> & filenames) + { + std::string result; + + for( auto filenameList = filenames.begin(); filenameList != filenames.end(); ++filenameList) + { + std::string innerResult = ""; + + for( auto filename = filenameList->begin(); filename != filenameList->end(); ++filename) + { + if( ! innerResult.empty() ) + innerResult += "+"; + innerResult += *filename; + } + + if( ! result.empty() ) + result += ","; + result += innerResult; + } + + return result; + } } // anonymous namespace } // namespace Mantid diff --git a/Code/Mantid/Framework/API/test/MultipleFilePropertyTest.h b/Code/Mantid/Framework/API/test/MultipleFilePropertyTest.h index 8f2e74d7556fde1a27b145e8e85596ae891759e8..9ac93452380f4deba6ad9acd2113a7a5f10078ae 100644 --- a/Code/Mantid/Framework/API/test/MultipleFilePropertyTest.h +++ b/Code/Mantid/Framework/API/test/MultipleFilePropertyTest.h @@ -16,10 +16,27 @@ using namespace Mantid::API; class MultipleFilePropertyTest : public CxxTest::TestSuite { -public: +private: + std::string m_multiFileLoading; +public: + + void setUp() + { + // Make sure that multi file loading is enabled for each test. + m_multiFileLoading = Kernel::ConfigService::Instance().getString("loading.multifile"); + Kernel::ConfigService::Instance().setString("loading.multifile", "On"); + } + + void tearDown() + { + // Replace user's preference after the test has run. + Kernel::ConfigService::Instance().setString("loading.multifile", m_multiFileLoading); + } + void test_setValue() { MultipleFileProperty p("Filename"); + // REF_L example is important since the instrument has no zero padding value. p.setValue("REF_L_32035.nxs, CSP78173.raw"); std::vector<std::vector<std::string> > filenames = p(); TS_ASSERT_EQUALS( filenames.size(), 2); @@ -45,13 +62,44 @@ public: TS_ASSERT_EQUALS(fileNames[3].size(), 1); } + void test_failsOnComplexAddition() + { + MultipleFileProperty p("Filename"); + p.setValue("MUSR15189:15190+MUSR15189"); + std::vector<std::vector<std::string>> fileNames = p(); + TS_ASSERT_EQUALS(fileNames.size(), 0); + } + + void test_failsOnBadlyFormedFilename() + { + MultipleFileProperty p("Filename"); + p.setValue("MUSR15189,,MUSR15189"); + std::vector<std::vector<std::string>> fileNames = p(); + TS_ASSERT_EQUALS(fileNames.size(), 0); + } + + void test_multiFileSwitchedOff() + { + Kernel::ConfigService::Instance().setString("loading.multifile", "Off"); + + std::string filename = "_MultipleFilePropertyTest_tempFileWithA+AndA,InTheName.txt"; + + Poco::File temp(filename); + temp.createFile(); + + MultipleFileProperty p("Filename"); + p.setValue(filename); + std::vector<std::vector<std::string>> fileNames = p(); + TS_ASSERT_EQUALS(fileNames.size(), 1); + } + void test_folderWithWhitespace() { std::string dirPath = "_MultipleFilePropertyTestDummyFolder WithWhiteSpace"; std::string filename = "TSC99999.raw"; std::string oldDataSearchDirectories = ""; - // Create a dummy folder with whitespace to use. + // Create a dummy folder with whitespace to use. Poco::File dir(dirPath); dir.createDirectories(); diff --git a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/Load.h b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/Load.h index 2d4bcd4fc3cd0b650e5945e8f335e0ea2e8789d4..96fcbd881f10f43e55065f21cfa455666094b1ce 100644 --- a/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/Load.h +++ b/Code/Mantid/Framework/DataHandling/inc/MantidDataHandling/Load.h @@ -93,16 +93,12 @@ namespace Mantid const API::IDataFileChecker_sptr loader) const; /// Load a file to into a hidden workspace. - API::Workspace_sptr loadFileToHiddenWs(const std::string & fileName, const std::string & wsName); + API::Workspace_sptr loadFileToWs(const std::string & fileName, const std::string & wsName); /// Plus two workspaces together, "in place". API::Workspace_sptr plusWs(API::Workspace_sptr ws1, API::Workspace_sptr ws2); - /// Delete a workspace with the given name. - void deleteWs(const std::string & wsName); - /// Rename a workspace with the given name. - void renameWs(const std::string & oldName, const std::string & newName); - /// Unhide the given workspace, if it is hidden. - void unhideWs(const std::string & wsName); - + /// Manually group workspaces. + API::WorkspaceGroup_sptr groupWsList(std::vector<API::Workspace_sptr> wsList); + private: /// The base properties std::set<std::string> m_baseProps; diff --git a/Code/Mantid/Framework/DataHandling/src/Load.cpp b/Code/Mantid/Framework/DataHandling/src/Load.cpp index 32765530e500b3b37c752baafaf108dfa55f854d..16c6705bb05c358ac581bf0da5c32918555f52cc 100644 --- a/Code/Mantid/Framework/DataHandling/src/Load.cpp +++ b/Code/Mantid/Framework/DataHandling/src/Load.cpp @@ -456,50 +456,32 @@ namespace Mantid void Load::loadMultipleFiles() { MultipleFileProperty * multiFileProp = dynamic_cast<MultipleFileProperty*>(getPointerToProperty("Filename")); - const std::vector<std::vector<std::string> > values = getProperty("Filename"); + const std::vector<std::vector<std::string> > allFilenames = getProperty("Filename"); std::string outputWsName = getProperty("OutputWorkspace"); - // Generate ws names for the files to be loaded. - const std::vector<std::vector<unsigned int> > runs = multiFileProp->getRuns(); std::vector<std::string> wsNames; - wsNames.resize(values.size()); + wsNames.resize(allFilenames.size()); - // If we successfully parsed run numbers (in the cases where we were given a string of "inst[runs].raw") then - // we can use them to generate the ws names. - if( ! runs.empty() ) - { - std::transform( - runs.begin(), runs.end(), - wsNames.begin(), - generateWsNameFromRuns); - } - // Else if no runs were returned then the string we were given was of the form "inst1.raw, inst2.raw, ...". - // It would not make sense to generate ws names from just the run numbers in this case, since we could have - // two files with the same run number with but with different instruments. - else - { - std::transform( - values.begin(), values.end(), + std::transform( + allFilenames.begin(), allFilenames.end(), wsNames.begin(), generateWsNameFromFileNames); - } - std::vector<std::string> loadedWsNames; + std::vector<API::Workspace_sptr> loadedWsList; - std::vector<std::vector<std::string> >::const_iterator values_it = values.begin(); - std::vector<std::string >::const_iterator wsNames_it = wsNames.begin(); + std::vector<std::vector<std::string> >::const_iterator filenames = allFilenames.begin(); + std::vector<std::string >::const_iterator wsName = wsNames.begin(); + assert( allFilenames.size() == wsNames.size() ); - // Cycle through the fileNames and wsNames. - for(; values_it != values.end(); ++values_it, ++wsNames_it) + // Cycle through the filenames and wsNames. + for(; filenames != allFilenames.end(); ++filenames, ++wsName) { - std::vector<std::string> fileNames = *values_it; - std::string wsName = *wsNames_it; - // If there is only one filename, then just load it to the given wsName. - if(fileNames.size() == 1) + if(filenames->size() == 1) { - loadFileToHiddenWs(fileNames.at(0), wsName); - loadedWsNames.push_back("__" + wsName); + Workspace_sptr loadedWs = loadFileToWs(filenames->at(0), *wsName); + loadedWs->setName(*wsName); + loadedWsList.push_back(loadedWs); } // Else there is more than one filename. Load them all, sum them, and rename the // result to the given wsName. @@ -507,67 +489,58 @@ namespace Mantid { // Load all files and place the resulting workspaces in a vector. std::vector<Workspace_sptr> loadedWs; - std::vector<std::string>::const_iterator vIt = fileNames.begin(); + std::vector<std::string>::const_iterator filename = filenames->begin(); - for(; vIt != fileNames.end(); ++vIt) + for(; filename != filenames->end(); ++filename) { - Workspace_sptr ws = loadFileToHiddenWs(*vIt, (*vIt) + "_temp"); + Workspace_sptr ws = loadFileToWs(*filename, (*filename) + "_temp"); loadedWs.push_back(ws); } // Add all workspaces together, sticking the result in sum. Workspace_sptr sum; - for( - size_t i = 1; // Start at second workspace in list. - i < loadedWs.size(); - i++) + for( size_t i = 1; i < loadedWs.size(); i++ ) { Workspace_sptr firstWsToAdd; // If there have been no workspaces added yet, then the first workspace to add // is the first workspace in the list. if(sum == Workspace_sptr()) - firstWsToAdd = loadedWs.at(i-1); + firstWsToAdd = loadedWs[i-1]; // Else the first workspace to add is "sum" itself. else firstWsToAdd = sum; - Workspace_sptr secondWsToAdd = loadedWs.at(i); + Workspace_sptr secondWsToAdd = loadedWs[i]; sum = plusWs(firstWsToAdd, secondWsToAdd); } - // Delete all of the temporarily loaded workspaces except the first one, so that we are left only - // with sum at this point. - for(size_t i = 1; i < fileNames.size(); i++) - { - deleteWs("__" + fileNames.at(i) + "_temp"); - } + sum->setName(*wsName); - // Rename the sum and add to the list of loaded workspace names. - renameWs(sum->name(), "__" + wsName); - loadedWsNames.push_back("__" + wsName); + // Add the sum to the list of loaded workspace names. + loadedWsList.push_back(sum); } } // If we only have one loaded ws, set it as the output. - if(loadedWsNames.size() == 1) + if(loadedWsList.size() == 1) { - renameWs(loadedWsNames.at(0), outputWsName); - setProperty("OutputWorkspace", AnalysisDataService::Instance().retrieve(outputWsName.c_str())); + setProperty("OutputWorkspace", loadedWsList[0]); } // Else we have multiple loaded workspaces - group them and set the group as output. else { - Mantid::API::IAlgorithm_sptr groupingAlg = this->createSubAlgorithm("GroupWorkspaces",0, 0, true, 1); - groupingAlg->setAlwaysStoreInADS(true); - - groupingAlg->setProperty("InputWorkspaces",loadedWsNames); - groupingAlg->setProperty("OutputWorkspace",outputWsName.c_str()); - groupingAlg->execute(); - - auto outws = AnalysisDataService::Instance().retrieve(outputWsName.c_str()); - unhideWs(outputWsName); - - setProperty("OutputWorkspace", outws); + API::WorkspaceGroup_sptr group = groupWsList(loadedWsList); + setProperty("OutputWorkspace", group); + std::vector<std::string> childWsNames = group->getNames(); + size_t count = 1; + for(auto childWsName = childWsNames.begin(); childWsName != childWsNames.end(); ++childWsName, ++count ) + { + Workspace_sptr childWs = group->getItem(*childWsName); + std::string outWsPropName = "OutputWorkspace_" + boost::lexical_cast<std::string>(count); + + declareProperty(new WorkspaceProperty<Workspace>(outWsPropName, *childWsName, Direction::Output)); + setProperty(outWsPropName, childWs); + } } } @@ -729,25 +702,20 @@ namespace Mantid * * @returns a pointer to the loaded workspace */ - API::Workspace_sptr Load::loadFileToHiddenWs( + API::Workspace_sptr Load::loadFileToWs( const std::string & fileName, const std::string & wsName) { Mantid::API::IAlgorithm_sptr loadAlg = createSubAlgorithm("Load", 1); - // Here, as a workaround for groupworkspaces who's members have names but no - // accompanying entries in the ADS, we set the sub algo to setAlwaysStoreInADS. - //loadAlg->setChild(false); - loadAlg->setAlwaysStoreInADS(true); - // Get the list properties for the concrete loader load algorithm const std::vector<Kernel::Property*> & props = getProperties(); // Loop through and set the properties on the sub algorithm - std::vector<Kernel::Property*>::const_iterator itr; - for (itr = props.begin(); itr != props.end(); ++itr) + std::vector<Kernel::Property*>::const_iterator prop = props.begin(); + for (; prop != props.end(); ++prop) { - const std::string propName = (*itr)->name(); + const std::string propName = (*prop)->name(); if( this->existsProperty(propName) ) { @@ -757,7 +725,7 @@ namespace Mantid } else if(propName == "OutputWorkspace") { - loadAlg->setPropertyValue("OutputWorkspace","__" + wsName); + loadAlg->setPropertyValue("OutputWorkspace", wsName); } else { @@ -768,7 +736,7 @@ namespace Mantid loadAlg->executeAsSubAlg(); - return AnalysisDataService::Instance().retrieve("__" + wsName); + return loadAlg->getProperty("OutputWorkspace"); } /** @@ -784,97 +752,48 @@ namespace Mantid Workspace_sptr ws2) { Mantid::API::IAlgorithm_sptr plusAlg = createSubAlgorithm("Plus", 1); - plusAlg->setPropertyValue("LHSWorkspace", ws1->name()); - plusAlg->setPropertyValue("RHSWorkspace", ws2->name()); - plusAlg->setPropertyValue("OutputWorkspace", ws1->name()); + plusAlg->setProperty("LHSWorkspace", ws1); + plusAlg->setProperty("RHSWorkspace", ws2); + plusAlg->setProperty("OutputWorkspace", ws1); plusAlg->executeAsSubAlg(); return ws1; } /** - * Renames a workspace. - * - * @param oldName :: the old workspace name. - * @param newName :: the new workspace name. - */ - void Load::renameWs( - const std::string & oldName, - const std::string & newName) - { - if(oldName == newName) - return; - - Mantid::API::IAlgorithm_sptr renameAlg = createSubAlgorithm("RenameWorkspace", 1); - renameAlg->setChild(true); // Must be keep child=true to prevent locking errors - renameAlg->setAlwaysStoreInADS(true); - renameAlg->setPropertyValue("InputWorkspace", oldName); - renameAlg->setPropertyValue("OutputWorkspace", newName); - renameAlg->executeAsSubAlg(); - } - - /** - * Deletes a given workspace. If the given workspace is a group workspace, - * then this function calls itself recursively for each workspace in the group. - * - * @param wsName :: the name of the workspace to delete. - */ - void Load::deleteWs(const std::string & wsName) - { - Workspace_sptr ws = AnalysisDataService::Instance().retrieve(wsName); - if(WorkspaceGroup_sptr wsGrpSptr = - boost::dynamic_pointer_cast<WorkspaceGroup>(ws)) - { - std::vector<std::string> childWsNames = wsGrpSptr->getNames(); - std::vector<std::string>::iterator vIt = childWsNames.begin(); - - for(; vIt != childWsNames.end(); ++vIt) - { - // Call this function recursively, to delete each child workspace. - deleteWs(*vIt); - } - } - else - { - Mantid::API::IAlgorithm_sptr deleteAlg = createSubAlgorithm("DeleteWorkspace", 1); - - deleteAlg->setPropertyValue("Workspace", wsName); - deleteAlg->execute(); - } - } - - /** - * Unhides a given workspace (by removing the "__" prefix from its name if present). - * If the given workspace is a group workspace, then this function calls itself - * recursively for each workspace in the group. + * Groups together a vector of workspaces. This is done "manually", since the + * workspaces being passed will be outside of the ADS and so the GroupWorkspaces + * alg is not an option here. * - * @param wsName :: the name of the workspace to unhide. + * @param wsList :: the list of workspaces to group */ - void Load::unhideWs(const std::string & wsName) + API::WorkspaceGroup_sptr Load::groupWsList(std::vector<API::Workspace_sptr> wsList) { - std::set<std::string> adsContents1 = AnalysisDataService::Instance().getObjectNames(); + WorkspaceGroup_sptr group = WorkspaceGroup_sptr(new WorkspaceGroup); - Workspace_sptr ws = AnalysisDataService::Instance().retrieve(wsName); - if(WorkspaceGroup_sptr wsGrpSptr = - boost::dynamic_pointer_cast<WorkspaceGroup>(ws)) + for( auto ws = wsList.begin(); ws != wsList.end(); ++ws ) { - std::vector<std::string> childWsNames = wsGrpSptr->getNames(); - std::vector<std::string>::iterator vIt = childWsNames.begin(); - - for(; vIt != childWsNames.end(); ++vIt) + WorkspaceGroup_sptr isGroup = boost::dynamic_pointer_cast<WorkspaceGroup>(*ws); + // If the ws to add is already a group, then add its children individually. + if(isGroup) { - // Call this function recursively, to unhide each child workspace. - unhideWs(*vIt); + std::vector<std::string> childrenNames = isGroup->getNames(); + size_t count = 1; + for( auto childName = childrenNames.begin(); childName != childrenNames.end(); ++childName, ++count) + { + Workspace_sptr childWs = isGroup->getItem(*childName); + isGroup->remove(*childName); + childWs->setName(isGroup->getName() + "_" + boost::lexical_cast<std::string>(count)); + group->addWorkspace(childWs); + } } - } - else - { - if(boost::starts_with(wsName, "__")) + else { - std::string newName = wsName.substr(2, (wsName.size() - 2)); - renameWs(wsName, newName); + group->addWorkspace(*ws); } } + + return group; } } // namespace DataHandling diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/MultiFileNameParser.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/MultiFileNameParser.h index 9d39fb7d2b3979e20949b12386c0f2621e148179..f289b1b8b34a8cdb03bd52552d0a595a51f45e17 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/MultiFileNameParser.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/MultiFileNameParser.h @@ -55,6 +55,15 @@ namespace Kernel extern const std::string ANY, LIST; } + /** + * Comparator for set that holds instrument names in Parser. + */ + class MANTID_KERNEL_DLL ReverseCaselessCompare + { + public: + bool operator()(const std::string & a, const std::string & b); + }; + /** This class takes a string representing multiple files and parses it into a vector of vectors of file names. Filenames to be added are placed in the @@ -63,10 +72,13 @@ namespace Kernel The string to parse should be of the format [dir][inst][under][runs][ext], where: [dir] (Optional) = The OS-specific file directory, e.g. "c:\data\" - [inst] (Required) = The instrument name, e.g. "IRS" or "PG3" + [inst] (Optional) = The instrument name, e.g. "IRS" or "PG3". If none provided then use default. [under] (Optional) = An underscore. [runs] (Required) = The run numbers, e.g. "0102, 0110-0115, 0120, 0130:0140:2" [ext] (Optional) = The file extension, e.g. ".raw" + + NOTE: This parser does not parse strings of the form: + [dir][inst][under][runs][ext],[dir][inst][under][runs][ext] */ class MANTID_KERNEL_DLL Parser { @@ -110,6 +122,8 @@ namespace Kernel std::string m_dirString, m_instString, m_underscoreString, m_runString, m_extString; /// The instrument-specific run zero padding value. int m_zeroPadding; + /// All the valid instrument names. + std::set<std::string, ReverseCaselessCompare> m_validInstNames; }; /** diff --git a/Code/Mantid/Framework/Kernel/inc/MantidKernel/PropertyWithValue.h b/Code/Mantid/Framework/Kernel/inc/MantidKernel/PropertyWithValue.h index 91b98437dfda6f5862fbd381a8c87fd8adfab703..4d54f2e899c1d2adb7b0109de7724be5ac97a697 100644 --- a/Code/Mantid/Framework/Kernel/inc/MantidKernel/PropertyWithValue.h +++ b/Code/Mantid/Framework/Kernel/inc/MantidKernel/PropertyWithValue.h @@ -72,7 +72,7 @@ std::string toString(const boost::shared_ptr<T>& value) /// Specialisation for a property of type std::vector. template <typename T> -std::string toString(const std::vector<T>& value) +std::string toString(const std::vector<T>& value, const std::string & delimiter = ",") { std::stringstream result; std::size_t vsize = value.size(); @@ -80,14 +80,15 @@ std::string toString(const std::vector<T>& value) { result << value[i]; if (i + 1 != vsize) - result << ","; + result << delimiter; } return result.str(); } /// Specialisation for a property of type std::vector<std::vector>. template <typename T> -std::string toString(const std::vector<std::vector<T> >& value) +std::string toString(const std::vector<std::vector<T> >& value, const std::string & outerDelimiter = ",", + const std::string & innerDelimiter = "+") { std::stringstream result; std::size_t vsize = value.size(); @@ -98,11 +99,11 @@ std::string toString(const std::vector<std::vector<T> >& value) { result << value[i][j]; if (j + 1 != innervsize) - result << "+"; + result << innerDelimiter; } if (i + 1 != vsize) - result << ","; + result << outerDelimiter; } return result.str(); } @@ -174,18 +175,18 @@ void toValue(const std::string& strvalue, std::vector<T>& value) } template <typename T> -void toValue(const std::string& strvalue, std::vector<std::vector<T> >& value) +void toValue(const std::string& strvalue, std::vector<std::vector<T> >& value, const std::string & outerDelimiter = ",", + const std::string & innerDelimiter = "+") { - // Split up comma-separated properties typedef Poco::StringTokenizer tokenizer; - tokenizer tokens(strvalue, ",", tokenizer::TOK_IGNORE_EMPTY | tokenizer::TOK_TRIM); + tokenizer tokens(strvalue, outerDelimiter, tokenizer::TOK_IGNORE_EMPTY | tokenizer::TOK_TRIM); value.clear(); value.reserve(tokens.count()); for (tokenizer::Iterator oIt = tokens.begin(); oIt != tokens.end(); ++oIt) { - tokenizer values(*oIt, "+", tokenizer::TOK_IGNORE_EMPTY | tokenizer::TOK_TRIM); + tokenizer values(*oIt, innerDelimiter, tokenizer::TOK_IGNORE_EMPTY | tokenizer::TOK_TRIM); std::vector<T> vect; for (tokenizer::Iterator iIt = values.begin(); iIt != values.end(); ++iIt) diff --git a/Code/Mantid/Framework/Kernel/src/MultiFileNameParser.cpp b/Code/Mantid/Framework/Kernel/src/MultiFileNameParser.cpp index 8d56ccf05a4d5c7e9314e7b9dab6615412cdb95a..f559f776317aded6f9ec13ae0f26b66d55eea652 100644 --- a/Code/Mantid/Framework/Kernel/src/MultiFileNameParser.cpp +++ b/Code/Mantid/Framework/Kernel/src/MultiFileNameParser.cpp @@ -4,6 +4,7 @@ #include "MantidKernel/MultiFileNameParser.h" #include "MantidKernel/ConfigService.h" +#include "MantidKernel/FacilityInfo.h" #include "MantidKernel/InstrumentInfo.h" #include "MantidKernel/Exception.h" @@ -67,7 +68,7 @@ namespace Kernel std::vector<std::vector<unsigned int> > generateRange(unsigned int from, unsigned int to, unsigned int stepSize, bool addRuns); void validateToken(const std::string & token); bool matchesFully(const std::string & stringToMatch, const std::string & regexString); - std::string getMatchingString(const std::string & regexString, const std::string & toParse); + std::string getMatchingString(const std::string & regexString, const std::string & toParse, bool caseless = false); std::string pad(std::string run, unsigned int padLength); std::set< std::pair<unsigned int, unsigned int> > & mergeAdjacentRanges( @@ -160,6 +161,26 @@ namespace Kernel // Return the suggested ws name. return parser.instString() + parser.underscoreString() + toString(runs); } + + ///////////////////////////////////////////////////////////////////////////// + // Comparator class. + ///////////////////////////////////////////////////////////////////////////// + + /** + * Comparator for the set that holds instrument names in Parser. This is reversed + * since we want to come across the longer instrument names first. It is caseless + * so we don't get "inst" coming before "INSTRUMENT" - though this is probably overkill. + */ + bool ReverseCaselessCompare::operator()(const std::string & a, const std::string & b) + { + std::string lowerA(a); + std::string lowerB(b); + + std::transform(lowerA.begin(), lowerA.end(), lowerA.begin(), tolower); + std::transform(lowerB.begin(), lowerB.end(), lowerB.begin(), tolower); + + return lowerA > lowerB; + } ///////////////////////////////////////////////////////////////////////////// // Public member functions of Parser class. @@ -168,8 +189,25 @@ namespace Kernel /// Constructor. Parser::Parser() : m_runs(), m_fileNames(), m_multiFileName(), m_dirString(), m_instString(), - m_underscoreString(), m_runString(), m_extString(), m_zeroPadding() - {} + m_underscoreString(), m_runString(), m_extString(), m_zeroPadding(), m_validInstNames() + { + ConfigServiceImpl & config = ConfigService::Instance(); + + std::vector<std::string> allFacilityNames; + boost::split( + allFacilityNames, + config.getString("supported.facilities"), + boost::is_any_of(";")); + + for( auto facilityName = allFacilityNames.begin(); facilityName != allFacilityNames.end(); ++facilityName ) + { + const FacilityInfo & facility = config.getFacility(*facilityName); + const std::vector<InstrumentInfo> instruments = facility.instruments(); + + for( auto instrument = instruments.begin(); instrument != instruments.end(); ++instrument ) + m_validInstNames.insert(instrument->shortName()); + } + } /// Destructor. Parser::~Parser() @@ -261,42 +299,48 @@ namespace Kernel std::string base = m_multiFileName.substr( m_dirString.size(), m_multiFileName.size() - (m_dirString.size() + m_extString.size())); - // Get the instrument name using a regex. - m_instString = getMatchingString("^" + Regexs::INST, base); + if( base.empty() ) + throw std::runtime_error("There does not appear to be any runs present."); - if(m_instString.empty()) + // If there is only a list of runs, then we need to use the default instrument. + if( matchesFully(base, Regexs::LIST) ) { - // Use default instrument name if one is not found. + m_runString = base; m_instString = ConfigService::Instance().getString("default.instrument"); - // The run string is now what's left. Throw if nothing found since runs are required. - m_runString = base; - if(m_runString.empty()) - throw std::runtime_error("There does not appear to be any runs present."); + // Do the files of the default instrument have an underscore? + InstrumentInfo instInfo = ConfigService::Instance().getInstrument(m_instString); + m_underscoreString = instInfo.delimiter(); } else { + // At this point, if we have a valid and parsable run string, then what remains must be + // one of the available instrument names followed by a possible underscore and a list of runs. + for( auto instName = m_validInstNames.begin(); instName != m_validInstNames.end(); ++instName ) + { + if(matchesFully(base, *instName + ".*")) + { + m_instString = getMatchingString("^" + *instName, base, true); // Caseless. + break; + } + } + + if( m_instString.empty() ) + throw std::runtime_error("There does not appear to be a valid instrument name present."); + // Check for an underscore after the instrument name. - size_t underscore = base.find_first_of("_"); + size_t underscore = base.find_last_of("_"); if(underscore == m_instString.size()) m_underscoreString = "_"; - // We can now deduce the run string. Throw if not found since this runs are required. + // We can now deduce the run string. Throw if not found since runs are required. m_runString = base.substr(m_underscoreString.size() + m_instString.size()); - if(m_instString.empty()) + if(m_runString.empty()) throw std::runtime_error("There does not appear to be any runs present."); } - - // Get zero padding of instrument. If throws then instrument does not exist. - try - { - InstrumentInfo instInfo = ConfigService::Instance().getInstrument(m_instString); - m_zeroPadding = instInfo.zeroPadding(); - } - catch (const Exception::NotFoundError &) - { - throw std::runtime_error("There does not appear to be a valid instrument name present."); - } + + InstrumentInfo instInfo = ConfigService::Instance().getInstrument(m_instString); + m_zeroPadding = instInfo.zeroPadding(); } ///////////////////////////////////////////////////////////////////////////// @@ -640,12 +684,22 @@ namespace Kernel * * @returns the part (if any) of the given string that matches the given regex */ - std::string getMatchingString(const std::string & regexString, const std::string & toParse) + std::string getMatchingString(const std::string & regexString, const std::string & toParse, bool caseless) { + boost::regex regex; + if( caseless ) + { + regex = boost::regex(regexString, boost::regex::icase); + } + else + { + regex = boost::regex(regexString); + } + boost::sregex_iterator it( - toParse.begin(), toParse.end(), - boost::regex(regexString) - ); + toParse.begin(), toParse.end(), + regex + ); if(it == boost::sregex_iterator()) return ""; diff --git a/Code/Mantid/Framework/Kernel/test/MultiFileNameParserTest.h b/Code/Mantid/Framework/Kernel/test/MultiFileNameParserTest.h index eafaf3a479b0ff3aa93c461a9567b180a72ca7fd..273d72f6fcc29bbbc23025acb53f6ca0ba7689b4 100644 --- a/Code/Mantid/Framework/Kernel/test/MultiFileNameParserTest.h +++ b/Code/Mantid/Framework/Kernel/test/MultiFileNameParserTest.h @@ -185,31 +185,32 @@ public: void test_errorThrownWhenPassedUnexpectedChar() { + std::string message = "Non-numeric or otherwise unaccetable character(s) detected."; TS_ASSERT_THROWS_EQUALS(parseMultiRunString("#"), const std::runtime_error & re, std::string(re.what()), - "Non-numeric or otherwise unaccetable character(s) detected."); + message); TS_ASSERT_THROWS_EQUALS(parseMultiRunString("a"), const std::runtime_error & re, std::string(re.what()), - "Non-numeric or otherwise unaccetable character(s) detected."); + message); TS_ASSERT_THROWS_EQUALS(parseMultiRunString("Z"), const std::runtime_error & re, std::string(re.what()), - "Non-numeric or otherwise unaccetable character(s) detected."); + message); TS_ASSERT_THROWS_EQUALS(parseMultiRunString("("), const std::runtime_error & re, std::string(re.what()), - "Non-numeric or otherwise unaccetable character(s) detected."); + message); TS_ASSERT_THROWS_EQUALS(parseMultiRunString(">"), const std::runtime_error & re, std::string(re.what()), - "Non-numeric or otherwise unaccetable character(s) detected."); + message); TS_ASSERT_THROWS_EQUALS(parseMultiRunString("1012-n1059:5"), const std::runtime_error & re, std::string(re.what()), - "Non-numeric or otherwise unaccetable character(s) detected."); + message); } void test_errorThrownOnEmptyToken() diff --git a/Code/Mantid/Framework/Properties/Mantid.properties.template b/Code/Mantid/Framework/Properties/Mantid.properties.template index 9c15e54f743db5fe9bdb8658fd3595ec1ccc6bb5..ff4a30776180e3e62cbca1045a60eed00f237431 100644 --- a/Code/Mantid/Framework/Properties/Mantid.properties.template +++ b/Code/Mantid/Framework/Properties/Mantid.properties.template @@ -180,3 +180,8 @@ filefinder.casesensitive=Off # Control which geometry the Instrument View window displays, valid values # are "Default", "Neutronic" or "Physical". instrument.view.geometry=Default + +# Disabling multifile loading will allow users to load files with "," or "+" +# in their path, but at the cost of no longer being able to load multiple +# files with a single call to Load. +loading.multifile=On diff --git a/Code/Mantid/MantidQt/CustomDialogs/inc/MantidQtCustomDialogs/LoadDialog.h b/Code/Mantid/MantidQt/CustomDialogs/inc/MantidQtCustomDialogs/LoadDialog.h index ba26fe763484a5e5ed76f6a38815c4b4904c7cae..e4daa4398c76639d7ee055cfd3db18fdfc844f40 100644 --- a/Code/Mantid/MantidQt/CustomDialogs/inc/MantidQtCustomDialogs/LoadDialog.h +++ b/Code/Mantid/MantidQt/CustomDialogs/inc/MantidQtCustomDialogs/LoadDialog.h @@ -99,7 +99,7 @@ namespace MantidQt /// Form Ui::LoadDialog m_form; /// The current file - QString m_currentFile; + QString m_currentFiles; /// The initial height int m_initialHeight; }; diff --git a/Code/Mantid/MantidQt/CustomDialogs/src/LoadDialog.cpp b/Code/Mantid/MantidQt/CustomDialogs/src/LoadDialog.cpp index 2f7676c0be8b2b71b4d90f13203a43a37b35f34f..dde6c7e8be05bf723dd704e3527c1a6a13357646 100644 --- a/Code/Mantid/MantidQt/CustomDialogs/src/LoadDialog.cpp +++ b/Code/Mantid/MantidQt/CustomDialogs/src/LoadDialog.cpp @@ -32,7 +32,7 @@ namespace MantidQt /// Default constructor LoadDialog:: LoadDialog(QWidget *parent) - : API::AlgorithmDialog(parent), m_form(), m_currentFile(), m_initialHeight(0) + : API::AlgorithmDialog(parent), m_form(), m_currentFiles(), m_initialHeight(0) { } @@ -96,7 +96,7 @@ namespace MantidQt std::string errMess = getAlgorithm()->getPointerToProperty("Filename")->isValid(); if ( !errMess.empty() ) { - m_currentFile = ""; + m_currentFiles = ""; createDynamicWidgets(); return; } @@ -135,7 +135,7 @@ namespace MantidQt AlgorithmDialog::saveInput(); //Ensure the filename is store as the full file API::AlgorithmInputHistory::Instance().storeNewValue("Load", - QPair<QString, QString>("Filename", m_currentFile)); + QPair<QString, QString>("Filename", m_currentFiles)); } /** @@ -199,13 +199,13 @@ namespace MantidQt if( !m_form.fileWidget->isValid() ) return; // First step is the get the specific loader that is responsible IAlgorithm *loadAlg = getAlgorithm(); - const QString filename = m_form.fileWidget->getFirstFilename(); - if( filename == m_currentFile ) return; - m_currentFile = filename; + const QString filenames = m_form.fileWidget->getText(); + if( filenames == m_currentFiles ) return; + m_currentFiles = filenames; removeOldInputWidgets(m_form.propertyLayout); // The new file might be invalid try { - loadAlg->setPropertyValue("Filename", filename.toStdString()); + loadAlg->setPropertyValue("Filename", filenames.toStdString()); } catch(std::exception & exc) { diff --git a/Code/Mantid/MantidQt/MantidWidgets/src/MWRunFiles.cpp b/Code/Mantid/MantidQt/MantidWidgets/src/MWRunFiles.cpp index f410c27b14f2fbe382fa761caf27b663aabb3caf..8002aaba68f4777ab479b6641ac970c6353b7f5e 100644 --- a/Code/Mantid/MantidQt/MantidWidgets/src/MWRunFiles.cpp +++ b/Code/Mantid/MantidQt/MantidWidgets/src/MWRunFiles.cpp @@ -134,8 +134,8 @@ void FindFilesThread::run() } /** -* Create a list of file extensions from the given algorithm property. -*/ + * Create a list of files from the given algorithm property. + */ void FindFilesThread::getFilesFromAlgorithm() { Mantid::API::IAlgorithm_sptr algorithm = Mantid::API::AlgorithmManager::Instance().createUnmanaged(m_algorithm.toStdString());