3 * This source code is part of
7 * GROningen MAchine for Chemical Simulations
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.
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.
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.
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.
29 * For more info, check our website at http://www.gromacs.org
33 * Implements gmx::SelectionOptionManager.
35 * \author Teemu Murtola <teemu.murtola@cbr.su.se>
36 * \ingroup module_selection
38 #include "selectionoptionmanager.h"
42 #include "gromacs/options/optionsvisitor.h"
43 #include "gromacs/selection/selection.h"
44 #include "gromacs/selection/selectioncollection.h"
45 #include "gromacs/selection/selectionoption.h"
46 #include "gromacs/selection/selectionfileoption.h"
47 #include "gromacs/utility/exceptions.h"
48 #include "gromacs/utility/stringutil.h"
50 #include "selectionoptionstorage.h"
55 /********************************************************************
56 * SelectionOptionManager::Impl
60 * Private implemention class for SelectionOptionManager.
62 * \ingroup module_selection
64 class SelectionOptionManager::Impl
68 * Request for postponed parsing of selections.
70 struct SelectionRequest
72 //! Initializes a request for the given option.
73 explicit SelectionRequest(SelectionOptionStorage *storage)
78 //! Returns name of the requested selection optin.
79 const std::string &name() const
81 return storage_->name();
83 //! Returns description for the requested selection option.
84 const std::string &description() const
86 return storage_->description();
89 * Returns the number of selections requested in this request.
91 * -1 indicates no upper limit.
95 return storage_->maxValueCount();
98 //! Storage object to which the selections will be added.
99 SelectionOptionStorage *storage_;
102 //! Collection for a list of selection requests.
103 typedef std::vector<SelectionRequest> RequestList;
104 //! Collection for list of option storage objects.
105 typedef std::vector<SelectionOptionStorage *> OptionList;
108 * Helper class that clears a request list on scope exit.
110 * Methods in this class do not throw.
112 class RequestsClearer
115 //! Constructs an object that clears given list on scope exit.
116 explicit RequestsClearer(RequestList *requests)
117 : requests_(requests)
120 //! Clears the request list given to the constructor.
127 RequestList *requests_;
131 * Creates a new selection collection.
133 * \throws std::bad_alloc if out of memory.
135 explicit Impl(SelectionCollection *collection);
138 * Assign selections from a list to pending requests.
140 * \param[in] selections List of selections to assign.
141 * \throws std::bad_alloc if out of memory.
142 * \throws InvalidInputError if the assignment cannot be done
143 * (see parseRequestedFromFile() for documented conditions).
145 * Loops through \p selections and the pending requests lists in order,
146 * and for each requests, assigns the first yet unassigned selections
149 void placeSelectionsInRequests(const SelectionList &selections);
151 * Adds a request for each required option that is not yet set.
153 * \throws std::bad_alloc if out of memory.
155 void requestUnsetRequiredOptions();
157 //! Selection collection to which selections are stored.
158 SelectionCollection &collection_;
159 //! List of selection options (storage objects) this manager manages.
161 //! List of selections requested for later parsing.
162 RequestList requests_;
165 SelectionOptionManager::Impl::Impl(SelectionCollection *collection)
166 : collection_(*collection)
170 void SelectionOptionManager::Impl::placeSelectionsInRequests(
171 const SelectionList &selections)
173 if (requests_.empty())
175 requestUnsetRequiredOptions();
178 RequestsClearer clearRequestsOnExit(&requests_);
180 SelectionList::const_iterator first = selections.begin();
181 SelectionList::const_iterator last = first;
182 RequestList::const_iterator i;
183 for (i = requests_.begin(); i != requests_.end(); ++i)
185 const SelectionRequest &request = *i;
186 if (request.count() > 0)
188 int remaining = selections.end() - first;
189 if (remaining < request.count())
191 int assigned = first - selections.begin();
192 GMX_THROW(InvalidInputError(formatString(
193 "Too few selections provided for '%s': "
194 "Expected %d selections, but only %d left "
195 "after assigning the first %d to other selections.",
196 request.name().c_str(), request.count(),
197 remaining, assigned)));
199 last = first + request.count();
203 RequestList::const_iterator nextRequest = i;
205 if (nextRequest != requests_.end())
207 const char *name = request.name().c_str();
208 const char *conflictName = nextRequest->name().c_str();
209 GMX_THROW(InvalidInputError(formatString(
210 "Ambiguous selections for '%s' and '%s': "
211 "Any number of selections is acceptable for "
212 "'%s', but you have requested subsequent "
213 "selections to be assigned to '%s'. "
214 "Resolution for such cases is not implemented, "
215 "and may be impossible.",
216 name, conflictName, name, conflictName)));
218 last = selections.end();
220 SelectionList curr(first, last);
221 request.storage_->addSelections(curr, true);
224 if (last != selections.end())
226 int count = selections.end() - selections.begin();
227 int remaining = selections.end() - last;
228 int assigned = last - selections.begin();
229 GMX_THROW(InvalidInputError(formatString(
230 "Too many selections provided: "
231 "Expected %d selections, but %d provided. "
232 "Last %d selections could not be assigned to any option.",
233 assigned, count, remaining)));
237 void SelectionOptionManager::Impl::requestUnsetRequiredOptions()
239 OptionList::const_iterator i;
240 for (i = options_.begin(); i != options_.end(); ++i)
242 SelectionOptionStorage &storage = **i;
243 if (storage.isRequired() && !storage.isSet())
245 requests_.push_back(SelectionRequest(&storage));
251 /********************************************************************
252 * SelectionOptionManager
255 SelectionOptionManager::SelectionOptionManager(SelectionCollection *collection)
256 : impl_(new Impl(collection))
260 SelectionOptionManager::~SelectionOptionManager()
265 SelectionOptionManager::registerOption(SelectionOptionStorage *storage)
267 impl_->requests_.reserve(impl_->options_.size() + 1);
268 impl_->options_.push_back(storage);
272 SelectionOptionManager::convertOptionValue(SelectionOptionStorage *storage,
273 const std::string &value)
275 SelectionList selections = impl_->collection_.parseFromString(value);
276 storage->addSelections(selections, false);
280 SelectionOptionManager::requestOptionDelayedParsing(
281 SelectionOptionStorage *storage)
283 impl_->requests_.push_back(Impl::SelectionRequest(storage));
287 SelectionOptionManager::parseRequestedFromStdin(bool bInteractive)
289 Impl::RequestsClearer clearRequestsOnExit(&impl_->requests_);
291 Impl::RequestList::const_iterator i;
292 for (i = impl_->requests_.begin(); i != impl_->requests_.end(); ++i)
294 const Impl::SelectionRequest &request = *i;
297 std::fprintf(stderr, "\nSpecify ");
298 if (request.count() < 0)
300 std::fprintf(stderr, "any number of selections");
302 else if (request.count() == 1)
304 std::fprintf(stderr, "a selection");
308 std::fprintf(stderr, "%d selections", request.count());
310 std::fprintf(stderr, " for option '%s' (%s):\n",
311 request.name().c_str(), request.description().c_str());
312 std::fprintf(stderr, "(one selection per line, 'help' for help%s)\n",
313 request.count() < 0 ? ", Ctrl-D to end" : "");
315 SelectionList selections
316 = impl_->collection_.parseFromStdin(request.count(), bInteractive);
317 request.storage_->addSelections(selections, true);
322 SelectionOptionManager::parseRequestedFromFile(const std::string &filename)
324 SelectionList selections = impl_->collection_.parseFromFile(filename);
327 impl_->placeSelectionsInRequests(selections);
329 catch (GromacsException &ex)
331 ex.prependContext(formatString(
332 "Error in adding selections from file '%s'",
339 SelectionOptionManager::parseRequestedFromString(const std::string &str)
341 SelectionList selections = impl_->collection_.parseFromString(str);
342 impl_->placeSelectionsInRequests(selections);
345 /********************************************************************
353 * Visitor that sets the manager for each selection option.
355 * \ingroup module_selection
357 class SelectionOptionManagerSetter : public OptionsModifyingVisitor
360 //! Construct a visitor that sets given manager.
361 explicit SelectionOptionManagerSetter(SelectionOptionManager *manager)
366 void visitSubSection(Options *section)
368 OptionsModifyingIterator iterator(section);
369 iterator.acceptSubSections(this);
370 iterator.acceptOptions(this);
373 void visitOption(OptionInfo *option)
375 SelectionOptionInfo *selOption
376 = option->toType<SelectionOptionInfo>();
377 if (selOption != NULL)
379 selOption->setManager(manager_);
381 SelectionFileOptionInfo *selFileOption
382 = option->toType<SelectionFileOptionInfo>();
383 if (selFileOption != NULL)
385 selFileOption->setManager(manager_);
390 SelectionOptionManager *manager_;
395 void setManagerForSelectionOptions(Options *options,
396 SelectionOptionManager *manager)
398 SelectionOptionManagerSetter(manager).visitSubSection(options);