Split lines with many copyright years
[gromacs.git] / src / gromacs / selection / selectionoptionmanager.cpp
blobbc755086c4e51833d0330ff26694eef9089e31e4
1 /*
2 * This file is part of the GROMACS molecular simulation package.
4 * Copyright (c) 2012,2013,2014,2015,2016 by the GROMACS development team.
5 * Copyright (c) 2019,2020, by the GROMACS development team, led by
6 * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
7 * and including many others, as listed in the AUTHORS file in the
8 * top-level source directory and at http://www.gromacs.org.
10 * GROMACS is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU Lesser General Public License
12 * as published by the Free Software Foundation; either version 2.1
13 * of the License, or (at your option) any later version.
15 * GROMACS is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public
21 * License along with GROMACS; if not, see
22 * http://www.gnu.org/licenses, or write to the Free Software Foundation,
23 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
25 * If you want to redistribute modifications to GROMACS, please
26 * consider that scientific software is very special. Version
27 * control is crucial - bugs must be traceable. We will be happy to
28 * consider code for inclusion in the official distribution, but
29 * derived work must not be called official GROMACS. Details are found
30 * in the README & COPYING files - if they are missing, get the
31 * official version at http://www.gromacs.org.
33 * To help us fund GROMACS development, we humbly ask that you cite
34 * the research papers on the package. Check out http://www.gromacs.org.
36 /*! \internal \file
37 * \brief
38 * Implements gmx::SelectionOptionManager.
40 * \author Teemu Murtola <teemu.murtola@gmail.com>
41 * \ingroup module_selection
43 #include "gmxpre.h"
45 #include "selectionoptionmanager.h"
47 #include <cstdio>
49 #include "gromacs/selection/selection.h"
50 #include "gromacs/selection/selectioncollection.h"
51 #include "gromacs/selection/selectionoption.h"
52 #include "gromacs/utility/exceptions.h"
53 #include "gromacs/utility/stringutil.h"
55 #include "selectionoptionstorage.h"
57 namespace gmx
60 /********************************************************************
61 * SelectionOptionManager::Impl
64 /*! \internal \brief
65 * Private implemention class for SelectionOptionManager.
67 * \ingroup module_selection
69 class SelectionOptionManager::Impl
71 public:
72 /*! \brief
73 * Request for postponed parsing of selections.
75 struct SelectionRequest
77 //! Initializes a request for the given option.
78 explicit SelectionRequest(SelectionOptionStorage* storage) : storage_(storage) {}
80 //! Returns name of the requested selection optin.
81 const std::string& name() const { return storage_->name(); }
82 //! Returns description for the requested selection option.
83 const std::string& description() const { return storage_->description(); }
84 /*! \brief
85 * Returns the number of selections requested in this request.
87 * -1 indicates no upper limit.
89 int count() const { return storage_->maxValueCount(); }
91 //! Storage object to which the selections will be added.
92 SelectionOptionStorage* storage_;
95 //! Collection for a list of selection requests.
96 typedef std::vector<SelectionRequest> RequestList;
97 //! Collection for list of option storage objects.
98 typedef std::vector<SelectionOptionStorage*> OptionList;
100 /*! \brief
101 * Helper class that clears a request list on scope exit.
103 * Methods in this class do not throw.
105 class RequestsClearer
107 public:
108 //! Constructs an object that clears given list on scope exit.
109 explicit RequestsClearer(RequestList* requests) : requests_(requests) {}
110 //! Clears the request list given to the constructor.
111 ~RequestsClearer() { requests_->clear(); }
113 private:
114 RequestList* requests_;
117 /*! \brief
118 * Creates a new selection collection.
120 * \throws std::bad_alloc if out of memory.
122 explicit Impl(SelectionCollection* collection);
124 /*! \brief
125 * Assign selections from a list to pending requests.
127 * \param[in] selections List of selections to assign.
128 * \throws std::bad_alloc if out of memory.
129 * \throws InvalidInputError if the assignment cannot be done
130 * (see parseRequestedFromFile() for documented conditions).
132 * Loops through \p selections and the pending requests lists in order,
133 * and for each requests, assigns the first yet unassigned selections
134 * from the list.
136 void placeSelectionsInRequests(const SelectionList& selections);
137 /*! \brief
138 * Adds a request for each required option that is not yet set.
140 * \throws std::bad_alloc if out of memory.
142 void requestUnsetRequiredOptions();
144 //! Selection collection to which selections are stored.
145 SelectionCollection& collection_;
146 //! List of selection options (storage objects) this manager manages.
147 OptionList options_;
148 //! List of selections requested for later parsing.
149 RequestList requests_;
152 SelectionOptionManager::Impl::Impl(SelectionCollection* collection) : collection_(*collection) {}
154 void SelectionOptionManager::Impl::placeSelectionsInRequests(const SelectionList& selections)
156 if (requests_.empty())
158 requestUnsetRequiredOptions();
161 RequestsClearer clearRequestsOnExit(&requests_);
163 SelectionList::const_iterator first = selections.begin();
164 SelectionList::const_iterator last = first;
165 RequestList::const_iterator i;
166 for (i = requests_.begin(); i != requests_.end(); ++i)
168 const SelectionRequest& request = *i;
169 if (request.count() > 0)
171 int remaining = selections.end() - first;
172 if (remaining < request.count())
174 int assigned = first - selections.begin();
175 GMX_THROW(InvalidInputError(
176 formatString("Too few selections provided for '%s': "
177 "Expected %d selections, but only %d left "
178 "after assigning the first %d to other selections.",
179 request.name().c_str(), request.count(), remaining, assigned)));
181 last = first + request.count();
183 else
185 RequestList::const_iterator nextRequest = i;
186 ++nextRequest;
187 if (nextRequest != requests_.end())
189 const char* name = request.name().c_str();
190 const char* conflictName = nextRequest->name().c_str();
191 GMX_THROW(InvalidInputError(
192 formatString("Ambiguous selections for '%s' and '%s': "
193 "Any number of selections is acceptable for "
194 "'%s', but you have requested subsequent "
195 "selections to be assigned to '%s'. "
196 "Resolution for such cases is not implemented, "
197 "and may be impossible.",
198 name, conflictName, name, conflictName)));
200 last = selections.end();
202 SelectionList curr(first, last);
203 request.storage_->addSelections(curr, true);
204 first = last;
206 if (last != selections.end())
208 int count = selections.end() - selections.begin();
209 int remaining = selections.end() - last;
210 int assigned = last - selections.begin();
211 GMX_THROW(InvalidInputError(
212 formatString("Too many selections provided: "
213 "Expected %d selections, but %d provided. "
214 "Last %d selections could not be assigned to any option.",
215 assigned, count, remaining)));
219 void SelectionOptionManager::Impl::requestUnsetRequiredOptions()
221 OptionList::const_iterator i;
222 for (i = options_.begin(); i != options_.end(); ++i)
224 SelectionOptionStorage& storage = **i;
225 if (storage.isRequired() && !storage.isSet())
227 requests_.emplace_back(&storage);
233 /********************************************************************
234 * SelectionOptionManager
237 SelectionOptionManager::SelectionOptionManager(SelectionCollection* collection) :
238 impl_(new Impl(collection))
242 SelectionOptionManager::~SelectionOptionManager() {}
244 void SelectionOptionManager::registerOption(SelectionOptionStorage* storage)
246 impl_->requests_.reserve(impl_->options_.size() + 1);
247 impl_->options_.push_back(storage);
250 void SelectionOptionManager::convertOptionValue(SelectionOptionStorage* storage,
251 const std::string& value,
252 bool bFullValue)
254 SelectionList selections = impl_->collection_.parseFromString(value);
255 storage->addSelections(selections, bFullValue);
258 void SelectionOptionManager::requestOptionDelayedParsing(SelectionOptionStorage* storage)
260 impl_->requests_.emplace_back(storage);
263 bool SelectionOptionManager::hasRequestedSelections() const
265 return !impl_->requests_.empty();
268 void SelectionOptionManager::initOptions(IOptionsContainer* options)
270 bool allowOnlyAtomOutput = true;
271 Impl::OptionList::const_iterator iter;
272 for (iter = impl_->options_.begin(); iter != impl_->options_.end(); ++iter)
274 if (!(*iter)->allowsOnlyAtoms())
276 allowOnlyAtomOutput = false;
280 SelectionCollection::SelectionTypeOption typeOption =
281 allowOnlyAtomOutput ? SelectionCollection::AlwaysAtomSelections
282 : SelectionCollection::IncludeSelectionTypeOption;
283 impl_->collection_.initOptions(options, typeOption);
286 void SelectionOptionManager::parseRequestedFromStdin(bool bInteractive)
288 Impl::RequestsClearer clearRequestsOnExit(&impl_->requests_);
290 Impl::RequestList::const_iterator i;
291 for (i = impl_->requests_.begin(); i != impl_->requests_.end(); ++i)
293 const Impl::SelectionRequest& request = *i;
294 std::string context = formatString("for option '%s'\n(%s)", request.name().c_str(),
295 request.description().c_str());
296 SelectionList selections =
297 impl_->collection_.parseFromStdin(request.count(), bInteractive, context);
298 request.storage_->addSelections(selections, true);
302 void SelectionOptionManager::parseRequestedFromFile(const std::string& filename)
304 SelectionList selections = impl_->collection_.parseFromFile(filename);
307 impl_->placeSelectionsInRequests(selections);
309 catch (GromacsException& ex)
311 ex.prependContext(formatString("Error in adding selections from file '%s'", filename.c_str()));
312 throw;
316 void SelectionOptionManager::parseRequestedFromString(const std::string& str)
318 SelectionList selections = impl_->collection_.parseFromString(str);
319 impl_->placeSelectionsInRequests(selections);
322 } // namespace gmx