Merge release-4-6 into master
[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 "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                 root = root->_impl->_parent;
122                 ++upcount;
123                 searchList.push_back(root);
124             }
125         }
126     }
127     if (hasFlag(efNoStrictSectioning) && option != NULL)
128     {
129         while (upcount > 0)
130         {
131             _sectionStack.pop_back();
132             --upcount;
133         }
134         std::vector<Options *> sections;
135         while (section != &currentSection())
136         {
137             sections.push_back(section);
138             section = section->_impl->_parent;
139         }
140         while (!sections.empty())
141         {
142             _sectionStack.push_back(sections.back());
143             sections.pop_back();
144         }
145     }
146     return option;
147 }
148
149 /********************************************************************
150  * OptionsAssigner
151  */
152
153 OptionsAssigner::OptionsAssigner(Options *options)
154     : _impl(new Impl(options))
155 {
156 }
157
158 OptionsAssigner::~OptionsAssigner()
159 {
160 }
161
162 void OptionsAssigner::setAcceptBooleanNoPrefix(bool enabled)
163 {
164     _impl->setFlag(Impl::efAcceptBooleanNoPrefix, enabled);
165 }
166
167 void OptionsAssigner::setNoStrictSectioning(bool enabled)
168 {
169     _impl->setFlag(Impl::efNoStrictSectioning, enabled);
170 }
171
172 void OptionsAssigner::start()
173 {
174     _impl->_options._impl->startSource();
175 }
176
177 void OptionsAssigner::startSubSection(const char *name)
178 {
179     Options *section = _impl->currentSection()._impl->findSubSection(name);
180     if (section == NULL)
181     {
182         GMX_THROW(InvalidInputError("Unknown subsection"));
183     }
184     _impl->_sectionStack.push_back(section);
185 }
186
187 void OptionsAssigner::startOption(const char *name)
188 {
189     GMX_RELEASE_ASSERT(_impl->_currentOption == NULL, "finishOption() not called");
190     AbstractOptionStorage *option = _impl->findOption(name);
191     if (option == NULL)
192     {
193         GMX_THROW(InvalidInputError("Unknown option"));
194     }
195     option->startSet();
196     _impl->_currentOption = option;
197     _impl->_currentValueCount = 0;
198 }
199
200 void OptionsAssigner::appendValue(const std::string &value)
201 {
202     AbstractOptionStorage *option = _impl->_currentOption;
203     GMX_RELEASE_ASSERT(option != NULL, "startOption() not called");
204     // Does not count correctly, but the actual count is not really used.
205     // TODO: Rename the variable to better reflect the usage.
206     ++_impl->_currentValueCount;
207     option->appendValue(value);
208 }
209
210 void OptionsAssigner::finishOption()
211 {
212     AbstractOptionStorage *option = _impl->_currentOption;
213     GMX_RELEASE_ASSERT(option != NULL, "startOption() not called");
214     bool bBoolReverseValue = false;
215     if (option->isBoolean())
216     {
217         if (_impl->_currentValueCount == 0)
218         {
219             // Should not throw, otherwise something is wrong.
220             // TODO: Get rid of the hard-coded values.
221             option->appendValue(_impl->_reverseBoolean ? "0" : "1");
222         }
223         else if (_impl->_reverseBoolean)
224         {
225             bBoolReverseValue = true;
226         }
227     }
228     _impl->_currentOption = NULL;
229     _impl->_reverseBoolean = false;
230     option->finishSet();
231     if (bBoolReverseValue)
232     {
233         GMX_THROW(InvalidInputError("Cannot specify a value together with 'no' prefix"));
234     }
235 }
236
237 void OptionsAssigner::finishSubSection()
238 {
239     // Should only be called if we are in a subsection.
240     GMX_RELEASE_ASSERT(_impl->inSubSection(), "startSubSection() not called");
241     _impl->_sectionStack.pop_back();
242 }
243
244 void OptionsAssigner::finish()
245 {
246     GMX_RELEASE_ASSERT(_impl->_currentOption == NULL, "finishOption() not called");
247     if (_impl->hasFlag(Impl::efNoStrictSectioning))
248     {
249         while (_impl->inSubSection())
250         {
251             finishSubSection();
252         }
253     }
254     GMX_RELEASE_ASSERT(!_impl->inSubSection(), "finishSubSection() not called");
255 }
256
257 } // namespace gmx