/*
* This file is part of the GROMACS molecular simulation package.
*
- * Copyright (c) 2014, by the GROMACS development team, led by
+ * Copyright (c) 2014,2015, by the GROMACS development team, led by
* Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
* and including many others, as listed in the AUTHORS file in the
* top-level source directory and at http://www.gromacs.org.
* \author Teemu Murtola <teemu.murtola@gmail.com>
* \ingroup module_options
*/
+#include "gmxpre.h"
+
#include "filenameoptionmanager.h"
+#include <cstring>
+
#include <string>
+#include "gromacs/fileio/filenm.h"
#include "gromacs/options/basicoptions.h"
#include "gromacs/options/filenameoption.h"
#include "gromacs/options/options.h"
-#include "gromacs/options/optionsvisitor.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/fileredirector.h"
+#include "gromacs/utility/path.h"
+#include "gromacs/utility/stringutil.h"
namespace gmx
{
+namespace
+{
+
+//! Extensions that are recognized as compressed files.
+const char *const c_compressedExtensions[] =
+{ ".gz", ".Z" };
+
+/********************************************************************
+ * Helper functions
+ */
+
+/*! \brief
+ * Adds an extension to \p prefix if it results in an existing file.
+ *
+ * Tries to add each extension for this file type to \p prefix and
+ * checks whether this results in an existing file.
+ * The first match is returned.
+ * Returns an empty string if no existing file is found.
+ */
+std::string findExistingExtension(const std::string &prefix,
+ const FileNameOptionInfo &option,
+ const IFileInputRedirector *redirector)
+{
+ ConstArrayRef<int> types = option.fileTypes();
+ ConstArrayRef<int>::const_iterator i;
+ for (i = types.begin(); i != types.end(); ++i)
+ {
+ std::string testFilename(prefix + ftp2ext_with_dot(*i));
+ if (redirector->fileExists(testFilename))
+ {
+ return testFilename;
+ }
+ }
+ return std::string();
+}
+
+} // namespace
+
/********************************************************************
* FileNameOptionManager::Impl
*/
class FileNameOptionManager::Impl
{
public:
+ Impl()
+ : redirector_(&defaultFileInputRedirector()),
+ bInputCheckingDisabled_(false)
+ {
+ }
+
+ //! Redirector for file existence checks.
+ const IFileInputRedirector *redirector_;
//! Global default file name, if set.
- std::string defaultFileName_;
+ std::string defaultFileName_;
+ //! Whether input option processing has been disabled.
+ bool bInputCheckingDisabled_;
};
/********************************************************************
{
}
-void FileNameOptionManager::addDefaultFileNameOption(
- Options *options, const char *name)
+void FileNameOptionManager::setInputRedirector(
+ const IFileInputRedirector *redirector)
{
- options->addOption(
- StringOption(name).store(&impl_->defaultFileName_)
- .description("Set the default filename for all file options"));
+ impl_->redirector_ = redirector;
}
-const std::string &FileNameOptionManager::defaultFileName() const
+void FileNameOptionManager::disableInputOptionChecking(bool bDisable)
{
- return impl_->defaultFileName_;
+ impl_->bInputCheckingDisabled_ = bDisable;
}
-/********************************************************************
- * Global functions
- */
-
-namespace
+void FileNameOptionManager::addDefaultFileNameOption(
+ Options *options, const char *name)
{
+ options->addOption(
+ StringOption(name).store(&impl_->defaultFileName_)
+ .description("Set the default filename for all file options"));
+}
-/*! \internal \brief
- * Visitor that sets the manager for each file name option.
- *
- * \ingroup module_options
- */
-class FileNameOptionManagerSetter : public OptionsModifyingVisitor
+std::string FileNameOptionManager::completeFileName(
+ const std::string &value, const FileNameOptionInfo &option)
{
- public:
- //! Construct a visitor that sets given manager.
- explicit FileNameOptionManagerSetter(FileNameOptionManager *manager)
- : manager_(manager)
+ const bool bAllowMissing = option.allowMissing();
+ const bool bInput
+ = option.isInputFile() || option.isInputOutputFile();
+ // Currently, directory options are simple, and don't need any
+ // special processing.
+ // TODO: Consider splitting them into a separate DirectoryOption.
+ if (option.isDirectoryOption())
+ {
+ if (!impl_->bInputCheckingDisabled_ && bInput && !bAllowMissing
+ && !Directory::exists(value))
{
+ std::string message
+ = formatString("Directory '%s' does not exist or is not accessible.",
+ value.c_str());
+ // TODO: Get actual errno value from the attempt to open the file
+ // to provide better feedback to the user.
+ GMX_THROW(InvalidInputError(message));
}
-
- void visitSubSection(Options *section)
+ return value;
+ }
+ const int fileType = fn2ftp(value.c_str());
+ if (bInput && !impl_->bInputCheckingDisabled_)
+ {
+ if (fileType == efNR && impl_->redirector_->fileExists(value))
{
- OptionsModifyingIterator iterator(section);
- iterator.acceptSubSections(this);
- iterator.acceptOptions(this);
+ ConstArrayRef<const char *> compressedExtensions(c_compressedExtensions);
+ ConstArrayRef<const char *>::const_iterator ext;
+ for (ext = compressedExtensions.begin(); ext != compressedExtensions.end(); ++ext)
+ {
+ if (endsWith(value, *ext))
+ {
+ std::string newValue = value.substr(0, value.length() - std::strlen(*ext));
+ if (option.isValidType(fn2ftp(newValue.c_str())))
+ {
+ return newValue;
+ }
+ else
+ {
+ return std::string();
+ }
+ }
+ }
+ // VMD plugins may be able to read the file.
+ if (option.isInputFile() && option.isTrajectoryOption())
+ {
+ return value;
+ }
}
-
- void visitOption(OptionInfo *option)
+ else if (fileType == efNR)
{
- FileNameOptionInfo *fileOption
- = option->toType<FileNameOptionInfo>();
- if (fileOption != NULL)
+ const std::string processedValue
+ = findExistingExtension(value, option, impl_->redirector_);
+ if (!processedValue.empty())
+ {
+ return processedValue;
+ }
+ if (bAllowMissing)
{
- fileOption->setManager(manager_);
+ return value + option.defaultExtension();
+ }
+ else if (option.isLibraryFile())
+ {
+ // TODO: Treat also library files here.
+ return value + option.defaultExtension();
+ }
+ else
+ {
+ std::string message
+ = formatString("File '%s' does not exist or is not accessible.\n"
+ "The following extensions were tried to complete the file name:\n %s",
+ value.c_str(), joinStrings(option.extensions(), ", ").c_str());
+ GMX_THROW(InvalidInputError(message));
}
}
+ else if (option.isValidType(fileType))
+ {
+ if (option.isLibraryFile())
+ {
+ // TODO: Treat also library files.
+ }
+ else if (!bAllowMissing && !impl_->redirector_->fileExists(value))
+ {
+ std::string message
+ = formatString("File '%s' does not exist or is not accessible.",
+ value.c_str());
+ // TODO: Get actual errno value from the attempt to open the file
+ // to provide better feedback to the user.
+ GMX_THROW(InvalidInputError(message));
+ }
+ return value;
+ }
+ }
+ else // Not an input file
+ {
+ if (fileType == efNR)
+ {
+ return value + option.defaultExtension();
+ }
+ else if (option.isValidType(fileType))
+ {
+ return value;
+ }
+ }
+ return std::string();
+}
- private:
- FileNameOptionManager *manager_;
-};
-
-} // namespace
-
-void setManagerForFileNameOptions(Options *options,
- FileNameOptionManager *manager)
+std::string FileNameOptionManager::completeDefaultFileName(
+ const std::string &prefix, const FileNameOptionInfo &option)
{
- FileNameOptionManagerSetter(manager).visitSubSection(options);
+ if (option.isDirectoryOption() || impl_->bInputCheckingDisabled_)
+ {
+ return std::string();
+ }
+ const bool bInput = option.isInputFile() || option.isInputOutputFile();
+ const std::string realPrefix
+ = !impl_->defaultFileName_.empty() ? impl_->defaultFileName_ : prefix;
+ const bool bAllowMissing = option.allowMissing();
+ if (bInput)
+ {
+ const std::string completedName
+ = findExistingExtension(realPrefix, option, impl_->redirector_);
+ if (!completedName.empty())
+ {
+ return completedName;
+ }
+ if (bAllowMissing)
+ {
+ return realPrefix + option.defaultExtension();
+ }
+ else if (option.isLibraryFile())
+ {
+ // TODO: Treat also library files here.
+ return realPrefix + option.defaultExtension();
+ }
+ else if (option.isSet())
+ {
+ std::string message
+ = formatString("No file name was provided, and the default file "
+ "'%s' does not exist or is not accessible.\n"
+ "The following extensions were tried to complete the file name:\n %s",
+ prefix.c_str(), joinStrings(option.extensions(), ", ").c_str());
+ GMX_THROW(InvalidInputError(message));
+ }
+ else if (option.isRequired())
+ {
+ std::string message
+ = formatString("Required option was not provided, and the default file "
+ "'%s' does not exist or is not accessible.\n"
+ "The following extensions were tried to complete the file name:\n %s",
+ prefix.c_str(), joinStrings(option.extensions(), ", ").c_str());
+ GMX_THROW(InvalidInputError(message));
+ }
+ // We get here with the legacy optional behavior.
+ }
+ return realPrefix + option.defaultExtension();
}
} // namespace gmx