Merge "Allow 'atomname' and 'atomtype' in selections."
[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/stringutil.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     for (i = requests_.begin(); i != requests_.end(); ++i)
181     {
182         const SelectionRequest &request = *i;
183         if (request.count() > 0)
184         {
185             int remaining = selections.end() - first;
186             if (remaining < request.count())
187             {
188                 int assigned = first - selections.begin();
189                 GMX_THROW(InvalidInputError(formatString(
190                                 "Too few selections provided for '%s': "
191                                 "Expected %d selections, but only %d left "
192                                 "after assigning the first %d to other selections.",
193                                 request.name().c_str(), request.count(),
194                                 remaining, assigned)));
195             }
196             last = first + request.count();
197         }
198         else
199         {
200             RequestList::const_iterator nextRequest = i;
201             ++nextRequest;
202             if (nextRequest != requests_.end())
203             {
204                 const char *name = request.name().c_str();
205                 const char *conflictName = nextRequest->name().c_str();
206                 GMX_THROW(InvalidInputError(formatString(
207                                 "Ambiguous selections for '%s' and '%s': "
208                                 "Any number of selections is acceptable for "
209                                 "'%s', but you have requested subsequent "
210                                 "selections to be assigned to '%s'. "
211                                 "Resolution for such cases is not implemented, "
212                                 "and may be impossible.",
213                                 name, conflictName, name, conflictName)));
214             }
215             last = selections.end();
216         }
217         SelectionList curr(first, last);
218         request.storage_->addSelections(curr, true);
219         first = last;
220     }
221     if (last != selections.end())
222     {
223         int count = selections.end() - selections.begin();
224         int remaining = selections.end() - last;
225         int assigned = last - selections.begin();
226         GMX_THROW(InvalidInputError(formatString(
227                         "Too many selections provided: "
228                         "Expected %d selections, but %d provided. "
229                         "Last %d selections could not be assigned to any option.",
230                         assigned, count, remaining)));
231     }
232 }
233
234 void SelectionOptionManager::Impl::requestUnsetRequiredOptions()
235 {
236     OptionList::const_iterator i;
237     for (i = options_.begin(); i != options_.end(); ++i)
238     {
239         SelectionOptionStorage &storage = **i;
240         if (storage.isRequired() && !storage.isSet())
241         {
242             requests_.push_back(SelectionRequest(&storage));
243         }
244     }
245 }
246
247
248 /********************************************************************
249  * SelectionOptionManager
250  */
251
252 SelectionOptionManager::SelectionOptionManager(SelectionCollection *collection)
253     : impl_(new Impl(collection))
254 {
255 }
256
257 SelectionOptionManager::~SelectionOptionManager()
258 {
259 }
260
261 void
262 SelectionOptionManager::registerOption(SelectionOptionStorage *storage)
263 {
264     impl_->requests_.reserve(impl_->options_.size() + 1);
265     impl_->options_.push_back(storage);
266 }
267
268 void
269 SelectionOptionManager::convertOptionValue(SelectionOptionStorage *storage,
270                                            const std::string &value)
271 {
272     SelectionList selections = impl_->collection_.parseFromString(value);
273     storage->addSelections(selections, false);
274 }
275
276 void
277 SelectionOptionManager::requestOptionDelayedParsing(
278         SelectionOptionStorage *storage)
279 {
280     impl_->requests_.push_back(Impl::SelectionRequest(storage));
281 }
282
283 void
284 SelectionOptionManager::parseRequestedFromStdin(bool bInteractive)
285 {
286     Impl::RequestsClearer clearRequestsOnExit(&impl_->requests_);
287
288     Impl::RequestList::const_iterator i;
289     for (i = impl_->requests_.begin(); i != impl_->requests_.end(); ++i)
290     {
291         const Impl::SelectionRequest &request = *i;
292         if (bInteractive)
293         {
294             std::fprintf(stderr, "\nSpecify ");
295             if (request.count() < 0)
296             {
297                 std::fprintf(stderr, "any number of selections");
298             }
299             else if (request.count() == 1)
300             {
301                 std::fprintf(stderr, "a selection");
302             }
303             else
304             {
305                 std::fprintf(stderr, "%d selections", request.count());
306             }
307             std::fprintf(stderr, " for option '%s' (%s):\n",
308                          request.name().c_str(), request.description().c_str());
309             std::fprintf(stderr, "(one selection per line, 'help' for help%s)\n",
310                          request.count() < 0 ? ", Ctrl-D to end" : "");
311         }
312         SelectionList selections
313             = impl_->collection_.parseFromStdin(request.count(), bInteractive);
314         request.storage_->addSelections(selections, true);
315     }
316 }
317
318 void
319 SelectionOptionManager::parseRequestedFromFile(const std::string &filename)
320 {
321     SelectionList selections = impl_->collection_.parseFromFile(filename);
322     try
323     {
324         impl_->placeSelectionsInRequests(selections);
325     }
326     catch (GromacsException &ex)
327     {
328         ex.prependContext(formatString(
329                     "Error in adding selections from file '%s'",
330                     filename.c_str()));
331         throw;
332     }
333 }
334
335 void
336 SelectionOptionManager::parseRequestedFromString(const std::string &str)
337 {
338     SelectionList selections = impl_->collection_.parseFromString(str);
339     impl_->placeSelectionsInRequests(selections);
340 }
341
342 } // namespace gmx