Merge remote-tracking branch 'gerrit/release-4-6'
[alexxy/gromacs.git] / src / gromacs / options / basicoptions.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 basicoptions.h, basicoptioninfo.h and
34  * basicoptionstorage.h.
35  *
36  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
37  * \ingroup module_options
38  */
39 #include "gromacs/options/basicoptions.h"
40
41 #include <cstdio>
42 #include <cstdlib>
43
44 #include <string>
45 #include <vector>
46
47 #include "gromacs/options/basicoptioninfo.h"
48 #include "gromacs/utility/exceptions.h"
49 #include "gromacs/utility/format.h"
50
51 #include "basicoptionstorage.h"
52
53 template <typename T> static
54 void expandVector(size_t length, std::vector<T> *values)
55 {
56     if (length > 0 && values->size() > 0 && values->size() != length)
57     {
58         if (values->size() != 1)
59         {
60             GMX_THROW(gmx::InvalidInputError(gmx::formatString(
61                       "Expected 1 or %d values, got %d", length, values->size())));
62         }
63         const T &value = (*values)[0];
64         values->resize(length, value);
65     }
66 }
67
68 namespace gmx
69 {
70
71 /********************************************************************
72  * BooleanOptionStorage
73  */
74
75 std::string BooleanOptionStorage::formatSingleValue(const bool &value) const
76 {
77     return value ? "yes" : "no";
78 }
79
80 void BooleanOptionStorage::convertValue(const std::string &value)
81 {
82     // TODO: Case-independence
83     if (value == "1" || value == "yes" || value == "true")
84     {
85         addValue(true);
86         return;
87     }
88     else if (value == "0" || value == "no" || value == "false")
89     {
90         addValue(false);
91         return;
92     }
93     GMX_THROW(InvalidInputError("Invalid value: '" + value + "'; supported values are: 1, 0, yes, no, true, false"));
94 }
95
96 /********************************************************************
97  * BooleanOptionInfo
98  */
99
100 BooleanOptionInfo::BooleanOptionInfo(BooleanOptionStorage *option)
101     : OptionInfo(option)
102 {
103 }
104
105 /********************************************************************
106  * BooleanOption
107  */
108
109 AbstractOptionStoragePointer BooleanOption::createStorage() const
110 {
111     return AbstractOptionStoragePointer(new BooleanOptionStorage(*this));
112 }
113
114
115 /********************************************************************
116  * IntegerOptionStorage
117  */
118
119 std::string IntegerOptionStorage::formatSingleValue(const int &value) const
120 {
121     return formatString("%d", value);
122 }
123
124 void IntegerOptionStorage::convertValue(const std::string &value)
125 {
126     const char *ptr = value.c_str();
127     char *endptr;
128     long int ival = std::strtol(ptr, &endptr, 10);
129     if (*endptr != '\0')
130     {
131         GMX_THROW(InvalidInputError("Invalid value: " + value));
132     }
133     addValue(ival);
134 }
135
136 void IntegerOptionStorage::processSetValues(ValueList *values)
137 {
138     if (hasFlag(efVector))
139     {
140         expandVector(maxValueCount(), values);
141     }
142 }
143
144 /********************************************************************
145  * IntegerOptionInfo
146  */
147
148 IntegerOptionInfo::IntegerOptionInfo(IntegerOptionStorage *option)
149     : OptionInfo(option)
150 {
151 }
152
153 /********************************************************************
154  * IntegerOption
155  */
156
157 AbstractOptionStoragePointer IntegerOption::createStorage() const
158 {
159     return AbstractOptionStoragePointer(new IntegerOptionStorage(*this));
160 }
161
162
163 /********************************************************************
164  * DoubleOptionStorage
165  */
166
167 DoubleOptionStorage::DoubleOptionStorage(const DoubleOption &settings)
168     : MyBase(settings), info_(this), bTime_(settings._bTime), factor_(1.0)
169 {
170 }
171
172 const char *DoubleOptionStorage::typeString() const
173 {
174     return hasFlag(efVector) ? "vector" : (isTime() ? "time" : "double");
175 }
176
177 std::string DoubleOptionStorage::formatSingleValue(const double &value) const
178 {
179     return formatString("%g", value / factor_);
180 }
181
182 void DoubleOptionStorage::convertValue(const std::string &value)
183 {
184     const char *ptr = value.c_str();
185     char *endptr;
186     double dval = std::strtod(ptr, &endptr);
187     if (*endptr != '\0')
188     {
189         GMX_THROW(InvalidInputError("Invalid value: " + value));
190     }
191     addValue(dval * factor_);
192 }
193
194 void DoubleOptionStorage::processSetValues(ValueList *values)
195 {
196     if (hasFlag(efVector))
197     {
198         expandVector(maxValueCount(), values);
199     }
200 }
201
202 void DoubleOptionStorage::processAll()
203 {
204 }
205
206 void DoubleOptionStorage::setScaleFactor(double factor)
207 {
208     GMX_RELEASE_ASSERT(factor > 0.0, "Invalid scaling factor");
209     if (!hasFlag(efHasDefaultValue))
210     {
211         double scale = factor / factor_;
212         ValueList::iterator i;
213         for (i = values().begin(); i != values().end(); ++i)
214         {
215             (*i) *= scale;
216         }
217         refreshValues();
218     }
219     factor_ = factor;
220 }
221
222 /********************************************************************
223  * DoubleOptionInfo
224  */
225
226 DoubleOptionInfo::DoubleOptionInfo(DoubleOptionStorage *option)
227     : OptionInfo(option)
228 {
229 }
230
231 DoubleOptionStorage &DoubleOptionInfo::option()
232 {
233     return static_cast<DoubleOptionStorage &>(OptionInfo::option());
234 }
235
236 const DoubleOptionStorage &DoubleOptionInfo::option() const
237 {
238     return static_cast<const DoubleOptionStorage &>(OptionInfo::option());
239 }
240
241 bool DoubleOptionInfo::isTime() const
242 {
243     return option().isTime();
244 }
245
246 void DoubleOptionInfo::setScaleFactor(double factor)
247 {
248     option().setScaleFactor(factor);
249 }
250
251 /********************************************************************
252  * DoubleOption
253  */
254
255 AbstractOptionStoragePointer DoubleOption::createStorage() const
256 {
257     return AbstractOptionStoragePointer(new DoubleOptionStorage(*this));
258 }
259
260
261 /********************************************************************
262  * StringOptionStorage
263  */
264
265 StringOptionStorage::StringOptionStorage(const StringOption &settings)
266     : MyBase(settings), _info(this), _enumIndexStore(NULL)
267 {
268     if (settings._defaultEnumIndex >= 0 && settings._enumValues == NULL)
269     {
270         GMX_THROW(APIError("Cannot set default enum index without enum values"));
271     }
272     if (settings._enumIndexStore != NULL && settings._enumValues == NULL)
273     {
274         GMX_THROW(APIError("Cannot set enum index store without enum values"));
275     }
276     if (settings._enumIndexStore != NULL && settings._maxValueCount < 0)
277     {
278         GMX_THROW(APIError("Cannot set enum index store with arbitrary number of values"));
279     }
280     if (settings._enumValues != NULL)
281     {
282         _enumIndexStore = settings._enumIndexStore;
283         const std::string *defaultValue = settings.defaultValue();
284         int match = -1;
285         for (int i = 0; settings._enumValues[i] != NULL; ++i)
286         {
287             if (defaultValue != NULL && settings._enumValues[i] == *defaultValue)
288             {
289                 match = i;
290             }
291             _allowed.push_back(settings._enumValues[i]);
292         }
293         if (defaultValue != NULL)
294         {
295             if (match < 0)
296             {
297                 GMX_THROW(APIError("Default value is not one of allowed values"));
298             }
299         }
300         if (settings._defaultEnumIndex >= 0)
301         {
302             if (settings._defaultEnumIndex >= static_cast<int>(_allowed.size()))
303             {
304                 GMX_THROW(APIError("Default enumeration index is out of range"));
305             }
306             if (defaultValue != NULL && *defaultValue != _allowed[settings._defaultEnumIndex])
307             {
308                 GMX_THROW(APIError("Conflicting default values"));
309             }
310         }
311         // If there is no default value, match is still -1.
312         if (_enumIndexStore != NULL)
313         {
314             *_enumIndexStore = match;
315         }
316     }
317     if (settings._defaultEnumIndex >= 0)
318     {
319         clear();
320         addValue(_allowed[settings._defaultEnumIndex]);
321         commitValues();
322     }
323 }
324
325 std::string StringOptionStorage::formatSingleValue(const std::string &value) const
326 {
327     return value;
328 }
329
330 void StringOptionStorage::convertValue(const std::string &value)
331 {
332     if (_allowed.size() == 0)
333     {
334         addValue(value);
335     }
336     else
337     {
338         ValueList::const_iterator  i;
339         ValueList::const_iterator  match = _allowed.end();
340         for (i = _allowed.begin(); i != _allowed.end(); ++i)
341         {
342             // TODO: Case independence.
343             if (i->find(value) == 0)
344             {
345                 if (match == _allowed.end() || i->size() < match->size())
346                 {
347                     match = i;
348                 }
349             }
350         }
351         if (match == _allowed.end())
352         {
353             GMX_THROW(InvalidInputError("Invalid value: " + value));
354         }
355         addValue(*match);
356     }
357 }
358
359 void StringOptionStorage::refreshValues()
360 {
361     MyBase::refreshValues();
362     if (_enumIndexStore != NULL)
363     {
364         for (size_t i = 0; i < values().size(); ++i)
365         {
366             ValueList::const_iterator match =
367                 std::find(_allowed.begin(), _allowed.end(), values()[i]);
368             GMX_ASSERT(match != _allowed.end(),
369                        "Enum value not found (internal error)");
370             _enumIndexStore[i] = static_cast<int>(match - _allowed.begin());
371         }
372     }
373 }
374
375 /********************************************************************
376  * StringOptionInfo
377  */
378
379 StringOptionInfo::StringOptionInfo(StringOptionStorage *option)
380     : OptionInfo(option)
381 {
382 }
383
384 /********************************************************************
385  * StringOption
386  */
387
388 AbstractOptionStoragePointer StringOption::createStorage() const
389 {
390     return AbstractOptionStoragePointer(new StringOptionStorage(*this));
391 }
392
393 std::string StringOption::createDescription() const
394 {
395     std::string value(MyBase::createDescription());
396
397     if (_enumValues != NULL)
398     {
399         value.append(": ");
400         for (int i = 0; _enumValues[i] != NULL; ++i)
401         {
402             value.append(_enumValues[i]);
403             if (_enumValues[i + 1] != NULL)
404             {
405                 value.append(_enumValues[i + 2] != NULL ? ", " : ", or ");
406             }
407         }
408     }
409     return value;
410 }
411
412 } // namespace gmx