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