Merge "Option parsing through SelectionOptionManager."
[alexxy/gromacs.git] / src / gromacs / selection / selectionoptionmanager.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::SelectionOptionManager.
34  *
35  * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36  * \ingroup module_selection
37  */
38 #include "selectionoptionmanager.h"
39
40 #include <cstdio>
41
42 #include "gromacs/selection/selection.h"
43 #include "gromacs/selection/selectioncollection.h"
44 #include "gromacs/utility/exceptions.h"
45 #include "gromacs/utility/format.h"
46
47 #include "selectionoptionstorage.h"
48
49 namespace gmx
50 {
51
52 /********************************************************************
53  * SelectionOptionManager::Impl
54  */
55
56 /*! \internal \brief
57  * Private implemention class for SelectionOptionManager.
58  *
59  * \ingroup module_selection
60  */
61 class SelectionOptionManager::Impl
62 {
63     public:
64         /*! \brief
65          * Request for postponed parsing of selections.
66          */
67         struct SelectionRequest
68         {
69             //! Initializes a request for the given option.
70             explicit SelectionRequest(SelectionOptionStorage *storage)
71                 : storage_(storage)
72             {
73             }
74
75             //! Returns name of the requested selection optin.
76             const std::string &name() const
77             {
78                 return storage_->name();
79             }
80             //! Returns description for the requested selection option.
81             const std::string &description() const
82             {
83                 return storage_->description();
84             }
85             /*! \brief
86              * Returns the number of selections requested in this request.
87              *
88              * -1 indicates no upper limit.
89              */
90             int count() const
91             {
92                 return storage_->maxValueCount();
93             }
94
95             //! Storage object to which the selections will be added.
96             SelectionOptionStorage     *storage_;
97         };
98
99         //! Collection for a list of selection requests.
100         typedef std::vector<SelectionRequest> RequestList;
101         //! Collection for list of option storage objects.
102         typedef std::vector<SelectionOptionStorage *> OptionList;
103
104         /*! \brief
105          * Helper class that clears a request list on scope exit.
106          *
107          * Methods in this class do not throw.
108          */
109         class RequestsClearer
110         {
111             public:
112                 //! Constructs an object that clears given list on scope exit.
113                 explicit RequestsClearer(RequestList *requests)
114                     : requests_(requests)
115                 {
116                 }
117                 //! Clears the request list given to the constructor.
118                 ~RequestsClearer()
119                 {
120                     requests_->clear();
121                 }
122
123             private:
124                 RequestList    *requests_;
125         };
126
127         /*! \brief
128          * Creates a new selection collection.
129          *
130          * \throws  std::bad_alloc if out of memory.
131          */
132         explicit Impl(SelectionCollection *collection);
133
134         /*! \brief
135          * Assign selections from a list to pending requests.
136          *
137          * \param[in] selections  List of selections to assign.
138          * \throws    std::bad_alloc if out of memory.
139          * \throws    InvalidInputError if the assignment cannot be done
140          *      (see parseRequestedFromFile() for documented conditions).
141          *
142          * Loops through \p selections and the pending requests lists in order,
143          * and for each requests, assigns the first yet unassigned selections
144          * from the list.
145          */
146         void placeSelectionsInRequests(const SelectionList &selections);
147         /*! \brief
148          * Adds a request for each required option that is not yet set.
149          *
150          * \throws    std::bad_alloc if out of memory.
151          */
152         void requestUnsetRequiredOptions();
153
154         //! Selection collection to which selections are stored.
155         SelectionCollection    &collection_;
156         //! List of selection options (storage objects) this manager manages.
157         OptionList              options_;
158         //! List of selections requested for later parsing.
159         RequestList             requests_;
160 };
161
162 SelectionOptionManager::Impl::Impl(SelectionCollection *collection)
163     : collection_(*collection)
164 {
165 }
166
167 void SelectionOptionManager::Impl::placeSelectionsInRequests(
168         const SelectionList &selections)
169 {
170     if (requests_.empty())
171     {
172         requestUnsetRequiredOptions();
173     }
174
175     RequestsClearer clearRequestsOnExit(&requests_);
176
177     SelectionList::const_iterator first = selections.begin();
178     SelectionList::const_iterator last = first;
179     RequestList::const_iterator i;
180     // TODO: Improve error messages.
181     for (i = requests_.begin(); i != requests_.end(); ++i)
182     {
183         const SelectionRequest &request = *i;
184         if (request.count() > 0)
185         {
186             if (selections.end() - first < request.count())
187             {
188                 GMX_THROW(InvalidInputError("Too few selections provided"));
189             }
190             last = first + request.count();
191         }
192         else
193         {
194             if (i != requests_.end() - 1)
195             {
196                 GMX_THROW(InvalidInputError(
197                             formatString("Request for selection '%s' must "
198                                          "not be followed by others",
199                                          request.name().c_str())));
200             }
201             last = selections.end();
202         }
203         SelectionList curr(first, last);
204         request.storage_->addSelections(curr, true);
205         first = last;
206     }
207     if (last != selections.end())
208     {
209         GMX_THROW(InvalidInputError("Too many selections provided"));
210     }
211 }
212
213 void SelectionOptionManager::Impl::requestUnsetRequiredOptions()
214 {
215     OptionList::const_iterator i;
216     for (i = options_.begin(); i != options_.end(); ++i)
217     {
218         SelectionOptionStorage &storage = **i;
219         if (storage.isRequired() && !storage.isSet())
220         {
221             requests_.push_back(SelectionRequest(&storage));
222         }
223     }
224 }
225
226
227 /********************************************************************
228  * SelectionOptionManager
229  */
230
231 SelectionOptionManager::SelectionOptionManager(SelectionCollection *collection)
232     : impl_(new Impl(collection))
233 {
234 }
235
236 SelectionOptionManager::~SelectionOptionManager()
237 {
238 }
239
240 void
241 SelectionOptionManager::registerOption(SelectionOptionStorage *storage)
242 {
243     impl_->requests_.reserve(impl_->options_.size() + 1);
244     impl_->options_.push_back(storage);
245 }
246
247 void
248 SelectionOptionManager::convertOptionValue(SelectionOptionStorage *storage,
249                                            const std::string &value)
250 {
251     SelectionList selections;
252     impl_->collection_.parseFromString(value, &selections);
253     storage->addSelections(selections, false);
254 }
255
256 void
257 SelectionOptionManager::requestOptionDelayedParsing(
258         SelectionOptionStorage *storage)
259 {
260     impl_->requests_.push_back(Impl::SelectionRequest(storage));
261 }
262
263 void
264 SelectionOptionManager::parseRequestedFromStdin(bool bInteractive)
265 {
266     Impl::RequestsClearer clearRequestsOnExit(&impl_->requests_);
267
268     Impl::RequestList::const_iterator i;
269     for (i = impl_->requests_.begin(); i != impl_->requests_.end(); ++i)
270     {
271         const Impl::SelectionRequest &request = *i;
272         if (bInteractive)
273         {
274             std::fprintf(stderr, "\nSpecify ");
275             if (request.count() < 0)
276             {
277                 std::fprintf(stderr, "any number of selections");
278             }
279             else if (request.count() == 1)
280             {
281                 std::fprintf(stderr, "a selection");
282             }
283             else
284             {
285                 std::fprintf(stderr, "%d selections", request.count());
286             }
287             std::fprintf(stderr, " for option '%s' (%s):\n",
288                          request.name().c_str(), request.description().c_str());
289             std::fprintf(stderr, "(one selection per line, 'help' for help%s)\n",
290                          request.count() < 0 ? ", Ctrl-D to end" : "");
291         }
292         SelectionList selections;
293         impl_->collection_.parseFromStdin(request.count(), bInteractive, &selections);
294         request.storage_->addSelections(selections, true);
295     }
296 }
297
298 void
299 SelectionOptionManager::parseRequestedFromFile(const std::string &filename)
300 {
301     SelectionList selections;
302     impl_->collection_.parseFromFile(filename, &selections);
303     impl_->placeSelectionsInRequests(selections);
304 }
305
306 void
307 SelectionOptionManager::parseRequestedFromString(const std::string &str)
308 {
309     SelectionList selections;
310     impl_->collection_.parseFromString(str, &selections);
311     impl_->placeSelectionsInRequests(selections);
312 }
313
314 } // namespace gmx