//! Collection for a list of selection requests.
typedef std::vector<SelectionRequest> RequestList;
+ //! Collection for list of option storage objects.
+ typedef std::vector<SelectionOptionStorage *> OptionList;
/*! \brief
* Helper class that clears a request list on scope exit.
* from the list.
*/
void placeSelectionsInRequests(const SelectionList &selections);
+ /*! \brief
+ * Adds a request for each required option that is not yet set.
+ *
+ * \throws std::bad_alloc if out of memory.
+ */
+ void requestUnsetRequiredOptions();
//! Selection collection to which selections are stored.
SelectionCollection &collection_;
+ //! List of selection options (storage objects) this manager manages.
+ OptionList options_;
//! List of selections requested for later parsing.
RequestList requests_;
};
void SelectionOptionManager::Impl::placeSelectionsInRequests(
const SelectionList &selections)
{
+ if (requests_.empty())
+ {
+ requestUnsetRequiredOptions();
+ }
+
RequestsClearer clearRequestsOnExit(&requests_);
SelectionList::const_iterator first = selections.begin();
}
}
+void SelectionOptionManager::Impl::requestUnsetRequiredOptions()
+{
+ OptionList::const_iterator i;
+ for (i = options_.begin(); i != options_.end(); ++i)
+ {
+ SelectionOptionStorage &storage = **i;
+ if (storage.isRequired() && !storage.isSet())
+ {
+ requests_.push_back(SelectionRequest(&storage));
+ }
+ }
+}
+
/********************************************************************
* SelectionOptionManager
return impl_->collection_;
}
+void
+SelectionOptionManager::registerOption(SelectionOptionStorage *storage)
+{
+ impl_->requests_.reserve(impl_->options_.size() + 1);
+ impl_->options_.push_back(storage);
+}
+
void
SelectionOptionManager::requestDelayedParsing(SelectionOptionStorage *storage)
{
* Does not throw.
*/
SelectionCollection &selectionCollection();
+ /*! \brief
+ * Adds a selection option to be managed.
+ *
+ * \param storage Storage object for the option to register.
+ * \throws std::bad_alloc if out of memory.
+ *
+ * This is only for internal use by the selection module.
+ * It is not possible to obtain a SelectionOptionStorage pointer
+ * through any public or library API.
+ *
+ * Strong exception safety.
+ */
+ void registerOption(SelectionOptionStorage *storage);
/*! \brief
* Adds a selection option for delayed user input.
*
* not the last (in which case it is not possible to determine
* which selections belong to which request).
*
- * This method behaves as parseRequestedFromStdin(), but reads the
- * selections from a file instead of standard input.
- * This is used to implement SelectionFileOption.
+ * This method behaves as parseRequestedFromStdin(), with two
+ * exceptions:
+ * -# It reads the selections from a file instead of standard input.
+ * -# If no requests are pending, assigns values to all required
+ * options that have not yet been set.
+ *
+ * This method used to implement SelectionFileOption.
*
* \see parseRequestedFromStdin()
*/
TEST_F(SelectionFileOptionTest, HandlesSingleSelectionOptionFromFile)
{
gmx::SelectionList sel;
+ gmx::SelectionList reqsel;
using gmx::SelectionOption;
ASSERT_NO_THROW(_options.addOption(
SelectionOption("sel").storeVector(&sel).multiValue()));
+ ASSERT_NO_THROW(_options.addOption(
+ SelectionOption("reqsel").storeVector(&reqsel)
+ .multiValue().required()));
setManager();
gmx::OptionsAssigner assigner(&_options);
ASSERT_EQ(2U, sel.size());
EXPECT_STREQ("resname RA RB", sel[0].selectionText());
EXPECT_STREQ("resname RB RC", sel[1].selectionText());
+ ASSERT_EQ(0U, reqsel.size());
}
}
+TEST_F(SelectionFileOptionTest, HandlesRequiredOptionFromFile)
+{
+ gmx::SelectionList sel;
+ gmx::SelectionList optsel;
+ using gmx::SelectionOption;
+ ASSERT_NO_THROW(_options.addOption(
+ SelectionOption("sel").storeVector(&sel)
+ .multiValue().required()));
+ ASSERT_NO_THROW(_options.addOption(
+ SelectionOption("optsel").storeVector(&optsel)
+ .multiValue()));
+ setManager();
+
+ gmx::OptionsAssigner assigner(&_options);
+ EXPECT_NO_THROW(assigner.start());
+ ASSERT_NO_THROW(assigner.startOption("sf"));
+ EXPECT_NO_THROW(assigner.appendValue(gmx::test::getTestFilePath("selfile.dat")));
+ EXPECT_NO_THROW(assigner.finishOption());
+ EXPECT_NO_THROW(assigner.startOption("optsel"));
+ EXPECT_NO_THROW(assigner.finishOption());
+ EXPECT_NO_THROW(assigner.finish());
+ EXPECT_NO_THROW(_options.finish());
+ EXPECT_NO_THROW(_manager.parseRequestedFromString("resname RC RD"));
+
+ // These should match the contents of selfile.dat
+ ASSERT_EQ(2U, sel.size());
+ EXPECT_STREQ("resname RA RB", sel[0].selectionText());
+ EXPECT_STREQ("resname RB RC", sel[1].selectionText());
+ ASSERT_EQ(1U, optsel.size());
+ EXPECT_STREQ("resname RC RD", optsel[0].selectionText());
+}
+
+
+// TODO: Is this the best possible behavior, or should it error out?
+TEST_F(SelectionFileOptionTest, HandlesRequiredOptionFromFileWithOtherOptionSet)
+{
+ gmx::SelectionList sel1;
+ gmx::SelectionList sel2;
+ using gmx::SelectionOption;
+ ASSERT_NO_THROW(_options.addOption(
+ SelectionOption("sel1").storeVector(&sel1)
+ .multiValue().required()));
+ ASSERT_NO_THROW(_options.addOption(
+ SelectionOption("sel2").storeVector(&sel2)
+ .multiValue().required()));
+ setManager();
+
+ gmx::OptionsAssigner assigner(&_options);
+ EXPECT_NO_THROW(assigner.start());
+ EXPECT_NO_THROW(assigner.startOption("sel1"));
+ EXPECT_NO_THROW(assigner.appendValue("resname RC RD"));
+ EXPECT_NO_THROW(assigner.finishOption());
+ ASSERT_NO_THROW(assigner.startOption("sf"));
+ EXPECT_NO_THROW(assigner.appendValue(gmx::test::getTestFilePath("selfile.dat")));
+ EXPECT_NO_THROW(assigner.finishOption());
+ EXPECT_NO_THROW(assigner.finish());
+ EXPECT_NO_THROW(_options.finish());
+
+ // These should match the contents of selfile.dat
+ ASSERT_EQ(2U, sel2.size());
+ EXPECT_STREQ("resname RA RB", sel2[0].selectionText());
+ EXPECT_STREQ("resname RB RC", sel2[1].selectionText());
+ ASSERT_EQ(1U, sel1.size());
+ EXPECT_STREQ("resname RC RD", sel1[0].selectionText());
+}
+
+
+TEST_F(SelectionFileOptionTest, HandlesTwoRequiredOptionsFromSingleFile)
+{
+ gmx::SelectionList sel1;
+ gmx::SelectionList sel2;
+ using gmx::SelectionOption;
+ ASSERT_NO_THROW(_options.addOption(
+ SelectionOption("sel1").storeVector(&sel1).required()));
+ ASSERT_NO_THROW(_options.addOption(
+ SelectionOption("sel2").storeVector(&sel2).required()));
+ setManager();
+
+ gmx::OptionsAssigner assigner(&_options);
+ std::string value(gmx::test::getTestFilePath("selfile.dat"));
+ EXPECT_NO_THROW(assigner.start());
+ ASSERT_NO_THROW(assigner.startOption("sf"));
+ EXPECT_NO_THROW(assigner.appendValue(value));
+ EXPECT_NO_THROW(assigner.finishOption());
+ EXPECT_NO_THROW(assigner.finish());
+ EXPECT_NO_THROW(_options.finish());
+
+ // These should match the contents of selfile.dat
+ ASSERT_EQ(1U, sel1.size());
+ EXPECT_STREQ("resname RA RB", sel1[0].selectionText());
+ ASSERT_EQ(1U, sel2.size());
+ EXPECT_STREQ("resname RB RC", sel2[0].selectionText());
+}
+
+
TEST_F(SelectionFileOptionTest, GivesErrorWithNoFile)
{
gmx::SelectionList sel;