Merge branch 'release-4-6'
[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 &handlerForType(OptionFileType type) const;
156
157     private:
158         //! Initializes the file type registry.
159         FileTypeRegistry();
160
161         //! Registers a file type that corresponds to a ftp in filenm.h.
162         void registerType(OptionFileType type, int ftp);
163         //! Registers a file type with a single extension.
164         void registerType(OptionFileType type, const char *extension);
165
166         std::vector<FileTypeHandler> filetypes_;
167 };
168
169 // static
170 const FileTypeRegistry &
171 FileTypeRegistry::instance()
172 {
173     static FileTypeRegistry singleton;
174     return singleton;
175 }
176
177 const FileTypeHandler &
178 FileTypeRegistry::handlerForType(OptionFileType type) const
179 {
180     GMX_RELEASE_ASSERT(type >= 0 && static_cast<size_t>(type) < filetypes_.size(),
181                        "Invalid file type");
182     return filetypes_[type];
183 }
184
185 FileTypeRegistry::FileTypeRegistry()
186 {
187     filetypes_.resize(eftOptionFileType_NR);
188     registerType(eftTopology,    efTPS);
189     registerType(eftTrajectory,  efTRX);
190     registerType(eftPDB,         efPDB);
191     registerType(eftIndex,       efNDX);
192     registerType(eftPlot,        efXVG);
193     registerType(eftGenericData, efDAT);
194 }
195
196 void FileTypeRegistry::registerType(OptionFileType type, int ftp)
197 {
198     GMX_RELEASE_ASSERT(type >= 0 && static_cast<size_t>(type) < filetypes_.size(),
199                        "Invalid file type");
200     const int genericTypeCount = ftp2generic_count(ftp);
201     if (genericTypeCount > 0)
202     {
203         const int *const genericTypes = ftp2generic_list(ftp);
204         filetypes_[type].extensions_.clear();
205         filetypes_[type].extensions_.reserve(genericTypeCount);
206         for (int i = 0; i < genericTypeCount; ++i)
207         {
208             filetypes_[type].extensions_.push_back(ftp2ext_with_dot(genericTypes[i]));
209         }
210     }
211     else
212     {
213         registerType(type, ftp2ext_with_dot(ftp));
214     }
215 }
216
217 void FileTypeRegistry::registerType(OptionFileType type,
218                                     const char    *extension)
219 {
220     GMX_RELEASE_ASSERT(type >= 0 && static_cast<size_t>(type) < filetypes_.size(),
221                        "Invalid file type");
222     filetypes_[type].extensions_.assign(1, extension);
223 }
224
225 /*! \brief
226  * Helper method to complete a file name provided to a file name option.
227  *
228  * \param[in] value     Value provided to the file name option.
229  * \param[in] filetype  File type for the option.
230  * \param[in] bCompleteToExisting
231  *      Whether to check existing files when completing the extension.
232  * \returns   \p value with possible extension added.
233  */
234 std::string completeFileName(const std::string &value, OptionFileType filetype,
235                              bool bCompleteToExisting)
236 {
237     if (bCompleteToExisting && File::exists(value))
238     {
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         return value;
243     }
244     const FileTypeRegistry &registry    = FileTypeRegistry::instance();
245     const FileTypeHandler  &typeHandler = registry.handlerForType(filetype);
246     if (typeHandler.hasKnownExtension(value))
247     {
248         return value;
249     }
250     if (bCompleteToExisting)
251     {
252         std::string newValue = typeHandler.findFileWithExtension(value);
253         if (!newValue.empty())
254         {
255             return newValue;
256         }
257     }
258     return typeHandler.addExtension(value);
259 }
260
261 //! \}
262
263 }   // namespace
264
265 /********************************************************************
266  * FileNameOptionStorage
267  */
268
269 FileNameOptionStorage::FileNameOptionStorage(const FileNameOption &settings)
270     : MyBase(settings), info_(this), filetype_(settings.filetype_),
271       bRead_(settings.bRead_), bWrite_(settings.bWrite_),
272       bLibrary_(settings.bLibrary_)
273 {
274     if (settings.defaultBasename_ != NULL)
275     {
276         if (isRequired())
277         {
278             setDefaultValue(completeFileName(settings.defaultBasename_,
279                                              filetype_, false));
280         }
281         else
282         {
283             setDefaultValueIfSet(completeFileName(settings.defaultBasename_,
284                                                   filetype_, false));
285         }
286     }
287 }
288
289 std::string FileNameOptionStorage::typeString() const
290 {
291     const FileTypeRegistry       &registry    = FileTypeRegistry::instance();
292     const FileTypeHandler        &typeHandler = registry.handlerForType(filetype_);
293     const ExtensionList          &extensions  = typeHandler.extensions();
294     std::string                   result;
295     ExtensionList::const_iterator i;
296     int                           count = 0;
297     for (i = extensions.begin(); count < 2 && i != extensions.end(); ++i, ++count)
298     {
299         if (i != extensions.begin())
300         {
301             result.append("/");
302         }
303         result.append(*i);
304     }
305     if (i != extensions.end())
306     {
307         result.append("/...");
308     }
309     if (result.empty())
310     {
311         result = "file";
312     }
313     return result;
314 }
315
316 std::string FileNameOptionStorage::formatExtraDescription() const
317 {
318     const FileTypeRegistry       &registry    = FileTypeRegistry::instance();
319     const FileTypeHandler        &typeHandler = registry.handlerForType(filetype_);
320     const ExtensionList          &extensions  = typeHandler.extensions();
321     std::string                   result;
322     if (extensions.size() > 2)
323     {
324         result.append(":");
325         ExtensionList::const_iterator i;
326         for (i = extensions.begin(); i != extensions.end(); ++i)
327         {
328             result.append(" ");
329             result.append((*i) + 1);
330         }
331     }
332     return result;
333 }
334
335 std::string FileNameOptionStorage::formatSingleValue(const std::string &value) const
336 {
337     return value;
338 }
339
340 void FileNameOptionStorage::convertValue(const std::string &value)
341 {
342     bool bInput = isInputFile() || isInputOutputFile();
343     addValue(completeFileName(value, filetype_, bInput));
344 }
345
346 /********************************************************************
347  * FileNameOptionInfo
348  */
349
350 FileNameOptionInfo::FileNameOptionInfo(FileNameOptionStorage *option)
351     : OptionInfo(option)
352 {
353 }
354
355 const FileNameOptionStorage &FileNameOptionInfo::option() const
356 {
357     return static_cast<const FileNameOptionStorage &>(OptionInfo::option());
358 }
359
360 bool FileNameOptionInfo::isInputFile() const
361 {
362     return option().isInputFile();
363 }
364
365 bool FileNameOptionInfo::isOutputFile() const
366 {
367     return option().isOutputFile();
368 }
369
370 bool FileNameOptionInfo::isInputOutputFile() const
371 {
372     return option().isInputOutputFile();
373 }
374
375 bool FileNameOptionInfo::isLibraryFile() const
376 {
377     return option().isLibraryFile();
378 }
379
380 /********************************************************************
381  * FileNameOption
382  */
383
384 AbstractOptionStoragePointer FileNameOption::createStorage() const
385 {
386     return AbstractOptionStoragePointer(new FileNameOptionStorage(*this));
387 }
388
389 } // namespace gmx