/* * MapOfActions.hpp * * Created on: 10.05.2010 * Author: heber */ #ifndef MAPOFACTIONS_HPP_ #define MAPOFACTIONS_HPP_ #include #include #include #include #include #include #include "Exceptions/IllegalTypeException.hpp" #include "Exceptions/MissingValueException.hpp" #include "Helpers/Assert.hpp" #include "Patterns/Singleton.hpp" class MapOfActionsTest; class Box; class atom; class element; class molecule; class Vector; namespace po = boost::program_options; using boost::lexical_cast; /** Central class for adding functionality to the code. * * In Molecuilder everything that can be done - such as adding atoms, * translating molecules, saving bind information - is an Action. * * In order to reference Action's with what the user sees, this class is the * mediator. * * An Action is described to the user by: * -# a name (this is the most important information) * -# a description * -# a shortform (single letter for use on the command line) * -# a text menu it resides in * -# the type of its argument * -# the command line category * * The Action::NAME is the most important information because every Action * registers itself automatically with the ActionRegistry and can be retrieved * therefrom and from this MapOfActions simply by knowing its name alone. * * In the constructor of MapOfActions all this is set. * * Note that Action will require input from the user. This is done via class * Query. * * And note also that MapOfActions actually contains more than just all * Actions: There are a number of names that actually are just arguments to * actions (e.g. "output-file"). * *

Howto add an Action

* * Let us assume your new action (class) is called SuperDuperAction, consisting * of two files SuperDuperAction.cpp and SuperDuperAction.hpp. * * Furthermore, let's say you Action needs two values: a double value as a * upper threshold and a string which is the name of the output file. * *

Command Line preliminaries

* * You have to decide whether (for the command line) it makes sense to have an * extra argument requesting the arguments, or one should be the argument of * your action. I.e. your action name is "super-duper", then the use may * call your action like this: * * ./molecuilder --super-duper 4 --output-file test.dat * * Here, we have picked the threshold as the value for your action and the * name of the output file is given by an additional argument. Of course, * it can be the other way round or by two arguments such as here: * * ./molecuilder --super-duper --threshold 4 --output-file test.dat * * It depends on what possible arguments are already there (don't make up * new ones if present ones actually make sense for your action) and which * argument is more natural or closer to what your action does. * *

Menu preliminaries

* * Whatever you decide, your action will need some Query dialogs to request * the necessary information from the user, either via a command line * argument (--output-file) via a text dialog (referenced by "output-file") * or via a graphical dialog (same reference). And therein, the names * of the arguments have to re-appear. * * Then, the following steps have to be done to incorporate your Action: * -# create a unique name for your action (no capital letters) to reference * it, this name has to appear in the file SuperDuperAction.cpp, e.g. * "super-duper" * -# pick names the other required arguments, best if they are already * present in the MapOfActions. They have to appear in Query's in the * code of your Action. * -# With this name create entries in the following maps for the action * name and for each names of a desired addtional argument if not present: * -# DescriptionMap, a catchy description of what your action does * -# TypeMap, see MapOfActions::OptionTypes for possible types of the single * argument it takes. * -# MenuContainsActionMap, in which menu should your action appear * -# ShortFormMap (optional), single letter for command line call * -# DefaultValueMap (optional), the default value (always a string) * -# add to one of the command line sets by the following categories * -# generic - generic options (i.e. not one of the others) * -# config - action/argument only considers internal bevahior, user * does not have to see it while still having full functionality * -# hidden - this should be hidden from the user * -# visible - this should be visible to the user * -# inputfile - this should only be parsed from an input file, not * from command line * -# add to a menu, i.e. make an entry in MenuContainsActionMap. * -# add header file SuperDuperAction.hpp to MapOfActions.cpp and instantiate * your action in populateMenu() (mind the sorting: 1. menu, * 2. alphabetical) * * And that's. * * Now, your action can be called from the command line, within the text * menu and the graphical user interface. * */ class MapOfActions : public Singleton { friend class Singleton; friend class MapOfActionsTest; public: enum OptionTypes { None, Boolean, Integer, ListOfIntegers, Double, ListOfDoubles, String, ListOfStrings, Vector, ListOfVectors, Box, Molecule, ListOfMolecules, Atom, ListOfAtoms, Element, ListOfElements }; // getter for the action descriptions and short forms std::string getDescription(std::string actionname); std::string getKeyAndShortForm(std::string actionname); std::string getShortForm(std::string actionname); std::map getShortFormToActionMap(); void AddOptionsToParser(); // check presence and getter for action type bool hasValue(std::string actionname); bool isShortFormPresent(std::string shortform); std::string getValueType(std::string actionname); std::set generic; std::set config; std::set hidden; std::set visible; std::set inputfile; std::multimap MenuContainsActionMap; std::map > MenuDescription; // instantiates and puts all known actions into the ActionRegistry void populateActions(); void queryCurrentValue(const char * name, class atom * &_T); void queryCurrentValue(const char * name, class element * &_T); void queryCurrentValue(const char * name, class molecule * &_T); void queryCurrentValue(const char * name, class Box &_T); void queryCurrentValue(const char * name, class Vector &_T); void queryCurrentValue(const char * name, std::vector&_T); void queryCurrentValue(const char * name, std::vector&_T); void queryCurrentValue(const char * name, std::vector&_T); template void queryCurrentValue(const char * name, T &_T) { if (typeid( T ) == *TypeMap[name]) { // constructor of type_info is private, hence can only store by ref or ptr if (CurrentValue.find(name) == CurrentValue.end()) throw MissingValueException(__FILE__, __LINE__); _T = lexical_cast(CurrentValue[name].c_str()); CurrentValue.erase(name); } else throw IllegalTypeException(__FILE__,__LINE__); } template void queryCurrentValue(const char * name, std::vector &_T) { T temp; if (typeid( std::vector ) == *TypeMap[name]) { // constructor of type_info is private, hence can only store by ref or ptr if (CurrentValue.find(name) == CurrentValue.end()) throw MissingValueException(__FILE__, __LINE__); std::istringstream stream(CurrentValue[name]); CurrentValue.erase(name); while (!stream.fail()) { stream >> temp >> std::ws; _T.push_back(temp); } } else throw IllegalTypeException(__FILE__,__LINE__); } void setCurrentValue(const char * name, class atom * &_T); void setCurrentValue(const char * name, class element * &_T); void setCurrentValue(const char * name, class molecule * &_T); void setCurrentValue(const char * name, class Box &_T); void setCurrentValue(const char * name, class Vector &_T); void setCurrentValue(const char * name, std::vector&_T); void setCurrentValue(const char * name, std::vector&_T); void setCurrentValue(const char * name, std::vector&_T); template void setCurrentValue(const char * name, T &_T) { std::ostringstream stream; if (typeid( T ) == *TypeMap[name]) { // constructor of type_info is private, hence can only store by ref or ptr stream << _T; CurrentValue[name] = stream.str(); } else throw IllegalTypeException(__FILE__,__LINE__); } template void setCurrentValue(const char * name, std::vector &_T) { std::ostringstream stream; if (typeid( std::vector ) == *TypeMap[name]) { // constructor of type_info is private, hence can only store by ref or ptr std::ostringstream stream; for (typename std::vector::iterator iter = _T.begin(); iter != _T.end(); ++iter) { stream << (*iter) << " "; } CurrentValue[name] = stream.str(); } else throw IllegalTypeException(__FILE__,__LINE__); } private: // private constructor and destructor MapOfActions(); virtual ~MapOfActions(); // lookup list from our configs to the ones of CommandLineParser std::map< std::set *, po::options_description *> CmdParserLookup; // map of the action names and their description std::map CurrentValue; std::map DescriptionMap; std::map ShortFormMap; std::map TypeMap; std::map TypeEnumMap; }; #endif /* MAPOFACTIONS_HPP_ */