Add a few C++ helper classes/macros.
[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/fatalerror/exceptions.h"
43 #include "gromacs/fatalerror/gmxassert.h"
44 #include "gromacs/options/abstractoptionstorage.h"
45 #include "gromacs/options/options.h"
46
47 #include "optionsassigner-impl.h"
48 #include "options-impl.h"
49
50 namespace gmx
51 {
52
53 /********************************************************************
54  * OptionsAssigner::Impl
55  */
56
57 OptionsAssigner::Impl::Impl(Options *options)
58     : _options(*options), _flags(0), _currentOption(NULL),
59       _currentValueCount(0), _reverseBoolean(false)
60 {
61     _sectionStack.push_back(&_options);
62 }
63
64 void OptionsAssigner::Impl::setFlag(OptionsAssigner::Impl::Flag flag, bool bSet)
65 {
66     if (bSet)
67     {
68         _flags |= flag;
69     }
70     else
71     {
72         _flags &= ~flag;
73     }
74 }
75
76 AbstractOptionStorage *
77 OptionsAssigner::Impl::findOption(const char *name)
78 {
79     GMX_RELEASE_ASSERT(_currentOption == NULL,
80                        "Cannot search for another option while processing one");
81     AbstractOptionStorage *option = NULL;
82     Options *section = NULL;
83     Options *root = &currentSection();
84     Options *oldRoot = NULL;
85     int      upcount = 0;
86     std::deque<Options *> searchList;
87     searchList.push_back(root);
88     while (option == NULL && !searchList.empty())
89     {
90         section = searchList.front();
91         option = section->_impl->findOption(name);
92         if (option == NULL && hasFlag(efAcceptBooleanNoPrefix))
93         {
94             if (name[0] == 'n' && name[1] == 'o')
95             {
96                 option = section->_impl->findOption(name + 2);
97                 if (option != NULL && option->isBoolean())
98                 {
99                     _reverseBoolean = true;
100                 }
101                 else
102                 {
103                     option = NULL;
104                 }
105             }
106         }
107         searchList.pop_front();
108         if (hasFlag(efNoStrictSectioning))
109         {
110             Options::Impl::SubSectionList::const_iterator i;
111             for (i = section->_impl->_subSections.begin();
112                  i != section->_impl->_subSections.end(); ++i)
113             {
114                 if (*i != oldRoot)
115                 {
116                     searchList.push_back(*i);
117                 }
118             }
119             if (searchList.empty() && root != &_options)
120             {
121                 Options *oldRoot = root;
122                 root = root->_impl->_parent;
123                 ++upcount;
124                 searchList.push_back(root);
125             }
126         }
127     }
128     if (hasFlag(efNoStrictSectioning) && option != NULL)
129     {
130         while (upcount > 0)
131         {
132             _sectionStack.pop_back();
133             --upcount;
134         }
135         std::vector<Options *> sections;
136         while (section != &currentSection())
137         {
138             sections.push_back(section);
139             section = section->_impl->_parent;
140         }
141         while (!sections.empty())
142         {
143             _sectionStack.push_back(sections.back());
144             sections.pop_back();
145         }
146     }
147     return option;
148 }
149
150 /********************************************************************
151  * OptionsAssigner
152  */
153
154 OptionsAssigner::OptionsAssigner(Options *options)
155     : _impl(new Impl(options))
156 {
157 }
158
159 OptionsAssigner::~OptionsAssigner()
160 {
161 }
162
163 void OptionsAssigner::setAcceptBooleanNoPrefix(bool enabled)
164 {
165     _impl->setFlag(Impl::efAcceptBooleanNoPrefix, enabled);
166 }
167
168 void OptionsAssigner::setNoStrictSectioning(bool enabled)
169 {
170     _impl->setFlag(Impl::efNoStrictSectioning, enabled);
171 }
172
173 void OptionsAssigner::start()
174 {
175     _impl->_options._impl->startSource();
176 }
177
178 void OptionsAssigner::startSubSection(const char *name)
179 {
180     Options *section = _impl->currentSection()._impl->findSubSection(name);
181     if (section == NULL)
182     {
183         GMX_THROW(InvalidInputError("Unknown subsection"));
184     }
185     _impl->_sectionStack.push_back(section);
186 }
187
188 void OptionsAssigner::startOption(const char *name)
189 {
190     GMX_RELEASE_ASSERT(_impl->_currentOption == NULL, "finishOption() not called");
191     AbstractOptionStorage *option = _impl->findOption(name);
192     if (option == NULL)
193     {
194         GMX_THROW(InvalidInputError("Unknown option"));
195     }
196     option->startSet();
197     _impl->_currentOption = option;
198     _impl->_currentValueCount = 0;
199 }
200
201 void OptionsAssigner::appendValue(const std::string &value)
202 {
203     AbstractOptionStorage *option = _impl->_currentOption;
204     GMX_RELEASE_ASSERT(option != NULL, "startOption() not called");
205     // Does not count correctly, but the actual count is not really used.
206     // TODO: Rename the variable to better reflect the usage.
207     ++_impl->_currentValueCount;
208     option->appendValue(value);
209 }
210
211 void OptionsAssigner::finishOption()
212 {
213     AbstractOptionStorage *option = _impl->_currentOption;
214     GMX_RELEASE_ASSERT(option != NULL, "startOption() not called");
215     bool bBoolReverseValue = false;
216     if (option->isBoolean())
217     {
218         if (_impl->_currentValueCount == 0)
219         {
220             // Should not throw, otherwise something is wrong.
221             // TODO: Get rid of the hard-coded values.
222             option->appendValue(_impl->_reverseBoolean ? "0" : "1");
223         }
224         else if (_impl->_reverseBoolean)
225         {
226             bBoolReverseValue = true;
227         }
228     }
229     _impl->_currentOption = NULL;
230     _impl->_reverseBoolean = false;
231     option->finishSet();
232     if (bBoolReverseValue)
233     {
234         GMX_THROW(InvalidInputError("Cannot specify a value together with 'no' prefix"));
235     }
236 }
237
238 void OptionsAssigner::finishSubSection()
239 {
240     // Should only be called if we are in a subsection.
241     GMX_RELEASE_ASSERT(_impl->inSubSection(), "startSubSection() not called");
242     _impl->_sectionStack.pop_back();
243 }
244
245 void OptionsAssigner::finish()
246 {
247     GMX_RELEASE_ASSERT(_impl->_currentOption == NULL, "finishOption() not called");
248     if (_impl->hasFlag(Impl::efNoStrictSectioning))
249     {
250         while (_impl->inSubSection())
251         {
252             finishSubSection();
253         }
254     }
255     GMX_RELEASE_ASSERT(!_impl->inSubSection(), "finishSubSection() not called");
256 }
257
258 } // namespace gmx