Merge "Split option code away from SelectionCollection."
[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
102         /*! \brief
103          * Helper class that clears a request list on scope exit.
104          *
105          * Methods in this class do not throw.
106          */
107         class RequestsClearer
108         {
109             public:
110                 //! Constructs an object that clears given list on scope exit.
111                 explicit RequestsClearer(RequestList *requests)
112                     : requests_(requests)
113                 {
114                 }
115                 //! Clears the request list given to the constructor.
116                 ~RequestsClearer()
117                 {
118                     requests_->clear();
119                 }
120
121             private:
122                 RequestList    *requests_;
123         };
124
125         /*! \brief
126          * Creates a new selection collection.
127          *
128          * \throws  std::bad_alloc if out of memory.
129          */
130         explicit Impl(SelectionCollection *collection);
131
132         /*! \brief
133          * Assign selections from a list to pending requests.
134          *
135          * \param[in] selections  List of selections to assign.
136          * \throws    std::bad_alloc if out of memory.
137          * \throws    InvalidInputError if the assignment cannot be done
138          *      (see parseRequestedFromFile() for documented conditions).
139          *
140          * Loops through \p selections and the pending requests lists in order,
141          * and for each requests, assigns the first yet unassigned selections
142          * from the list.
143          */
144         void placeSelectionsInRequests(const SelectionList &selections);
145
146         //! Selection collection to which selections are stored.
147         SelectionCollection    &collection_;
148         //! List of selections requested for later parsing.
149         RequestList             requests_;
150 };
151
152 SelectionOptionManager::Impl::Impl(SelectionCollection *collection)
153     : collection_(*collection)
154 {
155 }
156
157 void SelectionOptionManager::Impl::placeSelectionsInRequests(
158         const SelectionList &selections)
159 {
160     RequestsClearer clearRequestsOnExit(&requests_);
161
162     SelectionList::const_iterator first = selections.begin();
163     SelectionList::const_iterator last = first;
164     RequestList::const_iterator i;
165     // TODO: Improve error messages.
166     for (i = requests_.begin(); i != requests_.end(); ++i)
167     {
168         const SelectionRequest &request = *i;
169         if (request.count() > 0)
170         {
171             if (selections.end() - first < request.count())
172             {
173                 GMX_THROW(InvalidInputError("Too few selections provided"));
174             }
175             last = first + request.count();
176         }
177         else
178         {
179             if (i != requests_.end() - 1)
180             {
181                 GMX_THROW(InvalidInputError(
182                             formatString("Request for selection '%s' must "
183                                          "not be followed by others",
184                                          request.name().c_str())));
185             }
186             last = selections.end();
187         }
188         SelectionList curr(first, last);
189         request.storage_->addSelections(curr, true);
190         first = last;
191     }
192     if (last != selections.end())
193     {
194         GMX_THROW(InvalidInputError("Too many selections provided"));
195     }
196 }
197
198
199 /********************************************************************
200  * SelectionOptionManager
201  */
202
203 SelectionOptionManager::SelectionOptionManager(SelectionCollection *collection)
204     : impl_(new Impl(collection))
205 {
206 }
207
208 SelectionOptionManager::~SelectionOptionManager()
209 {
210 }
211
212 SelectionCollection &
213 SelectionOptionManager::selectionCollection()
214 {
215     return impl_->collection_;
216 }
217
218 void
219 SelectionOptionManager::requestDelayedParsing(SelectionOptionStorage *storage)
220 {
221     impl_->requests_.push_back(Impl::SelectionRequest(storage));
222 }
223
224 void
225 SelectionOptionManager::parseRequestedFromStdin(bool bInteractive)
226 {
227     Impl::RequestsClearer clearRequestsOnExit(&impl_->requests_);
228
229     Impl::RequestList::const_iterator i;
230     for (i = impl_->requests_.begin(); i != impl_->requests_.end(); ++i)
231     {
232         const Impl::SelectionRequest &request = *i;
233         if (bInteractive)
234         {
235             std::fprintf(stderr, "\nSpecify ");
236             if (request.count() < 0)
237             {
238                 std::fprintf(stderr, "any number of selections");
239             }
240             else if (request.count() == 1)
241             {
242                 std::fprintf(stderr, "a selection");
243             }
244             else
245             {
246                 std::fprintf(stderr, "%d selections", request.count());
247             }
248             std::fprintf(stderr, " for option '%s' (%s):\n",
249                          request.name().c_str(), request.description().c_str());
250             std::fprintf(stderr, "(one selection per line, 'help' for help%s)\n",
251                          request.count() < 0 ? ", Ctrl-D to end" : "");
252         }
253         SelectionList selections;
254         impl_->collection_.parseFromStdin(request.count(), bInteractive, &selections);
255         request.storage_->addSelections(selections, true);
256     }
257 }
258
259 void
260 SelectionOptionManager::parseRequestedFromFile(const std::string &filename)
261 {
262     SelectionList selections;
263     impl_->collection_.parseFromFile(filename, &selections);
264     impl_->placeSelectionsInRequests(selections);
265 }
266
267 void
268 SelectionOptionManager::parseRequestedFromString(const std::string &str)
269 {
270     SelectionList selections;
271     impl_->collection_.parseFromString(str, &selections);
272     impl_->placeSelectionsInRequests(selections);
273 }
274
275 } // namespace gmx