Merge 'release-4-6' into master
[alexxy/gromacs.git] / src / gromacs / options / options.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::Options.
34  *
35  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36  * \ingroup module_options
37  */
38 #include "gromacs/options/options.h"
39
40 #include "gromacs/options/abstractoption.h"
41 #include "gromacs/options/abstractoptionstorage.h"
42 #include "gromacs/utility/exceptions.h"
43 #include "gromacs/utility/gmxassert.h"
44 #include "gromacs/utility/messagestringcollector.h"
45 #include "gromacs/utility/stringutil.h"
46
47 #include "options-impl.h"
48
49 namespace gmx
50 {
51
52 /********************************************************************
53  * Options::Impl
54  */
55
56 Options::Impl::Impl(const char *name, const char *title)
57     : name_(name != NULL ? name : ""), title_(title != NULL ? title : ""),
58       parent_(NULL)
59 {
60 }
61
62 Options::Impl::~Impl()
63 {
64 }
65
66 Options *Options::Impl::findSubSection(const char *name) const
67 {
68     SubSectionList::const_iterator i;
69     for (i = subSections_.begin(); i != subSections_.end(); ++i)
70     {
71         if ((*i)->name() == name)
72         {
73             return *i;
74         }
75     }
76     return NULL;
77 }
78
79 AbstractOptionStorage *Options::Impl::findOption(const char *name) const
80 {
81     OptionList::const_iterator i;
82     for (i = options_.begin(); i != options_.end(); ++i)
83     {
84         if ((*i)->name() == name)
85         {
86             return i->get();
87         }
88     }
89     return NULL;
90 }
91
92 void Options::Impl::startSource()
93 {
94     OptionList::const_iterator i;
95     for (i = options_.begin(); i != options_.end(); ++i)
96     {
97         AbstractOptionStorage &option = **i;
98         option.startSource();
99     }
100     SubSectionList::const_iterator j;
101     for (j = subSections_.begin(); j != subSections_.end(); ++j)
102     {
103         Options &section = **j;
104         section.impl_->startSource();
105     }
106 }
107
108 /********************************************************************
109  * Options
110  */
111
112 Options::Options(const char *name, const char *title)
113     : impl_(new Impl(name, title))
114 {
115 }
116
117 Options::~Options()
118 {
119 }
120
121 const std::string &Options::name() const
122 {
123     return impl_->name_;
124 }
125
126 const std::string &Options::title() const
127 {
128     return impl_->title_;
129 }
130
131 const std::string &Options::description() const
132 {
133     return impl_->description_;
134 }
135
136 void Options::setDescription(const std::string &desc)
137 {
138     impl_->description_ = desc;
139 }
140
141 void Options::addSubSection(Options *section)
142 {
143     // Make sure that section is not already inserted somewhere.
144     GMX_RELEASE_ASSERT(section->impl_->parent_ == NULL,
145                        "Cannot add as subsection twice");
146     // Make sure that there are no duplicate sections.
147     GMX_RELEASE_ASSERT(impl_->findSubSection(section->name().c_str()) == NULL,
148                        "Duplicate subsection name");
149     impl_->subSections_.push_back(section);
150     section->impl_->parent_ = this;
151 }
152
153 OptionInfo *Options::addOption(const AbstractOption &settings)
154 {
155     AbstractOptionStoragePointer option(settings.createStorage());
156     if (impl_->findOption(option->name().c_str()) != NULL)
157     {
158         GMX_THROW(APIError("Duplicate option: " + option->name()));
159     }
160     impl_->options_.push_back(move(option));
161     return &impl_->options_.back()->optionInfo();
162 }
163
164 bool Options::isSet(const char *name) const
165 {
166     AbstractOptionStorage *option = impl_->findOption(name);
167     return (option != NULL ? option->isSet() : false);
168 }
169
170 void Options::finish()
171 {
172     // TODO: Consider how to customize these error messages based on context.
173     ExceptionInitializer errors("Invalid input values");
174     Impl::OptionList::const_iterator i;
175     for (i = impl_->options_.begin(); i != impl_->options_.end(); ++i)
176     {
177         AbstractOptionStorage &option = **i;
178         try
179         {
180             option.finish();
181         }
182         catch (UserInputError &ex)
183         {
184             ex.prependContext("In option " + option.name());
185             errors.addCurrentExceptionAsNested();
186         }
187     }
188     Impl::SubSectionList::const_iterator j;
189     for (j = impl_->subSections_.begin(); j != impl_->subSections_.end(); ++j)
190     {
191         Options &section = **j;
192         try
193         {
194             section.finish();
195         }
196         catch (const UserInputError &)
197         {
198             errors.addCurrentExceptionAsNested();
199         }
200     }
201     if (errors.hasNestedExceptions())
202     {
203         // TODO: This exception type may not always be appropriate.
204         GMX_THROW(InvalidInputError(errors));
205     }
206 }
207
208 } // namespace gmx