Apply clang-format to source tree
[alexxy/gromacs.git] / src / gromacs / options / optionsassigner.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2010-2017, The GROMACS development team.
5  * Copyright (c) 2019, by the GROMACS development team, led by
6  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7  * and including many others, as listed in the AUTHORS file in the
8  * top-level source directory and at http://www.gromacs.org.
9  *
10  * GROMACS is free software; you can redistribute it and/or
11  * modify it under the terms of the GNU Lesser General Public License
12  * as published by the Free Software Foundation; either version 2.1
13  * of the License, or (at your option) any later version.
14  *
15  * GROMACS is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
18  * Lesser General Public License for more details.
19  *
20  * You should have received a copy of the GNU Lesser General Public
21  * License along with GROMACS; if not, see
22  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
24  *
25  * If you want to redistribute modifications to GROMACS, please
26  * consider that scientific software is very special. Version
27  * control is crucial - bugs must be traceable. We will be happy to
28  * consider code for inclusion in the official distribution, but
29  * derived work must not be called official GROMACS. Details are found
30  * in the README & COPYING files - if they are missing, get the
31  * official version at http://www.gromacs.org.
32  *
33  * To help us fund GROMACS development, we humbly ask that you cite
34  * the research papers on the package. Check out http://www.gromacs.org.
35  */
36 /*! \internal \file
37  * \brief
38  * Implements gmx::OptionsAssigner.
39  *
40  * \author Teemu Murtola <teemu.murtola@gmail.com>
41  * \ingroup module_options
42  */
43 #include "gmxpre.h"
44
45 #include "optionsassigner.h"
46
47 #include <deque>
48
49 #include "gromacs/options/abstractoptionstorage.h"
50 #include "gromacs/options/options.h"
51 #include "gromacs/utility/any.h"
52 #include "gromacs/utility/exceptions.h"
53 #include "gromacs/utility/gmxassert.h"
54
55 #include "options_impl.h"
56
57 namespace gmx
58 {
59
60 /********************************************************************
61  * OptionsAssigner::Impl
62  */
63
64 /*! \internal \brief
65  * Private implementation class for OptionsAssigner.
66  *
67  * \ingroup module_options
68  */
69 class OptionsAssigner::Impl
70 {
71 public:
72     //! Shorthand for the internal type used to represent a section.
73     typedef internal::OptionSectionImpl Section;
74
75     //! Sets the option object to assign to.
76     explicit Impl(Options* options);
77
78     //! Returns true if a subsection has been set.
79     bool inSection() const { return sectionStack_.size() > 1; }
80     //! Returns the Options object for the current section.
81     Section& currentSection() const { return *sectionStack_.back(); }
82     /*! \brief
83      * Finds an option by the given name.
84      *
85      * \param[in] name  Name of the option to look for.
86      * \returns Pointer to the found option, or NULL if none found.
87      *
88      * This function takes into account the flags specified, and may change
89      * the internal state of the assigner to match the option found.
90      * If no option is found, the internal state is not modified.
91      */
92     AbstractOptionStorage* findOption(const char* name);
93
94     //! Options object to assign to.
95     Options& options_;
96     //! Recognize boolean option "name" also as "noname".
97     bool bAcceptBooleanNoPrefix_;
98     /*! \brief
99      * List of (sub)sections being assigned to.
100      *
101      * The first element always points to \a options_.
102      */
103     std::vector<Section*> sectionStack_;
104     //! Current option being assigned to, or NULL if none.
105     AbstractOptionStorage* currentOption_;
106     /*! \brief
107      * Number of values assigned so far to the current option.
108      *
109      * Counts the number of attempted assignments, whether they have been
110      * successful or not.
111      */
112     int currentValueCount_;
113     //! If true, a "no" prefix was given for the current boolean option.
114     bool reverseBoolean_;
115 };
116
117 OptionsAssigner::Impl::Impl(Options* options) :
118     options_(*options),
119     bAcceptBooleanNoPrefix_(false),
120     currentOption_(nullptr),
121     currentValueCount_(0),
122     reverseBoolean_(false)
123 {
124     sectionStack_.push_back(&options_.impl_->rootSection_);
125 }
126
127 AbstractOptionStorage* OptionsAssigner::Impl::findOption(const char* name)
128 {
129     GMX_RELEASE_ASSERT(currentOption_ == nullptr,
130                        "Cannot search for another option while processing one");
131     const Section&         section = currentSection();
132     AbstractOptionStorage* option  = section.findOption(name);
133     if (option == nullptr && bAcceptBooleanNoPrefix_)
134     {
135         if (name[0] == 'n' && name[1] == 'o')
136         {
137             option = section.findOption(name + 2);
138             if (option != nullptr && option->isBoolean())
139             {
140                 reverseBoolean_ = true;
141             }
142             else
143             {
144                 option = nullptr;
145             }
146         }
147     }
148     return option;
149 }
150
151 /********************************************************************
152  * OptionsAssigner
153  */
154
155 OptionsAssigner::OptionsAssigner(Options* options) : impl_(new Impl(options)) {}
156
157 OptionsAssigner::~OptionsAssigner() {}
158
159 void OptionsAssigner::setAcceptBooleanNoPrefix(bool bEnabled)
160 {
161     impl_->bAcceptBooleanNoPrefix_ = bEnabled;
162 }
163
164 void OptionsAssigner::start()
165 {
166     impl_->options_.impl_->rootSection_.start();
167 }
168
169 void OptionsAssigner::startSection(const char* name)
170 {
171     Impl::Section* section = impl_->currentSection().findSection(name);
172     if (section == nullptr)
173     {
174         GMX_THROW(InvalidInputError("Unknown subsection"));
175     }
176     impl_->sectionStack_.push_back(section);
177     section->start();
178 }
179
180 void OptionsAssigner::startOption(const char* name)
181 {
182     if (!tryStartOption(name))
183     {
184         GMX_THROW(InvalidInputError("Unknown option " + std::string(name)));
185     }
186 }
187
188 bool OptionsAssigner::tryStartOption(const char* name)
189 {
190     GMX_RELEASE_ASSERT(impl_->currentOption_ == nullptr, "finishOption() not called");
191     AbstractOptionStorage* option = impl_->findOption(name);
192     if (option == nullptr)
193     {
194         return false;
195     }
196     option->startSet();
197     impl_->currentOption_     = option;
198     impl_->currentValueCount_ = 0;
199     return true;
200 }
201
202 void OptionsAssigner::appendValue(const std::string& value)
203 {
204     appendValue(Any(value));
205 }
206
207 void OptionsAssigner::appendValue(const Any& value)
208 {
209     AbstractOptionStorage* option = impl_->currentOption_;
210     GMX_RELEASE_ASSERT(option != nullptr, "startOption() not called");
211     ++impl_->currentValueCount_;
212     option->appendValue(value);
213 }
214
215 void OptionsAssigner::finishOption()
216 {
217     AbstractOptionStorage* option = impl_->currentOption_;
218     GMX_RELEASE_ASSERT(option != nullptr, "startOption() not called");
219     bool bBoolReverseValue = false;
220     if (option->isBoolean())
221     {
222         if (impl_->currentValueCount_ == 0)
223         {
224             // Should not throw, otherwise something is wrong.
225             option->appendValue(Any::create<bool>(!impl_->reverseBoolean_));
226         }
227         else if (impl_->reverseBoolean_)
228         {
229             bBoolReverseValue = true;
230         }
231     }
232     impl_->currentOption_  = nullptr;
233     impl_->reverseBoolean_ = false;
234     option->finishSet();
235     if (bBoolReverseValue)
236     {
237         GMX_THROW(InvalidInputError("Cannot specify a value together with 'no' prefix"));
238     }
239 }
240
241 void OptionsAssigner::finishSection()
242 {
243     // Should only be called if we are in a subsection.
244     GMX_RELEASE_ASSERT(impl_->inSection(), "startSection() not called");
245     Impl::Section* section = impl_->sectionStack_.back();
246     section->finish();
247     impl_->sectionStack_.pop_back();
248 }
249
250 void OptionsAssigner::finish()
251 {
252     GMX_RELEASE_ASSERT(impl_->currentOption_ == nullptr, "finishOption() not called");
253     GMX_RELEASE_ASSERT(!impl_->inSection(), "finishSection() not called");
254 }
255
256 } // namespace gmx