Merge release-4-6 into master
[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 and filenameoptionstorage.h.
34  *
35  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36  * \ingroup module_options
37  */
38 #include "filenameoption.h"
39 #include "filenameoptionstorage.h"
40
41 #include <string>
42 #include <vector>
43
44 #include "gromacs/utility/file.h"
45 #include "gromacs/utility/stringutil.h"
46
47 namespace gmx
48 {
49
50 namespace
51 {
52
53 class FileTypeRegistry;
54
55 /********************************************************************
56  * FileTypeHandler
57  */
58
59 /*! \internal \brief
60  * Handles a single file type known to FileNameOptionStorage.
61  *
62  * \ingroup module_options
63  */
64 class FileTypeHandler
65 {
66     public:
67         //! Returns whether \p filename has a valid extension for this type.
68         bool hasKnownExtension(const std::string &filename) const;
69         //! Adds a default extension for this type to \p filename.
70         std::string addExtension(const std::string &filename) const;
71         /*! \brief
72          * Adds an extension to \p filename if it results in an existing file.
73          *
74          * Tries to add each extension for this file type to \p filename and
75          * checks whether this results in an existing file.
76          * The first match is returned.
77          * Returns an empty string if no existing file is found.
78          */
79         std::string findFileWithExtension(const std::string &filename) const;
80
81     private:
82         //! Possible extensions for this file type.
83         std::vector<const char *> extensions_;
84
85         /*! \brief
86          * Needed for initialization; all initialization is handled by
87          * FileTypeRegistry.
88          */
89         friend class FileTypeRegistry;
90 };
91
92 bool
93 FileTypeHandler::hasKnownExtension(const std::string &filename) const
94 {
95     for (size_t i = 0; i < extensions_.size(); ++i)
96     {
97         if (endsWith(filename, extensions_[i]))
98         {
99             return true;
100         }
101     }
102     return false;
103 }
104
105 std::string
106 FileTypeHandler::addExtension(const std::string &filename) const
107 {
108     if (extensions_.empty())
109     {
110         return filename;
111     }
112     return filename + extensions_[0];
113 }
114
115 std::string
116 FileTypeHandler::findFileWithExtension(const std::string &filename) const
117 {
118     for (size_t i = 0; i < extensions_.size(); ++i)
119     {
120         std::string testFilename(filename + extensions_[i]);
121         if (File::exists(testFilename))
122         {
123             return testFilename;
124         }
125     }
126     return std::string();
127 }
128
129 /********************************************************************
130  * FileTypeRegistry
131  */
132
133 /*! \internal \brief
134  * Singleton for managing static file type info for FileNameOptionStorage.
135  *
136  * \ingroup module_options
137  */
138 class FileTypeRegistry
139 {
140     public:
141         //! Returns a singleton instance of this class.
142         static const FileTypeRegistry &instance();
143         //! Returns a handler for a single file type.
144         const FileTypeHandler &handlerForType(OptionFileType type) const;
145
146     private:
147         //! Initializes the file type registry.
148         FileTypeRegistry();
149
150         //! Registers a file type with a single extension.
151         void registerType(OptionFileType type, const char *extension);
152         //! Registers a file type with multiple extensions.
153         template <size_t count>
154         void registerType(OptionFileType type,
155                           const char *const (&extensions)[count]);
156
157         std::vector<FileTypeHandler> filetypes_;
158 };
159
160 // static
161 const FileTypeRegistry &
162 FileTypeRegistry::instance()
163 {
164     static FileTypeRegistry singleton;
165     return singleton;
166 }
167
168 const FileTypeHandler &
169 FileTypeRegistry::handlerForType(OptionFileType type) const
170 {
171     GMX_RELEASE_ASSERT(type >= 0 && static_cast<size_t>(type) < filetypes_.size(),
172                        "Invalid file type");
173     return filetypes_[type];
174 }
175
176 FileTypeRegistry::FileTypeRegistry()
177 {
178     filetypes_.resize(eftOptionFileType_NR);
179     const char *const topExtensions[] = {
180         ".tpr", ".tpb", ".tpa", ".gro", ".g96", ".pdb", ".brk", ".ent"
181     };
182     const char *const trajExtensions[] = {
183         ".xtc", ".trr", ".trj", ".cpt", ".gro", ".g96", ".g87", ".pdb"
184     };
185     registerType(eftTopology,    topExtensions);
186     registerType(eftTrajectory,  trajExtensions);
187     registerType(eftPDB,         ".pdb");
188     registerType(eftIndex,       ".ndx");
189     registerType(eftPlot,        ".xvg");
190     registerType(eftGenericData, ".dat");
191 }
192
193 void FileTypeRegistry::registerType(OptionFileType type,
194                                     const char *extension)
195 {
196     GMX_RELEASE_ASSERT(type >= 0 && static_cast<size_t>(type) < filetypes_.size(),
197                        "Invalid file type");
198     filetypes_[type].extensions_.assign(1, extension);
199 }
200
201 template <size_t count>
202 void FileTypeRegistry::registerType(OptionFileType type,
203                                     const char *const (&extensions)[count])
204 {
205     GMX_RELEASE_ASSERT(type >= 0 && static_cast<size_t>(type) < filetypes_.size(),
206                        "Invalid file type");
207     filetypes_[type].extensions_.assign(extensions, extensions + count);
208 }
209
210 /*! \brief
211  * Helper method to complete a file name provided to a file name option.
212  *
213  * \param[in] value     Value provided to the file name option.
214  * \param[in] filetype  File type for the option.
215  * \param[in] bCompleteToExisting
216  *      Whether to check existing files when completing the extension.
217  * \returns   \p value with possible extension added.
218  */
219 std::string completeFileName(const std::string &value, OptionFileType filetype,
220                              bool bCompleteToExisting)
221 {
222     if (bCompleteToExisting && File::exists(value))
223     {
224         // TODO: This may not work as expected if the value is passed to a
225         // function that uses fn2ftp() to determine the file type and the input
226         // file has an unrecognized extension.
227         return value;
228     }
229     const FileTypeRegistry &registry = FileTypeRegistry::instance();
230     const FileTypeHandler &typeHandler = registry.handlerForType(filetype);
231     if (typeHandler.hasKnownExtension(value))
232     {
233         return value;
234     }
235     if (bCompleteToExisting)
236     {
237         std::string newValue = typeHandler.findFileWithExtension(value);
238         if (!newValue.empty())
239         {
240             return newValue;
241         }
242     }
243     return typeHandler.addExtension(value);
244 }
245
246 } // namespace
247
248 /********************************************************************
249  * FileNameOptionStorage
250  */
251
252 FileNameOptionStorage::FileNameOptionStorage(const FileNameOption &settings)
253     : MyBase(settings), info_(this), filetype_(settings.filetype_),
254       bRead_(settings.bRead_), bWrite_(settings.bWrite_),
255       bLibrary_(settings.bLibrary_)
256 {
257     if (settings.defaultBasename_ != NULL)
258     {
259         if (isRequired())
260         {
261             setDefaultValue(completeFileName(settings.defaultBasename_,
262                                              filetype_, false));
263         }
264         else
265         {
266             setDefaultValueIfSet(completeFileName(settings.defaultBasename_,
267                                                   filetype_, false));
268         }
269     }
270 }
271
272 std::string FileNameOptionStorage::formatSingleValue(const std::string &value) const
273 {
274     return value;
275 }
276
277 void FileNameOptionStorage::convertValue(const std::string &value)
278 {
279     bool bInput = isInputFile() || isInputOutputFile();
280     addValue(completeFileName(value, filetype_, bInput));
281 }
282
283 /********************************************************************
284  * FileNameOptionInfo
285  */
286
287 FileNameOptionInfo::FileNameOptionInfo(FileNameOptionStorage *option)
288     : OptionInfo(option)
289 {
290 }
291
292 const FileNameOptionStorage &FileNameOptionInfo::option() const
293 {
294     return static_cast<const FileNameOptionStorage &>(OptionInfo::option());
295 }
296
297 bool FileNameOptionInfo::isInputFile() const
298 {
299     return option().isInputFile();
300 }
301
302 bool FileNameOptionInfo::isOutputFile() const
303 {
304     return option().isOutputFile();
305 }
306
307 bool FileNameOptionInfo::isInputOutputFile() const
308 {
309     return option().isInputOutputFile();
310 }
311
312 bool FileNameOptionInfo::isLibraryFile() const
313 {
314     return option().isLibraryFile();
315 }
316
317 /********************************************************************
318  * FileNameOption
319  */
320
321 AbstractOptionStoragePointer FileNameOption::createStorage() const
322 {
323     return AbstractOptionStoragePointer(new FileNameOptionStorage(*this));
324 }
325
326 } // namespace gmx