2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014, by the GROMACS development team, led by
5 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6 * and including many others, as listed in the AUTHORS file in the
7 * top-level source directory and at http://www.gromacs.org.
9 * GROMACS is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public License
11 * as published by the Free Software Foundation; either version 2.1
12 * of the License, or (at your option) any later version.
14 * GROMACS is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with GROMACS; if not, see
21 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
24 * If you want to redistribute modifications to GROMACS, please
25 * consider that scientific software is very special. Version
26 * control is crucial - bugs must be traceable. We will be happy to
27 * consider code for inclusion in the official distribution, but
28 * derived work must not be called official GROMACS. Details are found
29 * in the README & COPYING files - if they are missing, get the
30 * official version at http://www.gromacs.org.
32 * To help us fund GROMACS development, we humbly ask that you cite
33 * the research papers on the package. Check out http://www.gromacs.org.
37 * Implements classes in filenameoption.h and filenameoptionstorage.h.
39 * \author Teemu Murtola <teemu.murtola@gmail.com>
40 * \ingroup module_options
42 #include "filenameoption.h"
43 #include "filenameoptionstorage.h"
50 #include "gromacs/fileio/filenm.h"
51 #include "gromacs/options/filenameoptionmanager.h"
52 #include "gromacs/options/optionmanagercontainer.h"
53 #include "gromacs/utility/arrayref.h"
54 #include "gromacs/utility/file.h"
55 #include "gromacs/utility/gmxassert.h"
56 #include "gromacs/utility/stringutil.h"
64 //! \addtogroup module_options
68 * Mapping from OptionFileType to a file type in filenm.h.
70 struct FileTypeMapping
72 //! OptionFileType value to map.
73 OptionFileType optionType;
74 //! Corresponding file type from filenm.h.
78 //! Mappings from OptionFileType to file types in filenm.h.
79 const FileTypeMapping c_fileTypeMapping[] =
81 { eftTopology, efTPS },
82 { eftTrajectory, efTRX },
86 { eftGenericData, efDAT }
89 //! Extensions that are recognized as compressed files.
90 const char *const c_compressedExtensions[] =
93 /********************************************************************
99 * Handles a single file type known to FileNameOptionStorage.
101 * Methods in this class do not throw, except for a possible std::bad_alloc
102 * when constructing std::string return values.
104 class FileTypeHandler
108 * Returns a handler for a single file type.
110 * \param[in] fileType File type (from filenm.h) to use.
112 explicit FileTypeHandler(int fileType);
114 //! Returns the number of acceptable extensions for this file type.
115 int extensionCount() const;
116 //! Returns the extension with the given index.
117 const char *extension(int i) const;
119 //! Returns whether \p filename has a valid extension for this type.
120 bool hasKnownExtension(const std::string &filename) const;
121 //! Adds a default extension for this type to \p filename.
122 std::string addExtension(const std::string &filename) const;
124 * Adds an extension to \p filename if it results in an existing file.
126 * Tries to add each extension for this file type to \p filename and
127 * checks whether this results in an existing file.
128 * The first match is returned.
129 * Returns an empty string if no existing file is found.
131 std::string findFileWithExtension(const std::string &filename) const;
135 * File type (from filenm.h) represented by this handler.
137 * -1 represents an unknown file type.
140 //! Number of different extensions this type supports.
143 * List of simple file types that are included in this type.
145 * If `fileType_` represents a generic type in filenm.h, i.e., a type
146 * that accepts multiple different types of files, then this is an
147 * array of `extensionCount_` elements, each element specifying one
148 * non-generic file type that this option accepts.
149 * `NULL` for single-extension types.
151 const int *genericTypes_;
154 FileTypeHandler::FileTypeHandler(int fileType)
155 : fileType_(fileType), extensionCount_(0), genericTypes_(NULL)
159 const int genericTypeCount = ftp2generic_count(fileType_);
160 if (genericTypeCount > 0)
162 extensionCount_ = genericTypeCount;
163 genericTypes_ = ftp2generic_list(fileType_);
165 else if (ftp2ext_with_dot(fileType_)[0] != '\0')
172 int FileTypeHandler::extensionCount() const
174 return extensionCount_;
177 const char *FileTypeHandler::extension(int i) const
179 GMX_ASSERT(i >= 0 && i < extensionCount_, "Invalid extension index");
180 if (genericTypes_ != NULL)
182 return ftp2ext_with_dot(genericTypes_[i]);
184 return ftp2ext_with_dot(fileType_);
188 FileTypeHandler::hasKnownExtension(const std::string &filename) const
190 for (int i = 0; i < extensionCount(); ++i)
192 if (endsWith(filename, extension(i)))
201 FileTypeHandler::addExtension(const std::string &filename) const
203 if (extensionCount() == 0)
207 return filename + extension(0);
211 FileTypeHandler::findFileWithExtension(const std::string &filename) const
213 for (int i = 0; i < extensionCount(); ++i)
215 std::string testFilename(filename + extension(i));
216 if (File::exists(testFilename))
221 return std::string();
225 * Helper method to complete a file name provided to a file name option.
227 * \param[in] value Value provided to the file name option.
228 * \param[in] typeHandler Handler for the file type.
229 * \param[in] bCompleteToExisting
230 * Whether to check existing files when completing the extension.
231 * \returns \p value with possible extension added.
233 std::string completeFileName(const std::string &value,
234 const FileTypeHandler &typeHandler,
235 bool bCompleteToExisting)
237 if (bCompleteToExisting && File::exists(value))
239 // TODO: This may not work as expected if the value is passed to a
240 // function that uses fn2ftp() to determine the file type and the input
241 // file has an unrecognized extension.
242 ConstArrayRef<const char *> compressedExtensions(c_compressedExtensions);
243 ConstArrayRef<const char *>::const_iterator ext;
244 for (ext = compressedExtensions.begin(); ext != compressedExtensions.end(); ++ext)
246 if (endsWith(value, *ext))
248 return value.substr(0, value.length() - std::strlen(*ext));
253 if (typeHandler.hasKnownExtension(value))
257 if (bCompleteToExisting)
259 std::string newValue = typeHandler.findFileWithExtension(value);
260 if (!newValue.empty())
265 return typeHandler.addExtension(value);
272 /********************************************************************
273 * FileNameOptionStorage
276 FileNameOptionStorage::FileNameOptionStorage(const FileNameOption &settings,
277 FileNameOptionManager *manager)
278 : MyBase(settings), info_(this), manager_(manager), fileType_(-1),
279 bRead_(settings.bRead_), bWrite_(settings.bWrite_),
280 bLibrary_(settings.bLibrary_)
282 GMX_RELEASE_ASSERT(!hasFlag(efOption_MultipleTimes),
283 "allowMultiple() is not supported for file name options");
284 if (settings.optionType_ == eftUnknown && settings.legacyType_ >= 0)
286 fileType_ = settings.legacyType_;
290 ConstArrayRef<FileTypeMapping> map(c_fileTypeMapping);
291 ConstArrayRef<FileTypeMapping>::const_iterator i;
292 for (i = map.begin(); i != map.end(); ++i)
294 if (i->optionType == settings.optionType_)
296 fileType_ = i->fileType;
301 if (settings.defaultBasename_ != NULL)
303 std::string defaultValue(settings.defaultBasename_);
304 defaultValue.append(defaultExtension());
305 setDefaultValueIfSet(defaultValue);
306 if (isRequired() || settings.bLegacyOptionalBehavior_)
308 setDefaultValue(defaultValue);
313 std::string FileNameOptionStorage::typeString() const
315 FileTypeHandler typeHandler(fileType_);
318 for (count = 0; count < 2 && count < typeHandler.extensionCount(); ++count)
324 result.append(typeHandler.extension(count));
326 if (count < typeHandler.extensionCount())
328 result.append("/...");
332 if (isDirectoryOption())
344 std::string FileNameOptionStorage::formatExtraDescription() const
346 FileTypeHandler typeHandler(fileType_);
348 if (typeHandler.extensionCount() > 2)
351 for (int i = 0; i < typeHandler.extensionCount(); ++i)
355 result.append(typeHandler.extension(i) + 1);
361 std::string FileNameOptionStorage::formatSingleValue(const std::string &value) const
366 void FileNameOptionStorage::convertValue(const std::string &value)
368 const bool bInput = isInputFile() || isInputOutputFile();
369 FileTypeHandler typeHandler(fileType_);
370 addValue(completeFileName(value, typeHandler, bInput));
373 void FileNameOptionStorage::processAll()
375 FileTypeHandler typeHandler(fileType_);
376 if (hasFlag(efOption_HasDefaultValue) && typeHandler.extensionCount() > 0)
378 const bool bInput = isInputFile() || isInputOutputFile();
379 ValueList &valueList = values();
380 GMX_RELEASE_ASSERT(valueList.size() == 1,
381 "There should be only one default value");
382 const bool bGlobalDefault =
383 (manager_ != NULL && !manager_->defaultFileName().empty());
384 if (!valueList[0].empty() && (typeHandler.extensionCount() > 1 || bGlobalDefault))
386 const std::string &oldValue = valueList[0];
387 GMX_ASSERT(endsWith(oldValue, defaultExtension()),
388 "Default value does not have the expected extension");
389 std::string prefix = stripSuffixIfPresent(oldValue, defaultExtension());
392 prefix = manager_->defaultFileName();
394 std::string newValue = completeFileName(prefix, typeHandler, bInput);
395 if (newValue != oldValue)
397 valueList[0] = newValue;
404 bool FileNameOptionStorage::isDirectoryOption() const
406 return fileType_ == efRND;
409 const char *FileNameOptionStorage::defaultExtension() const
411 FileTypeHandler typeHandler(fileType_);
412 if (typeHandler.extensionCount() == 0)
416 return typeHandler.extension(0);
419 std::vector<const char *> FileNameOptionStorage::extensions() const
421 FileTypeHandler typeHandler(fileType_);
422 std::vector<const char *> result;
423 result.reserve(typeHandler.extensionCount());
424 for (int i = 0; i < typeHandler.extensionCount(); ++i)
426 result.push_back(typeHandler.extension(i));
431 /********************************************************************
435 FileNameOptionInfo::FileNameOptionInfo(FileNameOptionStorage *option)
440 const FileNameOptionStorage &FileNameOptionInfo::option() const
442 return static_cast<const FileNameOptionStorage &>(OptionInfo::option());
445 bool FileNameOptionInfo::isInputFile() const
447 return option().isInputFile();
450 bool FileNameOptionInfo::isOutputFile() const
452 return option().isOutputFile();
455 bool FileNameOptionInfo::isInputOutputFile() const
457 return option().isInputOutputFile();
460 bool FileNameOptionInfo::isLibraryFile() const
462 return option().isLibraryFile();
465 bool FileNameOptionInfo::isDirectoryOption() const
467 return option().isDirectoryOption();
470 const char *FileNameOptionInfo::defaultExtension() const
472 return option().defaultExtension();
475 FileNameOptionInfo::ExtensionList FileNameOptionInfo::extensions() const
477 return option().extensions();
480 /********************************************************************
484 AbstractOptionStoragePointer
485 FileNameOption::createStorage(const OptionManagerContainer &managers) const
487 return AbstractOptionStoragePointer(
488 new FileNameOptionStorage(*this, managers.get<FileNameOptionManager>()));