//---------------------------------------------------------------------- // Includes //---------------------------------------------------------------------- #include "MantidDataHandling/Load.h" #include "MantidAPI/FileProperty.h" #include "MantidAPI/IEventWorkspace.h" #include "MantidAPI/IWorkspaceProperty.h" #include "MantidKernel/ArrayProperty.h" #include "MantidAPI/LoadAlgorithmFactory.h" #include "MantidAPI/AlgorithmManager.h" #include "MantidKernel/FacilityInfo.h" #include <cctype> #include <algorithm> namespace Mantid { namespace DataHandling { // Register the algorithm into the algorithm factory DECLARE_ALGORITHM(Load); /// Sets documentation strings for this algorithm void Load::initDocs() { this->setWikiSummary("Attempts to load a given file by finding an appropriate Load algorithm. "); this->setOptionalMessage("Attempts to load a given file by finding an appropriate Load algorithm."); } using namespace Kernel; using namespace API; //-------------------------------------------------------------------------- // Public methods //-------------------------------------------------------------------------- /// Default constructor Load::Load() : IDataFileChecker(), m_baseProps() { } /** Override setPropertyValue to catch if filename is being set, as this may mean * a change of concrete loader. If it's any other property, just forward the call. * @param name The name of the property * @param value The value of the property as a string */ void Load::setPropertyValue(const std::string &name, const std::string &value) { // Call base class method in all cases. // For a filename property is deals with resolving the full path. IDataFileChecker::setPropertyValue(name, value); std::string NAME(name); std::transform(name.begin(),name.end(),NAME.begin(),toupper); if( NAME == "FILENAME" ) { // Get back full path before passing to getFileLoader method. IDataFileChecker_sptr loader = getFileLoader(getPropertyValue(name)); if( loader ) declareLoaderProperties(loader); } } //-------------------------------------------------------------------------- // Private methods //-------------------------------------------------------------------------- /** * Quick file always returns false here * @param filePath :: File path * @param nread :: Number of bytes read * @param header :: A buffer containing the nread bytes */ bool Load::quickFileCheck(const std::string& filePath,size_t nread,const file_header& header) { (void)filePath; (void)nread; (void)header; return false; } /** * File check by looking at the structure of the data file * @param filePath :: The full file path * @returns -1 */ int Load::fileCheck(const std::string& filePath) { (void)filePath; return -1; } /** * Get a shared pointer to the load algorithm with highest preference for loading * @param filePath :: path of the file * @returns A shared pointer to the unmanaged algorithm */ API::IDataFileChecker_sptr Load::getFileLoader(const std::string& filePath) { /* Open the file and read in the first bufferSize bytes - these will * be used to determine the type of the file */ FILE* fp = fopen(filePath.c_str(), "rb"); if (fp == NULL) { throw Kernel::Exception::FileError("Unable to open the file:", filePath); } file_header header; size_t nread = fread(&header,sizeof(unsigned char), g_hdr_bytes, fp); // Ensure the character string is null terminated. header.full_hdr[g_hdr_bytes] = '\0'; if (fclose(fp) != 0) { throw std::runtime_error("Error while closing file \"" + filePath + "\""); } // Iterate through all loaders and attempt to find the best qualified for the job. // Each algorithm has a quick and long file check. The long version returns an integer // giving its certainty about be able to load the file. The highest wins. std::vector<std::string> loaderNames = API::LoadAlgorithmFactory::Instance().getKeys(); int highestPref(0); API::IDataFileChecker_sptr winningLoader; std::vector<std::string>::const_iterator cend = loaderNames.end(); for( std::vector<std::string>::const_iterator citr = loaderNames.begin(); citr != cend; ++citr ) { IDataFileChecker_sptr loader = API::LoadAlgorithmFactory::Instance().create(*citr); if( loader->quickFileCheck(filePath, nread, header) ) { int pref = loader->fileCheck(filePath); // Can't just pick the first as there might be one later in the list with a higher // preference if( pref > highestPref ) { highestPref = pref; winningLoader = loader; } } } if( !winningLoader ) { // Clear what may have been here previously setPropertyValue("LoaderName", ""); throw std::runtime_error("Cannot find an algorithm that is able to load \"" + filePath + "\".\n" "Check that the file is a supported type."); } setPropertyValue("LoaderName", winningLoader->name()); winningLoader->initialize(); setUpLoader(winningLoader); return winningLoader; } /** * Declare any additional properties of the concrete loader here * @param loader A pointer to the concrete loader */ void Load::declareLoaderProperties(const IDataFileChecker_sptr loader) { // If we have switch loaders then the concrete loader will have different properties // so take care of ensuring Load has the correct ones const std::vector<Property*> existingProps = this->getProperties(); for( size_t i = 0; i < existingProps.size(); ++i ) { const std::string name = existingProps[i]->name(); // Wipe all properties except the Load native ones if( m_baseProps.find(name) == m_baseProps.end() ) { this->removeProperty(name); } } const std::vector<Property*> &loaderProps = loader->getProperties(); const std::string filePropName(loader->filePropertyName()); size_t numProps(loaderProps.size()); for (size_t i = 0; i < numProps; ++i) { Property* loadProp = loaderProps[i]; if( loadProp->name() == filePropName ) continue; try { declareProperty(loadProp->clone(), loadProp->documentation()); } catch(Exception::ExistsError&) { // Already exists as a static property continue; } } } /// Initialisation method. void Load::init() { // Take extensions first from Facility object const FacilityInfo & defaultFacility = Mantid::Kernel::ConfigService::Instance().getFacility(); std::vector<std::string> exts = defaultFacility.extensions(); // Add in some other known extensions exts.push_back(".xml"); exts.push_back(".dat"); exts.push_back(".txt"); exts.push_back(".csv"); exts.push_back(".spe"); exts.push_back(".grp"); declareProperty(new FileProperty("Filename", "", FileProperty::Load, exts), "The name of the file to read, including its full or relative\n" "path. (N.B. case sensitive if running on Linux)."); declareProperty(new WorkspaceProperty<Workspace>("OutputWorkspace", "",Direction::Output), "The name of the workspace that will be created, filled with the\n" "read-in data and stored in the Analysis Data Service."); declareProperty("LoaderName", std::string(""), "A string containing the name of the concrete loader used", Direction::Output); // Save for later what the base Load properties are const std::vector<Property*> & props = this->getProperties(); for( size_t i = 0; i < this->propertyCount(); ++i ) { m_baseProps.insert(props[i]->name()); } } /** * Executes the algorithm. */ void Load::exec() { std::string loaderName = getPropertyValue("LoaderName"); IDataFileChecker_sptr loader; if( loaderName.empty() ) { loader = getFileLoader(getPropertyValue("Filename")); loaderName = loader->name(); setPropertyValue("LoaderName",loaderName); } else { loader = createLoader(loaderName,0,1); } g_log.information() << "Using " << loaderName << " version " << loader->version() << ".\n"; ///get the list properties for the concrete loader load algorithm const std::vector<Kernel::Property*> & loader_props = loader->getProperties(); // Loop through and set the properties on the sub algorithm std::vector<Kernel::Property*>::const_iterator itr; for (itr = loader_props.begin(); itr != loader_props.end(); ++itr) { const std::string propName = (*itr)->name(); if( this->existsProperty(propName) ) { loader->setPropertyValue(propName, getPropertyValue(propName)); } else if( propName == loader->filePropertyName() ) { loader->setPropertyValue(propName, getPropertyValue("Filename")); } } // Execute the concrete loader loader->execute(); // Set the workspace. Deals with possible multiple periods setOutputWorkspace(loader); } /** * Create the concrete instance use for the actual loading. * @param name :: The name of the loader to instantiate * @param startProgress :: The percentage progress value of the overall * algorithm where this child algorithm starts * @param endProgress :: The percentage progress value of the overall * algorithm where this child algorithm ends * @param logging :: Set to false to disable logging from the child algorithm */ API::IDataFileChecker_sptr Load::createLoader(const std::string & name, const double startProgress, const double endProgress, const bool logging) const { IDataFileChecker_sptr loader = boost::static_pointer_cast<IDataFileChecker>( API::AlgorithmManager::Instance().createUnmanaged(name)); loader->initialize(); if( !loader ) { throw std::runtime_error("Cannot create loader for \"" + getPropertyValue("Filename") + "\""); } setUpLoader(loader,startProgress,endProgress, logging); return loader; } /** * Set the loader option for use as a sub algorithm. * @param loader :: Concrete loader * @param startProgress :: The start progress fraction * @param endProgress :: The end progress fraction * @param logging:: If true, enable logging */ void Load::setUpLoader(API::IDataFileChecker_sptr loader, const double startProgress, const double endProgress, const bool logging) const { //Set as a child so that we are in control of output storage loader->setChild(true); loader->setLogging(logging); // If output workspaces are nameless, give them a temporary name to satisfy validator const std::vector< Property*> &props = loader->getProperties(); for (unsigned int i = 0; i < props.size(); ++i) { if (props[i]->direction() == Direction::Output && dynamic_cast<IWorkspaceProperty*>(props[i]) ) { if ( props[i]->value().empty() ) props[i]->setValue("LoadChildWorkspace"); } } if (startProgress >= 0. && endProgress > startProgress && endProgress <= 1.) { loader->addObserver(m_progressObserver); setChildStartProgress(startProgress); setChildEndProgress(endProgress); } } /** * Set the output workspace(s) if the load's return workspace has type API::Workspace * @param loader :: Shared pointer to load algorithm */ void Load::setOutputWorkspace(const API::IDataFileChecker_sptr loader) { // Go through each OutputWorkspace property and check whether we need to make a counterpart here const std::vector<Property*> loaderProps = loader->getProperties(); const size_t count = loader->propertyCount(); for( size_t i = 0; i < count; ++i ) { Property *prop = loaderProps[i]; if( dynamic_cast<IWorkspaceProperty*>(prop) && prop->direction() == Direction::Output ) { const std::string & name = prop->name(); if( !this->existsProperty(name) ) { declareProperty(new WorkspaceProperty<Workspace>(name, loader->getPropertyValue(name), Direction::Output)); } Workspace_sptr wkspace = getOutputWorkspace(name, loader); setProperty(name, wkspace); } } } /** * Return an output workspace property dealing with the lack of connection between of * WorkspaceProperty types * @param propName :: The name of the property * @param loader :: The loader algorithm * @returns A pointer to the OutputWorkspace property of the sub algorithm */ API::Workspace_sptr Load::getOutputWorkspace(const std::string & propName, const API::IDataFileChecker_sptr loader) const { // @todo Need to try and find a better way using the getValue methods try { return loader->getProperty(propName); } catch(std::runtime_error&) { } // Try a MatrixWorkspace try { MatrixWorkspace_sptr childWS = loader->getProperty(propName); return childWS; } catch(std::runtime_error&) { } // EventWorkspace try { IEventWorkspace_sptr childWS = loader->getProperty(propName); return childWS; } catch(std::runtime_error&) { } return Workspace_sptr(); } } // namespace DataHandling } // namespace Mantid