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