Merge remote-tracking branch 'gerrit/release-4-6'
[alexxy/gromacs.git] / src / gromacs / options / optionsassigner.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 gmx::OptionsAssigner.
34  *
35  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36  * \ingroup module_options
37  */
38 #include "gromacs/options/optionsassigner.h"
39
40 #include <deque>
41
42 #include "gromacs/options/abstractoptionstorage.h"
43 #include "gromacs/options/options.h"
44 #include "gromacs/utility/exceptions.h"
45 #include "gromacs/utility/gmxassert.h"
46
47 #include "options-impl.h"
48
49 namespace gmx
50 {
51
52 /********************************************************************
53  * OptionsAssigner::Impl
54  */
55
56 /*! \internal \brief
57  * Private implementation class for OptionsAssigner.
58  *
59  * \ingroup module_options
60  */
61 class OptionsAssigner::Impl
62 {
63     public:
64         //! Possible flags for controlling assignment behavior.
65         enum Flag
66         {
67             //! Recognize boolean option "name" also as "noname".
68             efAcceptBooleanNoPrefix     = 1<<0,
69             //! Look for options in all sections, not just the current one.
70             efNoStrictSectioning        = 1<<1,
71         };
72         //! Sets the option object to assign to.
73         Impl(Options *options);
74
75         //! Sets or clears the given flag.
76         void setFlag(Flag flag, bool bSet);
77
78         //! Returns true if the given flag is set.
79         bool hasFlag(Flag flag) const { return _flags & flag; }
80         //! Returns true if a subsection has been set.
81         bool inSubSection() const { return _sectionStack.size() > 1; }
82         //! Returns the Options object for the current section.
83         Options &currentSection() const { return *_sectionStack.back(); }
84         /*! \brief
85          * Finds an option by the given name.
86          *
87          * \param[in] name  Name of the option to look for.
88          * \returns Pointer to the found option, or NULL if none found.
89          *
90          * This function takes into account the flags specified, and may change
91          * the internal state of the assigner to match the option found.
92          * If no option is found, the internal state is not modified.
93          */
94         AbstractOptionStorage *findOption(const char *name);
95
96         //! Options object to assign to.
97         Options                &_options;
98         //! Flags that control assignment behavior.
99         unsigned long           _flags;
100         /*! \brief
101          * List of (sub)sections being assigned to.
102          *
103          * The first element always points to \a _options.
104          */
105         std::vector<Options *>  _sectionStack;
106         //! Current option being assigned to, or NULL if none.
107         AbstractOptionStorage  *_currentOption;
108         //! Number of values assigned so far to the current option.
109         int                     _currentValueCount;
110         //! If true, a "no" prefix was given for the current boolean option.
111         bool                    _reverseBoolean;
112 };
113
114 OptionsAssigner::Impl::Impl(Options *options)
115     : _options(*options), _flags(0), _currentOption(NULL),
116       _currentValueCount(0), _reverseBoolean(false)
117 {
118     _sectionStack.push_back(&_options);
119 }
120
121 void OptionsAssigner::Impl::setFlag(OptionsAssigner::Impl::Flag flag, bool bSet)
122 {
123     if (bSet)
124     {
125         _flags |= flag;
126     }
127     else
128     {
129         _flags &= ~flag;
130     }
131 }
132
133 AbstractOptionStorage *
134 OptionsAssigner::Impl::findOption(const char *name)
135 {
136     GMX_RELEASE_ASSERT(_currentOption == NULL,
137                        "Cannot search for another option while processing one");
138     AbstractOptionStorage *option = NULL;
139     Options *section = NULL;
140     Options *root = &currentSection();
141     Options *oldRoot = NULL;
142     int      upcount = 0;
143     std::deque<Options *> searchList;
144     searchList.push_back(root);
145     while (option == NULL && !searchList.empty())
146     {
147         section = searchList.front();
148         option = section->_impl->findOption(name);
149         if (option == NULL && hasFlag(efAcceptBooleanNoPrefix))
150         {
151             if (name[0] == 'n' && name[1] == 'o')
152             {
153                 option = section->_impl->findOption(name + 2);
154                 if (option != NULL && option->isBoolean())
155                 {
156                     _reverseBoolean = true;
157                 }
158                 else
159                 {
160                     option = NULL;
161                 }
162             }
163         }
164         searchList.pop_front();
165         if (hasFlag(efNoStrictSectioning))
166         {
167             Options::Impl::SubSectionList::const_iterator i;
168             for (i = section->_impl->_subSections.begin();
169                  i != section->_impl->_subSections.end(); ++i)
170             {
171                 if (*i != oldRoot)
172                 {
173                     searchList.push_back(*i);
174                 }
175             }
176             if (searchList.empty() && root != &_options)
177             {
178                 root = root->_impl->_parent;
179                 ++upcount;
180                 searchList.push_back(root);
181             }
182         }
183     }
184     if (hasFlag(efNoStrictSectioning) && option != NULL)
185     {
186         while (upcount > 0)
187         {
188             _sectionStack.pop_back();
189             --upcount;
190         }
191         std::vector<Options *> sections;
192         while (section != &currentSection())
193         {
194             sections.push_back(section);
195             section = section->_impl->_parent;
196         }
197         while (!sections.empty())
198         {
199             _sectionStack.push_back(sections.back());
200             sections.pop_back();
201         }
202     }
203     return option;
204 }
205
206 /********************************************************************
207  * OptionsAssigner
208  */
209
210 OptionsAssigner::OptionsAssigner(Options *options)
211     : _impl(new Impl(options))
212 {
213 }
214
215 OptionsAssigner::~OptionsAssigner()
216 {
217 }
218
219 void OptionsAssigner::setAcceptBooleanNoPrefix(bool enabled)
220 {
221     _impl->setFlag(Impl::efAcceptBooleanNoPrefix, enabled);
222 }
223
224 void OptionsAssigner::setNoStrictSectioning(bool enabled)
225 {
226     _impl->setFlag(Impl::efNoStrictSectioning, enabled);
227 }
228
229 void OptionsAssigner::start()
230 {
231     _impl->_options._impl->startSource();
232 }
233
234 void OptionsAssigner::startSubSection(const char *name)
235 {
236     Options *section = _impl->currentSection()._impl->findSubSection(name);
237     if (section == NULL)
238     {
239         GMX_THROW(InvalidInputError("Unknown subsection"));
240     }
241     _impl->_sectionStack.push_back(section);
242 }
243
244 void OptionsAssigner::startOption(const char *name)
245 {
246     GMX_RELEASE_ASSERT(_impl->_currentOption == NULL, "finishOption() not called");
247     AbstractOptionStorage *option = _impl->findOption(name);
248     if (option == NULL)
249     {
250         GMX_THROW(InvalidInputError("Unknown option"));
251     }
252     option->startSet();
253     _impl->_currentOption = option;
254     _impl->_currentValueCount = 0;
255 }
256
257 void OptionsAssigner::appendValue(const std::string &value)
258 {
259     AbstractOptionStorage *option = _impl->_currentOption;
260     GMX_RELEASE_ASSERT(option != NULL, "startOption() not called");
261     // Does not count correctly, but the actual count is not really used.
262     // TODO: Rename the variable to better reflect the usage.
263     ++_impl->_currentValueCount;
264     option->appendValue(value);
265 }
266
267 void OptionsAssigner::finishOption()
268 {
269     AbstractOptionStorage *option = _impl->_currentOption;
270     GMX_RELEASE_ASSERT(option != NULL, "startOption() not called");
271     bool bBoolReverseValue = false;
272     if (option->isBoolean())
273     {
274         if (_impl->_currentValueCount == 0)
275         {
276             // Should not throw, otherwise something is wrong.
277             // TODO: Get rid of the hard-coded values.
278             option->appendValue(_impl->_reverseBoolean ? "0" : "1");
279         }
280         else if (_impl->_reverseBoolean)
281         {
282             bBoolReverseValue = true;
283         }
284     }
285     _impl->_currentOption = NULL;
286     _impl->_reverseBoolean = false;
287     option->finishSet();
288     if (bBoolReverseValue)
289     {
290         GMX_THROW(InvalidInputError("Cannot specify a value together with 'no' prefix"));
291     }
292 }
293
294 void OptionsAssigner::finishSubSection()
295 {
296     // Should only be called if we are in a subsection.
297     GMX_RELEASE_ASSERT(_impl->inSubSection(), "startSubSection() not called");
298     _impl->_sectionStack.pop_back();
299 }
300
301 void OptionsAssigner::finish()
302 {
303     GMX_RELEASE_ASSERT(_impl->_currentOption == NULL, "finishOption() not called");
304     if (_impl->hasFlag(Impl::efNoStrictSectioning))
305     {
306         while (_impl->inSubSection())
307         {
308             finishSubSection();
309         }
310     }
311     GMX_RELEASE_ASSERT(!_impl->inSubSection(), "finishSubSection() not called");
312 }
313
314 } // namespace gmx