Extension completion for FileNameOption.
[alexxy/gromacs.git] / src / gromacs / options / filenameoption.cpp
1 /*
2  *
3  *                This source code is part of
4  *
5  *                 G   R   O   M   A   C   S
6  *
7  *          GROningen MAchine for Chemical Simulations
8  *
9  * Written by David van der Spoel, Erik Lindahl, Berk Hess, and others.
10  * Copyright (c) 1991-2000, University of Groningen, The Netherlands.
11  * Copyright (c) 2001-2009, The GROMACS development team,
12  * check out http://www.gromacs.org for more information.
13
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * If you want to redistribute modifications, please consider that
20  * scientific software is very special. Version control is crucial -
21  * bugs must be traceable. We will be happy to consider code for
22  * inclusion in the official distribution, but derived work must not
23  * be called official GROMACS. Details are found in the README & COPYING
24  * files - if they are missing, get the official version at www.gromacs.org.
25  *
26  * To help us fund GROMACS development, we humbly ask that you cite
27  * the papers on the package - you can find them in the top README file.
28  *
29  * For more info, check our website at http://www.gromacs.org
30  */
31 /*! \internal \file
32  * \brief
33  * Implements classes in filenameoption.h, filenameoptioninfo.h and
34  * filenameoptionstorage.h.
35  *
36  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
37  * \ingroup module_options
38  */
39 #include "filenameoption.h"
40
41 #include <string>
42 #include <vector>
43
44 #include "gromacs/options/filenameoptioninfo.h"
45 #include "gromacs/utility/file.h"
46 #include "gromacs/utility/stringutil.h"
47
48 #include "filenameoptionstorage.h"
49
50 namespace gmx
51 {
52
53 namespace
54 {
55
56 class FileTypeRegistry;
57
58 /********************************************************************
59  * FileTypeHandler
60  */
61
62 /*! \internal \brief
63  * Handles a single file type known to FileNameOptionStorage.
64  *
65  * \ingroup module_options
66  */
67 class FileTypeHandler
68 {
69     public:
70         //! Returns whether \p filename has a valid extension for this type.
71         bool hasKnownExtension(const std::string &filename) const;
72         //! Adds a default extension for this type to \p filename.
73         std::string addExtension(const std::string &filename) const;
74         /*! \brief
75          * Adds an extension to \p filename if it results in an existing file.
76          *
77          * Tries to add each extension for this file type to \p filename and
78          * checks whether this results in an existing file.
79          * The first match is returned.
80          * Returns an empty string if no existing file is found.
81          */
82         std::string findFileWithExtension(const std::string &filename) const;
83
84     private:
85         //! Possible extensions for this file type.
86         std::vector<const char *> extensions_;
87
88         /*! \brief
89          * Needed for initialization; all initialization is handled by
90          * FileTypeRegistry.
91          */
92         friend class FileTypeRegistry;
93 };
94
95 bool
96 FileTypeHandler::hasKnownExtension(const std::string &filename) const
97 {
98     for (size_t i = 0; i < extensions_.size(); ++i)
99     {
100         if (endsWith(filename, extensions_[i]))
101         {
102             return true;
103         }
104     }
105     return false;
106 }
107
108 std::string
109 FileTypeHandler::addExtension(const std::string &filename) const
110 {
111     if (extensions_.empty())
112     {
113         return filename;
114     }
115     return filename + extensions_[0];
116 }
117
118 std::string
119 FileTypeHandler::findFileWithExtension(const std::string &filename) const
120 {
121     for (size_t i = 0; i < extensions_.size(); ++i)
122     {
123         std::string testFilename(filename + extensions_[i]);
124         if (File::exists(testFilename))
125         {
126             return testFilename;
127         }
128     }
129     return std::string();
130 }
131
132 /********************************************************************
133  * FileTypeRegistry
134  */
135
136 /*! \internal \brief
137  * Singleton for managing static file type info for FileNameOptionStorage.
138  *
139  * \ingroup module_options
140  */
141 class FileTypeRegistry
142 {
143     public:
144         //! Returns a singleton instance of this class.
145         static const FileTypeRegistry &instance();
146         //! Returns a handler for a single file type.
147         const FileTypeHandler &handlerForType(OptionFileType type) const;
148
149     private:
150         //! Initializes the file type registry.
151         FileTypeRegistry();
152
153         //! Registers a file type with a single extension.
154         void registerType(OptionFileType type, const char *extension);
155         //! Registers a file type with multiple extensions.
156         template <size_t count>
157         void registerType(OptionFileType type,
158                           const char *const (&extensions)[count]);
159
160         std::vector<FileTypeHandler> filetypes_;
161 };
162
163 // static
164 const FileTypeRegistry &
165 FileTypeRegistry::instance()
166 {
167     static FileTypeRegistry singleton;
168     return singleton;
169 }
170
171 const FileTypeHandler &
172 FileTypeRegistry::handlerForType(OptionFileType type) const
173 {
174     GMX_RELEASE_ASSERT(type >= 0 && static_cast<size_t>(type) < filetypes_.size(),
175                        "Invalid file type");
176     return filetypes_[type];
177 }
178
179 FileTypeRegistry::FileTypeRegistry()
180 {
181     filetypes_.resize(eftOptionFileType_NR);
182     const char *const topExtensions[] = {
183         ".tpr", ".tpb", ".tpa", ".gro", ".g96", ".pdb", ".brk", ".ent"
184     };
185     const char *const trajExtensions[] = {
186         ".xtc", ".trr", ".trj", ".cpt", ".gro", ".g96", ".g87", ".pdb"
187     };
188     registerType(eftTopology,    topExtensions);
189     registerType(eftTrajectory,  trajExtensions);
190     registerType(eftIndex,       ".ndx");
191     registerType(eftPlot,        ".xvg");
192     registerType(eftGenericData, ".dat");
193 }
194
195 void FileTypeRegistry::registerType(OptionFileType type,
196                                     const char *extension)
197 {
198     GMX_RELEASE_ASSERT(type >= 0 && static_cast<size_t>(type) < filetypes_.size(),
199                        "Invalid file type");
200     filetypes_[type].extensions_.assign(1, extension);
201 }
202
203 template <size_t count>
204 void FileTypeRegistry::registerType(OptionFileType type,
205                                     const char *const (&extensions)[count])
206 {
207     GMX_RELEASE_ASSERT(type >= 0 && static_cast<size_t>(type) < filetypes_.size(),
208                        "Invalid file type");
209     filetypes_[type].extensions_.assign(extensions, extensions + count);
210 }
211
212 /*! \brief
213  * Helper method to complete a file name provided to a file name option.
214  *
215  * \param[in] value     Value provided to the file name option.
216  * \param[in] filetype  File type for the option.
217  * \param[in] bCompleteToExisting
218  *      Whether to check existing files when completing the extension.
219  * \returns   \p value with possible extension added.
220  */
221 std::string completeFileName(const std::string &value, OptionFileType filetype,
222                              bool bCompleteToExisting)
223 {
224     if (bCompleteToExisting && File::exists(value))
225     {
226         // TODO: This may not work as expected if the value is passed to a
227         // function that uses fn2ftp() to determine the file type and the input
228         // file has an unrecognized extension.
229         return value;
230     }
231     const FileTypeRegistry &registry = FileTypeRegistry::instance();
232     const FileTypeHandler &typeHandler = registry.handlerForType(filetype);
233     if (typeHandler.hasKnownExtension(value))
234     {
235         return value;
236     }
237     if (bCompleteToExisting)
238     {
239         std::string newValue = typeHandler.findFileWithExtension(value);
240         if (!newValue.empty())
241         {
242             return newValue;
243         }
244     }
245     return typeHandler.addExtension(value);
246 }
247
248 } // namespace
249
250 /********************************************************************
251  * FileNameOptionStorage
252  */
253
254 FileNameOptionStorage::FileNameOptionStorage(const FileNameOption &settings)
255     : MyBase(settings), info_(this), filetype_(settings.filetype_),
256       bRead_(settings.bRead_), bWrite_(settings.bWrite_),
257       bLibrary_(settings.bLibrary_)
258 {
259     if (settings.defaultValue())
260     {
261         setDefaultValue(completeFileName(*settings.defaultValue(),
262                                          filetype_, false));
263     }
264     if (settings.defaultValueIfSet())
265     {
266         setDefaultValueIfSet(completeFileName(*settings.defaultValueIfSet(),
267                                               filetype_, false));
268     }
269 }
270
271 std::string FileNameOptionStorage::formatSingleValue(const std::string &value) const
272 {
273     return value;
274 }
275
276 void FileNameOptionStorage::convertValue(const std::string &value)
277 {
278     bool bInput = isInputFile() || isInputOutputFile();
279     addValue(completeFileName(value, filetype_, bInput));
280 }
281
282 /********************************************************************
283  * FileNameOptionInfo
284  */
285
286 FileNameOptionInfo::FileNameOptionInfo(FileNameOptionStorage *option)
287     : OptionInfo(option)
288 {
289 }
290
291 const FileNameOptionStorage &FileNameOptionInfo::option() const
292 {
293     return static_cast<const FileNameOptionStorage &>(OptionInfo::option());
294 }
295
296 bool FileNameOptionInfo::isInputFile() const
297 {
298     return option().isInputFile();
299 }
300
301 bool FileNameOptionInfo::isOutputFile() const
302 {
303     return option().isOutputFile();
304 }
305
306 bool FileNameOptionInfo::isInputOutputFile() const
307 {
308     return option().isInputOutputFile();
309 }
310
311 bool FileNameOptionInfo::isLibraryFile() const
312 {
313     return option().isLibraryFile();
314 }
315
316 /********************************************************************
317  * FileNameOption
318  */
319
320 AbstractOptionStoragePointer FileNameOption::createStorage() const
321 {
322     return AbstractOptionStoragePointer(new FileNameOptionStorage(*this));
323 }
324
325 } // namespace gmx