/*
* This file is part of the GROMACS molecular simulation package.
*
- * Copyright (c) 2012, by the GROMACS development team, led by
- * David van der Spoel, Berk Hess, Erik Lindahl, and including many
- * others, as listed in the AUTHORS file in the top-level source
- * directory and at http://www.gromacs.org.
+ * Copyright (c) 2012,2013,2014, 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.
*
* GROMACS is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public License
* \author Teemu Murtola <teemu.murtola@gmail.com>
* \ingroup module_options
*/
+#include "gmxpre.h"
+
#include "filenameoption.h"
#include "filenameoptionstorage.h"
+#include <cstring>
+
#include <string>
#include <vector>
-#include "gromacs/utility/file.h"
+#include "gromacs/fileio/filenm.h"
+#include "gromacs/options/filenameoptionmanager.h"
+#include "gromacs/options/optionmanagercontainer.h"
+#include "gromacs/utility/arrayref.h"
+#include "gromacs/utility/exceptions.h"
+#include "gromacs/utility/gmxassert.h"
#include "gromacs/utility/stringutil.h"
namespace gmx
namespace
{
-class FileTypeRegistry;
+//! \addtogroup module_options
+//! \{
+
+/*! \brief
+ * Mapping from OptionFileType to a file type in filenm.h.
+ */
+struct FileTypeMapping
+{
+ //! OptionFileType value to map.
+ OptionFileType optionType;
+ //! Corresponding file type from filenm.h.
+ int fileType;
+};
+
+//! Mappings from OptionFileType to file types in filenm.h.
+const FileTypeMapping c_fileTypeMapping[] =
+{
+ { eftTopology, efTPS },
+ { eftTrajectory, efTRX },
+ { eftPDB, efPDB },
+ { eftIndex, efNDX },
+ { eftPlot, efXVG },
+ { eftGenericData, efDAT }
+};
/********************************************************************
* FileTypeHandler
*/
-/*! \internal \brief
+/*! \internal
+ * \brief
* Handles a single file type known to FileNameOptionStorage.
*
- * \ingroup module_options
+ * Methods in this class do not throw, except for a possible std::bad_alloc
+ * when constructing std::string return values.
*/
class FileTypeHandler
{
public:
- //! Returns whether \p filename has a valid extension for this type.
- bool hasKnownExtension(const std::string &filename) const;
- //! Adds a default extension for this type to \p filename.
- std::string addExtension(const std::string &filename) const;
/*! \brief
- * Adds an extension to \p filename if it results in an existing file.
+ * Returns a handler for a single file type.
*
- * Tries to add each extension for this file type to \p filename and
- * checks whether this results in an existing file.
- * The first match is returned.
- * Returns an empty string if no existing file is found.
+ * \param[in] fileType File type (from filenm.h) to use.
*/
- std::string findFileWithExtension(const std::string &filename) const;
+ explicit FileTypeHandler(int fileType);
- private:
- //! Possible extensions for this file type.
- std::vector<const char *> extensions_;
+ //! Returns the number of acceptable extensions for this file type.
+ int extensionCount() const;
+ //! Returns the extension with the given index.
+ const char *extension(int i) const;
+
+ //! Returns whether \p fileType (from filenm.h) is accepted for this type.
+ bool isValidType(int fileType) const;
+ private:
/*! \brief
- * Needed for initialization; all initialization is handled by
- * FileTypeRegistry.
+ * File type (from filenm.h) represented by this handler.
+ *
+ * -1 represents an unknown file type.
*/
- friend class FileTypeRegistry;
+ int fileType_;
+ //! Number of different extensions this type supports.
+ int extensionCount_;
+ /*! \brief
+ * List of simple file types that are included in this type.
+ *
+ * If `fileType_` represents a generic type in filenm.h, i.e., a type
+ * that accepts multiple different types of files, then this is an
+ * array of `extensionCount_` elements, each element specifying one
+ * non-generic file type that this option accepts.
+ * `NULL` for single-extension types.
+ */
+ const int *genericTypes_;
};
-bool
-FileTypeHandler::hasKnownExtension(const std::string &filename) const
+FileTypeHandler::FileTypeHandler(int fileType)
+ : fileType_(fileType), extensionCount_(0), genericTypes_(NULL)
{
- for (size_t i = 0; i < extensions_.size(); ++i)
+ if (fileType_ >= 0)
{
- if (endsWith(filename, extensions_[i]))
+ const int genericTypeCount = ftp2generic_count(fileType_);
+ if (genericTypeCount > 0)
{
- return true;
+ extensionCount_ = genericTypeCount;
+ genericTypes_ = ftp2generic_list(fileType_);
+ }
+ else if (ftp2ext_with_dot(fileType_)[0] != '\0')
+ {
+ extensionCount_ = 1;
}
}
- return false;
}
-std::string
-FileTypeHandler::addExtension(const std::string &filename) const
+int FileTypeHandler::extensionCount() const
{
- if (extensions_.empty())
+ return extensionCount_;
+}
+
+const char *FileTypeHandler::extension(int i) const
+{
+ GMX_ASSERT(i >= 0 && i < extensionCount_, "Invalid extension index");
+ if (genericTypes_ != NULL)
{
- return filename;
+ return ftp2ext_with_dot(genericTypes_[i]);
}
- return filename + extensions_[0];
+ return ftp2ext_with_dot(fileType_);
}
-std::string
-FileTypeHandler::findFileWithExtension(const std::string &filename) const
+bool
+FileTypeHandler::isValidType(int fileType) const
{
- for (size_t i = 0; i < extensions_.size(); ++i)
+ if (genericTypes_ != NULL)
{
- std::string testFilename(filename + extensions_[i]);
- if (File::exists(testFilename))
+ for (int i = 0; i < extensionCount(); ++i)
{
- return testFilename;
+ if (fileType == genericTypes_[i])
+ {
+ return true;
+ }
}
+ return false;
+ }
+ else
+ {
+ return fileType == fileType_;
}
- return std::string();
}
-/********************************************************************
- * FileTypeRegistry
- */
+//! \}
-/*! \internal \brief
- * Singleton for managing static file type info for FileNameOptionStorage.
- *
- * \ingroup module_options
- */
-class FileTypeRegistry
-{
- public:
- //! Returns a singleton instance of this class.
- static const FileTypeRegistry &instance();
- //! Returns a handler for a single file type.
- const FileTypeHandler &handlerForType(OptionFileType type) const;
-
- private:
- //! Initializes the file type registry.
- FileTypeRegistry();
-
- //! Registers a file type with a single extension.
- void registerType(OptionFileType type, const char *extension);
- //! Registers a file type with multiple extensions.
- template <size_t count>
- void registerType(OptionFileType type,
- const char *const (&extensions)[count]);
+} // namespace
- std::vector<FileTypeHandler> filetypes_;
-};
+/********************************************************************
+ * FileNameOptionStorage
+ */
-// static
-const FileTypeRegistry &
-FileTypeRegistry::instance()
+FileNameOptionStorage::FileNameOptionStorage(const FileNameOption &settings,
+ FileNameOptionManager *manager)
+ : MyBase(settings), info_(this), manager_(manager), fileType_(-1),
+ defaultExtension_(""), bRead_(settings.bRead_), bWrite_(settings.bWrite_),
+ bLibrary_(settings.bLibrary_)
{
- static FileTypeRegistry singleton;
- return singleton;
+ GMX_RELEASE_ASSERT(!hasFlag(efOption_MultipleTimes),
+ "allowMultiple() is not supported for file name options");
+ if (settings.optionType_ == eftUnknown && settings.legacyType_ >= 0)
+ {
+ fileType_ = settings.legacyType_;
+ }
+ else
+ {
+ ConstArrayRef<FileTypeMapping> map(c_fileTypeMapping);
+ ConstArrayRef<FileTypeMapping>::const_iterator i;
+ for (i = map.begin(); i != map.end(); ++i)
+ {
+ if (i->optionType == settings.optionType_)
+ {
+ fileType_ = i->fileType;
+ break;
+ }
+ }
+ }
+ FileTypeHandler typeHandler(fileType_);
+ if (settings.defaultType_ >= 0 && settings.defaultType_ < efNR)
+ {
+ // This also assures that the default type is not a generic type.
+ GMX_RELEASE_ASSERT(typeHandler.isValidType(settings.defaultType_),
+ "Default type for a file option is not an accepted "
+ "type for the option");
+ FileTypeHandler defaultHandler(settings.defaultType_);
+ defaultExtension_ = defaultHandler.extension(0);
+ }
+ else if (typeHandler.extensionCount() > 0)
+ {
+ defaultExtension_ = typeHandler.extension(0);
+ }
+ if (settings.defaultBasename_ != NULL)
+ {
+ std::string defaultValue(settings.defaultBasename_);
+ int type = fn2ftp(settings.defaultBasename_);
+ GMX_RELEASE_ASSERT(type == efNR || type == settings.defaultType_,
+ "Default basename has an extension that does not "
+ "match the default type");
+ if (type == efNR)
+ {
+ defaultValue.append(defaultExtension());
+ }
+ setDefaultValueIfSet(defaultValue);
+ if (isRequired() || settings.bLegacyOptionalBehavior_)
+ {
+ setDefaultValue(defaultValue);
+ }
+ }
}
-const FileTypeHandler &
-FileTypeRegistry::handlerForType(OptionFileType type) const
+std::string FileNameOptionStorage::typeString() const
{
- GMX_RELEASE_ASSERT(type >= 0 && static_cast<size_t>(type) < filetypes_.size(),
- "Invalid file type");
- return filetypes_[type];
-}
-
-FileTypeRegistry::FileTypeRegistry()
-{
- filetypes_.resize(eftOptionFileType_NR);
- const char *const topExtensions[] = {
- ".tpr", ".tpb", ".tpa", ".gro", ".g96", ".pdb", ".brk", ".ent"
- };
- const char *const trajExtensions[] = {
- ".xtc", ".trr", ".trj", ".cpt", ".gro", ".g96", ".g87", ".pdb"
- };
- registerType(eftTopology, topExtensions);
- registerType(eftTrajectory, trajExtensions);
- registerType(eftPDB, ".pdb");
- registerType(eftIndex, ".ndx");
- registerType(eftPlot, ".xvg");
- registerType(eftGenericData, ".dat");
+ FileTypeHandler typeHandler(fileType_);
+ std::string result;
+ int count;
+ for (count = 0; count < 2 && count < typeHandler.extensionCount(); ++count)
+ {
+ if (count > 0)
+ {
+ result.append("/");
+ }
+ result.append(typeHandler.extension(count));
+ }
+ if (count < typeHandler.extensionCount())
+ {
+ result.append("/...");
+ }
+ if (result.empty())
+ {
+ if (isDirectoryOption())
+ {
+ result = "dir";
+ }
+ else
+ {
+ result = "file";
+ }
+ }
+ return result;
}
-void FileTypeRegistry::registerType(OptionFileType type,
- const char *extension)
+std::string FileNameOptionStorage::formatExtraDescription() const
{
- GMX_RELEASE_ASSERT(type >= 0 && static_cast<size_t>(type) < filetypes_.size(),
- "Invalid file type");
- filetypes_[type].extensions_.assign(1, extension);
+ FileTypeHandler typeHandler(fileType_);
+ std::string result;
+ if (typeHandler.extensionCount() > 2)
+ {
+ result.append(":");
+ for (int i = 0; i < typeHandler.extensionCount(); ++i)
+ {
+ result.append(" ");
+ // Skip the dot.
+ result.append(typeHandler.extension(i) + 1);
+ }
+ }
+ return result;
}
-template <size_t count>
-void FileTypeRegistry::registerType(OptionFileType type,
- const char *const (&extensions)[count])
+std::string FileNameOptionStorage::formatSingleValue(const std::string &value) const
{
- GMX_RELEASE_ASSERT(type >= 0 && static_cast<size_t>(type) < filetypes_.size(),
- "Invalid file type");
- filetypes_[type].extensions_.assign(extensions, extensions + count);
+ return value;
}
-/*! \brief
- * Helper method to complete a file name provided to a file name option.
- *
- * \param[in] value Value provided to the file name option.
- * \param[in] filetype File type for the option.
- * \param[in] bCompleteToExisting
- * Whether to check existing files when completing the extension.
- * \returns \p value with possible extension added.
- */
-std::string completeFileName(const std::string &value, OptionFileType filetype,
- bool bCompleteToExisting)
+void FileNameOptionStorage::convertValue(const std::string &value)
{
- if (bCompleteToExisting && File::exists(value))
+ if (manager_ != NULL)
{
- // TODO: This may not work as expected if the value is passed to a
- // function that uses fn2ftp() to determine the file type and the input
- // file has an unrecognized extension.
- return value;
+ std::string processedValue = manager_->completeFileName(value, info_);
+ if (!processedValue.empty())
+ {
+ // If the manager returns a value, use it without further checks,
+ // except for sanity checking.
+ if (!isDirectoryOption())
+ {
+ const int fileType = fn2ftp(processedValue.c_str());
+ if (fileType == efNR)
+ {
+ // If the manager returned an invalid file name, assume
+ // that it knows what it is doing. But assert that it
+ // only does that for the only case that it is currently
+ // required for: VMD plugins.
+ GMX_ASSERT(isInputFile() && isTrajectoryOption(),
+ "Manager returned an invalid file name");
+ }
+ else
+ {
+ GMX_ASSERT(isValidType(fileType),
+ "Manager returned an invalid file name");
+ }
+ }
+ addValue(processedValue);
+ return;
+ }
+ }
+ // Currently, directory options are simple, and don't need any
+ // special processing.
+ // TODO: Consider splitting them into a separate DirectoryOption.
+ if (isDirectoryOption())
+ {
+ addValue(value);
+ return;
}
- const FileTypeRegistry ®istry = FileTypeRegistry::instance();
- const FileTypeHandler &typeHandler = registry.handlerForType(filetype);
- if (typeHandler.hasKnownExtension(value))
+ const int fileType = fn2ftp(value.c_str());
+ if (fileType == efNR)
{
- return value;
+ std::string message
+ = formatString("File '%s' cannot be used by GROMACS because it "
+ "does not have a recognizable extension.\n"
+ "The following extensions are possible for this option:\n %s",
+ value.c_str(), joinStrings(extensions(), ", ").c_str());
+ GMX_THROW(InvalidInputError(message));
}
- if (bCompleteToExisting)
+ else if (!isValidType(fileType))
{
- std::string newValue = typeHandler.findFileWithExtension(value);
- if (!newValue.empty())
+ std::string message
+ = formatString("File name '%s' cannot be used for this option.\n"
+ "Only the following extensions are possible:\n %s",
+ value.c_str(), joinStrings(extensions(), ", ").c_str());
+ GMX_THROW(InvalidInputError(message));
+ }
+ addValue(value);
+}
+
+void FileNameOptionStorage::processAll()
+{
+ if (manager_ != NULL && hasFlag(efOption_HasDefaultValue))
+ {
+ ValueList &valueList = values();
+ GMX_RELEASE_ASSERT(valueList.size() == 1,
+ "There should be only one default value");
+ if (!valueList[0].empty())
{
- return newValue;
+ const std::string &oldValue = valueList[0];
+ GMX_ASSERT(endsWith(oldValue, defaultExtension()),
+ "Default value does not have the expected extension");
+ const std::string prefix
+ = stripSuffixIfPresent(oldValue, defaultExtension());
+ const std::string newValue
+ = manager_->completeDefaultFileName(prefix, info_);
+ if (!newValue.empty() && newValue != oldValue)
+ {
+ GMX_ASSERT(isValidType(fn2ftp(newValue.c_str())),
+ "Manager returned an invalid default value");
+ valueList[0] = newValue;
+ refreshValues();
+ }
}
}
- return typeHandler.addExtension(value);
}
-} // namespace
+bool FileNameOptionStorage::isDirectoryOption() const
+{
+ return fileType_ == efRND;
+}
-/********************************************************************
- * FileNameOptionStorage
- */
+bool FileNameOptionStorage::isTrajectoryOption() const
+{
+ return fileType_ == efTRX;
+}
-FileNameOptionStorage::FileNameOptionStorage(const FileNameOption &settings)
- : MyBase(settings), info_(this), filetype_(settings.filetype_),
- bRead_(settings.bRead_), bWrite_(settings.bWrite_),
- bLibrary_(settings.bLibrary_)
+const char *FileNameOptionStorage::defaultExtension() const
{
- if (settings.defaultBasename_ != NULL)
+ return defaultExtension_;
+}
+
+std::vector<const char *> FileNameOptionStorage::extensions() const
+{
+ FileTypeHandler typeHandler(fileType_);
+ std::vector<const char *> result;
+ result.reserve(typeHandler.extensionCount());
+ for (int i = 0; i < typeHandler.extensionCount(); ++i)
{
- if (isRequired())
- {
- setDefaultValue(completeFileName(settings.defaultBasename_,
- filetype_, false));
- }
- else
- {
- setDefaultValueIfSet(completeFileName(settings.defaultBasename_,
- filetype_, false));
- }
+ result.push_back(typeHandler.extension(i));
}
+ return result;
}
-std::string FileNameOptionStorage::formatSingleValue(const std::string &value) const
+bool FileNameOptionStorage::isValidType(int fileType) const
{
- return value;
+ FileTypeHandler typeHandler(fileType_);
+ return typeHandler.isValidType(fileType);
}
-void FileNameOptionStorage::convertValue(const std::string &value)
+ConstArrayRef<int> FileNameOptionStorage::fileTypes() const
{
- bool bInput = isInputFile() || isInputOutputFile();
- addValue(completeFileName(value, filetype_, bInput));
+ if (fileType_ < 0)
+ {
+ return ConstArrayRef<int>();
+ }
+ const int genericTypeCount = ftp2generic_count(fileType_);
+ if (genericTypeCount > 0)
+ {
+ return constArrayRefFromArray<int>(ftp2generic_list(fileType_), genericTypeCount);
+ }
+ return constArrayRefFromArray<int>(&fileType_, 1);
}
/********************************************************************
return option().isLibraryFile();
}
+bool FileNameOptionInfo::isDirectoryOption() const
+{
+ return option().isDirectoryOption();
+}
+
+bool FileNameOptionInfo::isTrajectoryOption() const
+{
+ return option().isTrajectoryOption();
+}
+
+const char *FileNameOptionInfo::defaultExtension() const
+{
+ return option().defaultExtension();
+}
+
+FileNameOptionInfo::ExtensionList FileNameOptionInfo::extensions() const
+{
+ return option().extensions();
+}
+
+bool FileNameOptionInfo::isValidType(int fileType) const
+{
+ return option().isValidType(fileType);
+}
+
+ConstArrayRef<int> FileNameOptionInfo::fileTypes() const
+{
+ return option().fileTypes();
+}
+
/********************************************************************
* FileNameOption
*/
-AbstractOptionStoragePointer FileNameOption::createStorage() const
+AbstractOptionStorage *
+FileNameOption::createStorage(const OptionManagerContainer &managers) const
{
- return AbstractOptionStoragePointer(new FileNameOptionStorage(*this));
+ return new FileNameOptionStorage(*this, managers.get<FileNameOptionManager>());
}
} // namespace gmx