Remove old help formatting code
[alexxy/gromacs.git] / src / gromacs / options / filenameoption.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
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.
8  *
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.
13  *
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.
18  *
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.
23  *
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.
31  *
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.
34  */
35 /*! \internal \file
36  * \brief
37  * Implements classes in filenameoption.h and filenameoptionstorage.h.
38  *
39  * \author Teemu Murtola <teemu.murtola@gmail.com>
40  * \ingroup module_options
41  */
42 #include "filenameoption.h"
43 #include "filenameoptionstorage.h"
44
45 #include <string>
46 #include <vector>
47
48 #include "gromacs/fileio/filenm.h"
49
50 #include "gromacs/utility/file.h"
51 #include "gromacs/utility/stringutil.h"
52
53 namespace gmx
54 {
55
56 namespace
57 {
58
59 class FileTypeRegistry;
60
61 //! \addtogroup module_options
62 //! \{
63
64 //! Shorthand for a list of file extensions.
65 typedef std::vector<const char *> ExtensionList;
66
67 /********************************************************************
68  * FileTypeHandler
69  */
70
71 /*! \internal \brief
72  * Handles a single file type known to FileNameOptionStorage.
73  */
74 class FileTypeHandler
75 {
76     public:
77         //! Returns the list of extensions for this file type.
78         const ExtensionList &extensions() const { return extensions_; }
79
80         //! Returns whether \p filename has a valid extension for this type.
81         bool hasKnownExtension(const std::string &filename) const;
82         //! Adds a default extension for this type to \p filename.
83         std::string addExtension(const std::string &filename) const;
84         /*! \brief
85          * Adds an extension to \p filename if it results in an existing file.
86          *
87          * Tries to add each extension for this file type to \p filename and
88          * checks whether this results in an existing file.
89          * The first match is returned.
90          * Returns an empty string if no existing file is found.
91          */
92         std::string findFileWithExtension(const std::string &filename) const;
93
94     private:
95         //! Possible extensions for this file type.
96         ExtensionList extensions_;
97
98         /*! \brief
99          * Needed for initialization; all initialization is handled by
100          * FileTypeRegistry.
101          */
102         friend class FileTypeRegistry;
103 };
104
105 bool
106 FileTypeHandler::hasKnownExtension(const std::string &filename) const
107 {
108     for (size_t i = 0; i < extensions_.size(); ++i)
109     {
110         if (endsWith(filename, extensions_[i]))
111         {
112             return true;
113         }
114     }
115     return false;
116 }
117
118 std::string
119 FileTypeHandler::addExtension(const std::string &filename) const
120 {
121     if (extensions_.empty())
122     {
123         return filename;
124     }
125     return filename + extensions_[0];
126 }
127
128 std::string
129 FileTypeHandler::findFileWithExtension(const std::string &filename) const
130 {
131     for (size_t i = 0; i < extensions_.size(); ++i)
132     {
133         std::string testFilename(filename + extensions_[i]);
134         if (File::exists(testFilename))
135         {
136             return testFilename;
137         }
138     }
139     return std::string();
140 }
141
142 /********************************************************************
143  * FileTypeRegistry
144  */
145
146 /*! \internal \brief
147  * Singleton for managing static file type info for FileNameOptionStorage.
148  */
149 class FileTypeRegistry
150 {
151     public:
152         //! Returns a singleton instance of this class.
153         static const FileTypeRegistry &instance();
154         //! Returns a handler for a single file type.
155         const FileTypeHandler &
156         handlerForType(OptionFileType type, int legacyType) const;
157
158     private:
159         //! Initializes the file type registry.
160         FileTypeRegistry();
161
162         //! Registers a file type that corresponds to a ftp in filenm.h.
163         void registerType(int type, int ftp);
164         //! Registers a file type with a single extension.
165         void registerType(int type, const char *extension);
166
167         std::vector<FileTypeHandler> filetypes_;
168 };
169
170 // static
171 const FileTypeRegistry &
172 FileTypeRegistry::instance()
173 {
174     static FileTypeRegistry singleton;
175     return singleton;
176 }
177
178 const FileTypeHandler &
179 FileTypeRegistry::handlerForType(OptionFileType type, int legacyType) const
180 {
181     int index = type;
182     if (type == eftUnknown && legacyType >= 0)
183     {
184         index = eftOptionFileType_NR + legacyType;
185     }
186     GMX_RELEASE_ASSERT(index >= 0 && static_cast<size_t>(index) < filetypes_.size(),
187                        "Invalid file type");
188     return filetypes_[index];
189 }
190
191 FileTypeRegistry::FileTypeRegistry()
192 {
193     filetypes_.resize(eftOptionFileType_NR + efNR);
194     registerType(eftTopology,    efTPS);
195     registerType(eftTrajectory,  efTRX);
196     registerType(eftPDB,         efPDB);
197     registerType(eftIndex,       efNDX);
198     registerType(eftPlot,        efXVG);
199     registerType(eftGenericData, efDAT);
200     for (int i = 0; i < efNR; ++i)
201     {
202         registerType(eftOptionFileType_NR + i, i);
203     }
204 }
205
206 void FileTypeRegistry::registerType(int type, int ftp)
207 {
208     GMX_RELEASE_ASSERT(type >= 0 && static_cast<size_t>(type) < filetypes_.size(),
209                        "Invalid file type");
210     const int genericTypeCount = ftp2generic_count(ftp);
211     if (genericTypeCount > 0)
212     {
213         const int *const genericTypes = ftp2generic_list(ftp);
214         filetypes_[type].extensions_.clear();
215         filetypes_[type].extensions_.reserve(genericTypeCount);
216         for (int i = 0; i < genericTypeCount; ++i)
217         {
218             filetypes_[type].extensions_.push_back(ftp2ext_with_dot(genericTypes[i]));
219         }
220     }
221     else
222     {
223         registerType(type, ftp2ext_with_dot(ftp));
224     }
225 }
226
227 void FileTypeRegistry::registerType(int type, const char *extension)
228 {
229     GMX_RELEASE_ASSERT(type >= 0 && static_cast<size_t>(type) < filetypes_.size(),
230                        "Invalid file type");
231     filetypes_[type].extensions_.assign(1, extension);
232 }
233
234 /*! \brief
235  * Helper method to complete a file name provided to a file name option.
236  *
237  * \param[in] value      Value provided to the file name option.
238  * \param[in] filetype   File type for the option.
239  * \param[in] legacyType If \p filetype is eftUnknown, this gives the type as
240  *     an enum value from filenm.h.
241  * \param[in] bCompleteToExisting
242  *     Whether to check existing files when completing the extension.
243  * \returns   \p value with possible extension added.
244  */
245 std::string completeFileName(const std::string &value, OptionFileType filetype,
246                              int legacyType, bool bCompleteToExisting)
247 {
248     if (bCompleteToExisting && File::exists(value))
249     {
250         // TODO: This may not work as expected if the value is passed to a
251         // function that uses fn2ftp() to determine the file type and the input
252         // file has an unrecognized extension.
253         return value;
254     }
255     const FileTypeRegistry &registry    = FileTypeRegistry::instance();
256     const FileTypeHandler  &typeHandler = registry.handlerForType(filetype, legacyType);
257     if (typeHandler.hasKnownExtension(value))
258     {
259         return value;
260     }
261     if (bCompleteToExisting)
262     {
263         std::string newValue = typeHandler.findFileWithExtension(value);
264         if (!newValue.empty())
265         {
266             return newValue;
267         }
268     }
269     return typeHandler.addExtension(value);
270 }
271
272 //! \}
273
274 }   // namespace
275
276 /********************************************************************
277  * FileNameOptionStorage
278  */
279
280 FileNameOptionStorage::FileNameOptionStorage(const FileNameOption &settings)
281     : MyBase(settings), info_(this), filetype_(settings.filetype_),
282       legacyType_(settings.legacyType_),
283       bRead_(settings.bRead_), bWrite_(settings.bWrite_),
284       bLibrary_(settings.bLibrary_)
285 {
286     if (settings.defaultBasename_ != NULL)
287     {
288         std::string defaultValue =
289             completeFileName(settings.defaultBasename_, filetype_,
290                              legacyType_, false);
291         setDefaultValueIfSet(defaultValue);
292         if (isRequired())
293         {
294             setDefaultValue(defaultValue);
295         }
296     }
297 }
298
299 std::string FileNameOptionStorage::typeString() const
300 {
301     const FileTypeRegistry       &registry    = FileTypeRegistry::instance();
302     const FileTypeHandler        &typeHandler = registry.handlerForType(filetype_, legacyType_);
303     const ExtensionList          &extensions  = typeHandler.extensions();
304     std::string                   result;
305     ExtensionList::const_iterator i;
306     int                           count = 0;
307     for (i = extensions.begin(); count < 2 && i != extensions.end(); ++i, ++count)
308     {
309         if (i != extensions.begin())
310         {
311             result.append("/");
312         }
313         result.append(*i);
314     }
315     if (i != extensions.end())
316     {
317         result.append("/...");
318     }
319     if (result.empty())
320     {
321         if (legacyType_ == efRND)
322         {
323             result = "dir";
324         }
325         else
326         {
327             result = "file";
328         }
329     }
330     return result;
331 }
332
333 std::string FileNameOptionStorage::formatExtraDescription() const
334 {
335     const FileTypeRegistry       &registry    = FileTypeRegistry::instance();
336     const FileTypeHandler        &typeHandler = registry.handlerForType(filetype_, legacyType_);
337     const ExtensionList          &extensions  = typeHandler.extensions();
338     std::string                   result;
339     if (extensions.size() > 2)
340     {
341         result.append(":");
342         ExtensionList::const_iterator i;
343         for (i = extensions.begin(); i != extensions.end(); ++i)
344         {
345             result.append(" ");
346             result.append((*i) + 1);
347         }
348     }
349     return result;
350 }
351
352 std::string FileNameOptionStorage::formatSingleValue(const std::string &value) const
353 {
354     return value;
355 }
356
357 void FileNameOptionStorage::convertValue(const std::string &value)
358 {
359     bool bInput = isInputFile() || isInputOutputFile();
360     addValue(completeFileName(value, filetype_, legacyType_, bInput));
361 }
362
363 /********************************************************************
364  * FileNameOptionInfo
365  */
366
367 FileNameOptionInfo::FileNameOptionInfo(FileNameOptionStorage *option)
368     : OptionInfo(option)
369 {
370 }
371
372 const FileNameOptionStorage &FileNameOptionInfo::option() const
373 {
374     return static_cast<const FileNameOptionStorage &>(OptionInfo::option());
375 }
376
377 bool FileNameOptionInfo::isInputFile() const
378 {
379     return option().isInputFile();
380 }
381
382 bool FileNameOptionInfo::isOutputFile() const
383 {
384     return option().isOutputFile();
385 }
386
387 bool FileNameOptionInfo::isInputOutputFile() const
388 {
389     return option().isInputOutputFile();
390 }
391
392 bool FileNameOptionInfo::isLibraryFile() const
393 {
394     return option().isLibraryFile();
395 }
396
397 /********************************************************************
398  * FileNameOption
399  */
400
401 AbstractOptionStoragePointer FileNameOption::createStorage() const
402 {
403     return AbstractOptionStoragePointer(new FileNameOptionStorage(*this));
404 }
405
406 } // namespace gmx