Better invalid option value handling.
[alexxy/gromacs.git] / src / gromacs / options / abstractoption.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 abstractoption.h and abstractoptionstorage.h.
34  *
35  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36  * \ingroup module_options
37  */
38 #include "gromacs/options/abstractoption.h"
39
40 #include "gromacs/options/abstractoptionstorage.h"
41 #include "gromacs/options/optionflags.h"
42 #include "gromacs/options/optioninfo.h"
43 #include "gromacs/utility/exceptions.h"
44 #include "gromacs/utility/gmxassert.h"
45
46 #include "basicoptionstorage.h"
47
48 namespace gmx
49 {
50
51 /********************************************************************
52  * AbstractOptionStorage
53  */
54
55 AbstractOptionStorage::AbstractOptionStorage(const AbstractOption &settings,
56                                              OptionFlags staticFlags)
57     : flags_(settings.flags_ | staticFlags),
58       minValueCount_(settings.minValueCount_),
59       maxValueCount_(settings.maxValueCount_),
60       bInSet_(false), bSetValuesHadErrors_(false)
61 {
62     // Check that user has not provided incorrect values for vectors.
63     if (hasFlag(efOption_Vector) && (minValueCount_ > 1 || maxValueCount_ < 1))
64     {
65         GMX_THROW(APIError("Inconsistent value counts for vector values"));
66     }
67
68     if (settings.name_ != NULL)
69     {
70         name_  = settings.name_;
71     }
72     descr_ = settings.createDescription();
73     setFlag(efOption_ClearOnNextSet);
74 }
75
76 AbstractOptionStorage::~AbstractOptionStorage()
77 {
78 }
79
80 bool AbstractOptionStorage::isBoolean() const
81 {
82     return dynamic_cast<const BooleanOptionStorage *>(this) != NULL;
83 }
84
85 void AbstractOptionStorage::startSource()
86 {
87     setFlag(efOption_ClearOnNextSet);
88 }
89
90 void AbstractOptionStorage::startSet()
91 {
92     GMX_RELEASE_ASSERT(!bInSet_, "finishSet() not called");
93     // The last condition takes care of the situation where multiple
94     // sources are used, and a later source should be able to reassign
95     // the value even though the option is already set.
96     if (isSet() && !hasFlag(efOption_MultipleTimes)
97         && !hasFlag(efOption_ClearOnNextSet))
98     {
99         GMX_THROW(InvalidInputError("Option specified multiple times"));
100     }
101     clearSet();
102     bInSet_ = true;
103     bSetValuesHadErrors_ = false;
104 }
105
106 void AbstractOptionStorage::appendValue(const std::string &value)
107 {
108     GMX_RELEASE_ASSERT(bInSet_, "startSet() not called");
109     try
110     {
111         convertValue(value);
112     }
113     catch (...)
114     {
115         bSetValuesHadErrors_ = true;
116         throw;
117     }
118 }
119
120 void AbstractOptionStorage::finishSet()
121 {
122     GMX_RELEASE_ASSERT(bInSet_, "startSet() not called");
123     bInSet_ = false;
124     // We mark the option as set even when there are errors to avoid additional
125     // errors from required options not set.
126     // TODO: There could be a separate flag for this purpose.
127     setFlag(efOption_Set);
128     if (!bSetValuesHadErrors_)
129     {
130         // TODO: Correct handling of the efOption_ClearOnNextSet requires
131         // processSet() and/or convertValue() to check it internally.
132         // OptionStorageTemplate takes care of it, but it's error-prone if
133         // a custom option is implemented that doesn't use it.
134         processSet();
135     }
136     bSetValuesHadErrors_ = false;
137     clearFlag(efOption_ClearOnNextSet);
138     clearSet();
139 }
140
141 void AbstractOptionStorage::finish()
142 {
143     GMX_RELEASE_ASSERT(!bInSet_, "finishSet() not called");
144     processAll();
145     if (isRequired() && !isSet())
146     {
147         GMX_THROW(InvalidInputError("Option is required, but not set"));
148     }
149 }
150
151 void AbstractOptionStorage::setMinValueCount(int count)
152 {
153     GMX_RELEASE_ASSERT(!hasFlag(efOption_MultipleTimes),
154                        "setMinValueCount() not supported with efOption_MultipleTimes");
155     GMX_RELEASE_ASSERT(count >= 0, "Invalid value count");
156     minValueCount_ = count;
157     if (isSet() && !hasFlag(efOption_DontCheckMinimumCount)
158         && valueCount() < minValueCount_)
159     {
160         GMX_THROW(InvalidInputError("Too few values"));
161     }
162 }
163
164 void AbstractOptionStorage::setMaxValueCount(int count)
165 {
166     GMX_RELEASE_ASSERT(!hasFlag(efOption_MultipleTimes),
167                        "setMaxValueCount() not supported with efOption_MultipleTimes");
168     GMX_RELEASE_ASSERT(count >= -1, "Invalid value count");
169     maxValueCount_ = count;
170     if (isSet() && maxValueCount_ >= 0 && valueCount() > maxValueCount_)
171     {
172         GMX_THROW(InvalidInputError("Too many values"));
173     }
174 }
175
176 /********************************************************************
177  * OptionInfo
178  */
179
180 /*! \cond libapi */
181 OptionInfo::OptionInfo(AbstractOptionStorage *option)
182     : option_(*option)
183 {
184 }
185 //! \endcond
186
187 OptionInfo::~OptionInfo()
188 {
189 }
190
191 bool OptionInfo::isSet() const
192 {
193     return option().isSet();
194 }
195
196 bool OptionInfo::isHidden() const
197 {
198     return option().isHidden();
199 }
200
201 bool OptionInfo::isRequired() const
202 {
203     return option().isRequired();
204 }
205
206 const std::string &OptionInfo::name() const
207 {
208     return option().name();
209 }
210
211 const std::string &OptionInfo::description() const
212 {
213     return option().description();
214 }
215
216 const char *OptionInfo::type() const
217 {
218     return option().typeString();
219 }
220
221 int OptionInfo::valueCount() const
222 {
223     return option().valueCount();
224 }
225
226 std::string OptionInfo::formatValue(int i) const
227 {
228     return option().formatValue(i);
229 }
230
231 std::string OptionInfo::formatDefaultValueIfSet() const
232 {
233     return option().formatDefaultValueIfSet();
234 }
235
236 } // namespace gmx