#ifndef __JEEP__JPARSER__ #define __JEEP__JPARSER__ #include #include #include #include #include #include #include #include #include #include #include #include "JLang/Jpp.hh" #include "JLang/JAbstractIO.hh" #include "JLang/JException.hh" #include "JLang/JSharedPointer.hh" #include "JLang/JAbstractObjectStatus.hh" #include "JLang/JComparable.hh" #include "JLang/JComparisonAvailable.hh" #include "JLang/JBool.hh" #include "JLang/JResolve.hh" #include "JLang/JLangToolkit.hh" #include "JLang/JManip.hh" #include "Jeep/JStreamToolkit.hh" #include "Jeep/JeepToolkit.hh" #include "Jeep/JArgs.hh" #include "Jeep/JMessage.hh" #include "Jeep/JPrint.hh" /** * \file * Utility class to parse command line options. * \author mdejong */ class TString; //::value> class JProxy; /** * Template specialisation of JProxy for fundamental data type. */ template class JProxy { public: /** * Constructor. * * \param option textual value * \param value custom value */ JProxy(const std::string& option, const T& value) : __value (), __option(option), __custom(value) {} /** * Constructor. * * \param value actual value */ JProxy(const T& value) : __value (value), __option(), __custom() {} /** * Get actual value. * * \return value */ const T& getValue() const { return __value; } /** * Get option. * * \return option */ const std::string& getOption() const { return __option; } /** * Get custom value. * * \return value */ const T& getCustom() const { return __custom; } /** * Type conversion operator. * * \return object */ operator const T&() const { return __value; } /** * Assignment operator. * * Note that only the actual value is assigned. * * \param object object * \return this object */ JProxy& operator=(const JProxy& object) { __value = object.__value; return *this; } /** * Assignment to a value. * * \param value value * \return this object */ JProxy& operator=(const T& value) { __value = value; return *this; } /** * Read option from input. * * \param in input stream * \param object option * \return input stream */ friend inline std::istream& operator>>(std::istream& in, JProxy& object) { using namespace std; string buffer; if (getline(in, buffer)) { if (buffer == object.__option) { object.__value = object.__custom; } else { istringstream is(buffer); is >> object.__value; in.setstate(is.rdstate()); } } return in; } /** * Write options to output. * * \param out output stream * \param object option * \return output stream */ friend inline std::ostream& operator<<(std::ostream& out, const JProxy& object) { return out << object.__value; } protected: T __value; std::string __option; T __custom; }; /** * Template specialisation of JProxy for non-fundamental data type. */ template class JProxy : public T { public: /** * Constructor. * * \param option textual value * \param value custom value */ JProxy(const std::string& option, const T& value) : T(), __option(option), __custom(value) {} /** * Constructor. * * \param value actual value */ JProxy(const T& value) : T(value), __option(), __custom() {} /** * Get actual value. * * \return value */ const T& getValue() const { return static_cast(*this); } /** * Get option. * * \return option */ const std::string& getOption() const { return __option; } /** * Get custom value. * * \return value */ const T& getCustom() const { return __custom; } /** * Assignment operator. * * Note that only the actual value is assigned. * * \param object object * \return this object */ JProxy& operator=(const JProxy& object) { static_cast(*this) = static_cast(object); return *this; } /** * Assignment to a value. * * \param value value * \return this object */ JProxy& operator=(const T& value) { static_cast(*this) = value; return *this; } /** * Read option from input. * * \param in input stream * \param object option * \return input stream */ friend inline std::istream& operator>>(std::istream& in, JProxy& object) { using namespace std; string buffer; if (getline(in, buffer)) { if (buffer == object.__option) { static_cast(object) = object.__custom; } else { istringstream is(buffer); is >> static_cast(object); in.setstate(is.rdstate()); } } return in; } /** * Write options to output. * * \param out output stream * \param object option * \return output stream */ friend inline std::ostream& operator<<(std::ostream& out, const JProxy& object) { return out << static_cast(object); } protected: std::string __option; T __custom; }; /** * Auxiliary class to handle multiple boolean-like I/O. */ class JCounter : public JAbstractObjectStatus, public JComparable, public JComparable { public: /** * Default constructor. */ JCounter() : counter(0) {} /** * Get status of this counter. * * \return true if counter not zero; else false */ virtual bool getStatus() const override { return (this->counter != 0); } /** * Compare value. * * \param value value * \return true if this counter is less than given value; else false */ inline bool less(const JCounter& value) const { return this->counter < value.counter; } /** * Compare value. * * \param value value * \return true if this counter is less than given value; else false */ inline bool less(const int value) const { return this->counter < value; } /** * Compare value. * * \param value value * \return true if this counter is more than given value; else false */ inline bool more(const int value) const { return this->counter > value; } /** * Get counter. * * \return counter */ int getCounter() const { return counter; } /** * Set value. * * Note that no assignment actually takes place but the internal counter is set to one. * * \param value value * \return this object */ const JCounter& operator=(const bool value) { counter = 1; return *this; } /** * Read option from input. * * Note that no value actually is read, only the internal counter is incremented. * * \param in input stream * \param object option * \return input stream */ friend inline std::istream& operator>>(std::istream& in, JCounter& object) { ++object.counter; return in; } /** * Write options to output. * * \param out output stream * \param object option * \return output stream */ friend inline std::ostream& operator<<(std::ostream& out, const JCounter& object) { out << object.counter; return out; } protected: int counter; }; /** * Interface for I/O of parser element. */ class JParserElementInterface : public JStreamInput, public JStreamOutput { protected: /** * Constructor. * * \param name name of object * \param help help of object */ JParserElementInterface(const std::string& name = "arg", const std::string& help = "") : __name(name), __help(help) {} public: /** * Get name of parameter. * * \return name */ const std::string& getName() const { return __name; } /** * Get help of parameter. * * \return help */ const std::string& getHelp() const { return __help; } /** * Get status of parameter. * * \return status */ virtual bool getStatus() const = 0; /** * Get initialisation status of parameter. * * \return status */ virtual bool getInitialisationStatus() const = 0; /** * Set initialisation status of parameter. * * \param value initialisation status */ virtual void setInitialiationStatus(const bool value) = 0; /** * Print. * * \param out output stream */ virtual void print(std::ostream& out) const { using namespace std; out << "<" << getName() << ">"; if (getStatus() && !getShortprint(out)) { int width = WIDTH - getName().length(); if (width > 0) { out << setw(width) << " "; } out << " = "; write(out); } if (getLongprint(out) && getHelp() != "") { out << " \"" << getHelp() << "\""; } } /** * Read counter. * * \return true if at least one character to be read; else false */ virtual bool gcount() const { return true; } protected: std::string __name; std::string __help; }; /** * Template class holder for I/O of parser element. * This class implements the JPARSER::JParserElementInterface interface. */ template::has_eq> class JParserTemplateElement; /** * Auxiliary class to assign the remainder of a sequence of Comma Separated Values. */ template class JCSV { public: /** * Constructor. * * \param element parser element */ JCSV(JParserTemplateElement& element) : __element(element) {} /** * Type conversion operator. * * \return parser element */ operator const JParserTemplateElement&() const { return __element; } /** * Parsing of additional possible values. * * \param value possible value * \return this JCSV object */ JCSV& operator,(JType_t value) { __element.possibleValues.push_back(value); return *this; } /** * Parsing of additional possible values. * * \param values possible values * \return this object */ template class JContainer_t, class JAllocator_t> JCSV& operator,(const JContainer_t& values) { for (typename JContainer_t::const_iterator i = values.begin(); i != values.end(); ++i) { __element.possibleValues.push_back(*i); } return *this; } /** * Parsing of additional possible values. * * \param values possible values * \return this object */ template class JContainer_t, class JCompare_t, class JAllocator_t> JCSV& operator,(const JContainer_t& values) { for (typename JContainer_t::const_iterator i = values.begin(); i != values.end(); ++i) { __element.possibleValues.push_back(*i); } return *this; } private: JParserTemplateElement& __element; }; /** * Template specialisation of JPARSER::JParserTemplateElement for data type without equal operator ==. */ template class JParserTemplateElement : public JParserElementInterface { public: /** * Constructor. * * \param object reference to object * \param name name of object * \param help help of object */ JParserTemplateElement(JType_t& object, const std::string& name = "arg", const std::string& help = "") : JParserElementInterface(name, help), object(object), is_initialised(false) {} /** * Set initialised status to true. * * \param value initialised object * \return this object */ const JParserTemplateElement& operator=(const initialised& value) { setInitialiationStatus(true); return *this; } /** * Set initialised status to false. * * \param value initialised object * \return this object */ const JParserTemplateElement& operator=(const not_initialised& value) { setInitialiationStatus(false); return *this; } /** * Assignment to a default value. * * \param value default value * \return this object */ const JParserTemplateElement& operator=(const JType_t& value) { object = value; setInitialiationStatus(true); return *this; } /** * Get status of object. * * \return true if current value is ok, else false */ virtual bool getStatus() const override { return getInitialisationStatus(); } /** * Get initialisation status of parameter. * * \return status */ virtual bool getInitialisationStatus() const override { return is_initialised; } /** * Set initialisation status of parameter. * * \param value initialisation status */ virtual void setInitialiationStatus(const bool value) override { is_initialised = value; } /** * Stream input. * * \param in input stream * \return input stream */ virtual std::istream& read(std::istream& in) override { if (in.peek() == EOF) { THROW(JParserException, "JParser: error no data for parameter " << getName()); } readObject(in, object); if (fail(in)) { THROW(JParserException, "JParser: error reading parameter " << getName()); } while (isspace(in.peek())) { in.get(); } if (in.peek() != EOF) { THROW(JParserException, "JParser: pending data after reading parameter " << getName()); } setInitialiationStatus(true); return in; } /** * Stream output. * * \param out output stream * \return output stream */ virtual std::ostream& write(std::ostream& out) const override { return writeObject(out, object); } protected: JType_t& object; bool is_initialised; }; /** * Template specialisation of JParserTemplateElement::read to read complete line from stream input. * * \param in input stream * \return input stream */ template<> inline std::istream& JParserTemplateElement::read(std::istream& in) { std::getline(in, object); if (fail(in)) { THROW(JParserException, "JParser: error reading parameter " << getName()); } if (in.peek() != EOF) { THROW(JParserException, "JParser: pending data after reading parameter " << getName()); } setInitialiationStatus(true); return in; } /** * Template specialisation of JParserTemplateElement::write to surround text with quotes. * * \param out output stream * \return output stream */ template<> inline std::ostream& JParserTemplateElement::write(std::ostream& out) const { return writeObject(out, JLANG::double_quote(object)); } /** * Auxiliary class for handling I/O of TString depending on its existence.\n * The result is identical to that of std::string. */ template::value> struct TStringHelper; /** * Specialisation of TStringHelper if TString does not exist. */ template<> struct TStringHelper { /** * Read object from input stream.\n * This method thrown an error. * * \param in input stream * \param object object * \return input stream */ template static inline std::istream& read(std::istream& in, T& object) { THROW(JParserException, "JParser: invalid data type TString (include TString.h before JParser.hh)"); } /** * Read std::vector of objects from input stream.\n * This method thrown an error. * * \param in input stream * \param object object * \return input stream */ template static inline std::istream& read(std::istream& in, std::vector& object) { THROW(JParserException, "JParser: invalid data type TString (include TString.h before JParser.hh)"); } }; /** * Specialisation of TStringHelper if TString exists. */ template<> struct TStringHelper { /** * Read object from input stream. * * \param in input stream * \param object object * \return input stream */ template static inline std::istream& read(std::istream& in, T& object) { return object.ReadLine(in); } /** * Read std::vector of objects from input stream. * * \param in input stream * \param object object * \return input stream */ template static inline std::istream& read(std::istream& in, std::vector& object) { for (std::string buffer; in >> buffer; ) { object.push_back(buffer.c_str()); } return in; } }; /** * Template specialisation of JParserTemplateElement::read to read complete line from stream input. * * \param in input stream * \return input stream */ template<> inline std::istream& JParserTemplateElement::read(std::istream& in) { TStringHelper<>::read(in, object); setInitialiationStatus(true); return in; } /** * Template specialisation of JParserTemplateElement< std::vector >::read to read tokens from stream input. * * \param in input stream * \return input stream */ template<> inline std::istream& JParserTemplateElement, false>::read(std::istream& in) { TStringHelper<>::read(in, object); setInitialiationStatus(true); return in; } /** * Template specialisation of JPARSER::JParserTemplateElement for data type with equal operator ==. */ template class JParserTemplateElement : public JParserTemplateElement { public: friend class JCSV; /** * Constructor. * * \param object reference to object * \param name name of object * \param help help of object */ JParserTemplateElement(JType_t& object, const std::string& name = "arg", const std::string& help = "") : JParserTemplateElement(object, name, help) {} /** * Constructor. * * \param object reference to object * \param name name of object * \param __begin begin of possible values * \param __end end of possible values */ template JParserTemplateElement(JType_t& object, const std::string& name, T __begin, T __end) : JParserTemplateElement(object, name) { setPossibleValues(__begin, __end); } /** * Constructor. * * \param object reference to object * \param name name of object * \param help help of object * \param __begin begin of possible values * \param __end end of possible values */ template JParserTemplateElement(JType_t& object, const std::string& name, const std::string& help, T __begin, T __end) : JParserTemplateElement(object, name, help) { setPossibleValues(__begin, __end); } /** * Set initialised status to true. * * \param value initialised object * \return this object */ const JParserTemplateElement& operator=(const initialised& value) { this->setInitialiationStatus(true); return *this; } /** * Set initialised status to false. * * \param value initialised object * \return this object */ const JParserTemplateElement& operator=(const not_initialised& value) { this->setInitialiationStatus(false); return *this; } /** * Assignment to a default value and possible other values. * * \param value default value * \return comma separated values parser */ JCSV operator=(const JType_t& value) { this->object = value; this->setInitialiationStatus(true); possibleValues.push_back(value); return JCSV(*this); } /** * Assignment to a default value and possible other values. * * \param values default values * \return this object */ template class JContainer_t, class JAllocator_t> JCSV operator=(const JContainer_t& values) { setPossibleValues(values.begin(), values.end()); return JCSV(*this); } /** * Assignment to a default value and possible other values. * * \param values default values * \return this object */ template class JContainer_t, class JCompare_t, class JAllocator_t> JCSV operator=(const JContainer_t& values) { setPossibleValues(values.begin(), values.end()); return JCSV(*this); } /** * Get status of object. * * If more than one possible values are provided, * the current value should be equal to one of the possible values, * else a value should have been set by the user. * * \return true if current value is ok, else false */ virtual bool getStatus() const override { if (possibleValues.size() > 1) { for (typename std::vector::const_iterator i = possibleValues.begin(); i != possibleValues.end(); ++i) { if (this->object == *i) { return true; } } return false; } else { return this->getInitialisationStatus(); } } /** * Print. * * \param out output stream */ virtual void print(std::ostream& out) const override { JParserTemplateElement::print(out); if (possibleValues.size() > 1 && getLongprint(out)) { writeArray(out, " [", "]", ", ", possibleValues.begin(), possibleValues.end()); } } protected: /** * Set possible values. * * \param __begin begin of possible values * \param __end end of possible values */ template void setPossibleValues(T __begin, T __end) { if (__begin != __end) { this->object = *__begin; this->setInitialiationStatus(true); for (T i = __begin; i != __end; ++i) { possibleValues.push_back(*i); } } } std::vector possibleValues; }; /** * Template specialisation of JPARSER::JParserTemplateElement for type bool. * The value is by default set to false and set to true in method read() without reading any data. * This makes it possible to parse mutiple options in one go (e.g.\ -abc). * This class implements the JPARSER::JParserElementInterface interface. */ template<> class JParserTemplateElement : public JParserElementInterface { public: /** * Constructor. * * The constructor assigns the default value false to the referenced parameter. * * \param object reference to object * \param name name of object * \param help help of object */ JParserTemplateElement(bool& object, const std::string& name = "arg", const std::string& help = "") : JParserElementInterface(name, help), object(object) { this->object = false; } /** * Stream input. * This method sets the value to true, without reading any data. * * \param in input stream * \return input stream */ virtual std::istream& read(std::istream& in) override { this->object = true; return in; } /** * Stream output. * * \param out output stream * \return output stream */ virtual std::ostream& write(std::ostream& out) const override { return out << object; } /** * Status of object. * * \return true */ virtual bool getStatus() const override { return true; } /** * Get initialisation status of parameter. * * \return true */ virtual bool getInitialisationStatus() const override { return true; } /** * Set initialisation status of parameter. * This implementation doesn't do anything. * * \param value initialisation status */ virtual void setInitialiationStatus(const bool value) override {} /** * Read counter. * * \return true if at least one character to be read; else false */ virtual bool gcount() const override { return false; } private: bool& object; }; /** * Template specialisation of JPARSER::JParserTemplateElement for type JCounter. * The value is by default set to zero and set incremented in method read() without reading any data. * This makes it possible to parse mutiple options in one go (e.g.\ -aaa). * This class implements the JPARSER::JParserElementInterface interface. */ template<> class JParserTemplateElement : public JParserElementInterface { public: /** * Constructor. * * The constructor assigns the default value false to the referenced parameter. * * \param object reference to object * \param name name of object * \param help help of object */ JParserTemplateElement(JCounter& object, const std::string& name, const std::string& help = "") : JParserElementInterface(name, help), object(object) { this->object = JCounter(); } /** * Stream input. * This method sets the value to true, without reading any data. * * \param in input stream * \return input stream */ virtual std::istream& read(std::istream& in) override { return in >> object; } /** * Stream output. * * \param out output stream * \return output stream */ virtual std::ostream& write(std::ostream& out) const override { return out << object; } /** * Status of object. * * \return true */ virtual bool getStatus() const override { return true; } /** * Get initialisation status of parameter. * * \return true */ virtual bool getInitialisationStatus() const override { return true; } /** * Set initialisation status of parameter. * This implementation doesn't do anything. * * \param value initialisation status */ virtual void setInitialiationStatus(const bool value) override {} /** * Read counter. * * \return true if at least one character to be read; else false */ virtual bool gcount() const override { return false; } private: JCounter& object; }; /** * Auxiliary class to handle pointer to JPARSER::JParserElementInterface. */ class JParserElement : public JSharedPointer { public: typedef JSharedPointer JSharedPointer_t; /** * Default constructor. */ JParserElement() : JSharedPointer_t() {} /** * Copy constructor. * * \param value reference to JParserElement */ JParserElement(const JParserElement& value) : JSharedPointer_t(static_cast(value)) {} /** * Assignment operator. * * \param value reference to JParserElement * \return this JParserElement */ JParserElement& operator=(JParserElement& value) { JSharedPointer_t::operator=(static_cast(value)); return *this; } /** * Assignment operator. * * \param value reference to unnamed data object * \return corresponding new JParserTemplateElement object */ template JParserTemplateElement& operator=(JType_t& value) { JParserTemplateElement* __p = new JParserTemplateElement(value); reset(__p); return *__p; } /** * Assignment operator. * * \param value reference to JParserTemplateElement object * \return corresponding new JParserTemplateElement object */ template JParserTemplateElement& operator=(const JParserTemplateElement& value) { JParserTemplateElement* __p = new JParserTemplateElement(value); reset(__p); return *__p; } /** * Assignment operator. * * \param value reference to a corresponding JCSV object * \return corresponding new comma separated values parser */ template JCSV operator=(const JCSV& value) { JParserTemplateElement* __p = new JParserTemplateElement(value); reset(__p); return JCSV(*__p); } /** * Set initialised status to true. * * \param value initialised object * \return this object */ const JParserElement& operator=(const initialised& value) { if (!is_valid()) THROW(JParserException, "No parser object defined."); else (*this)->setInitialiationStatus(true); return *this; } /** * Set initialised status to false. * * \param value initialised object * \return this object */ const JParserElement& operator=(const not_initialised& value) { (*this)->setInitialiationStatus(false); return *this; } /** * Stream input. * * \param in input stream * \param value parser element * \return input stream */ friend inline std::istream& operator>>(std::istream& in, JParserElement& value) { if (value.is_valid()) return value->read(in); else return in; } /** * Stream output. * * \param out output stream * \param value parser element * \return output stream */ friend inline std::ostream& operator<<(std::ostream& out, const JParserElement& value) { if (value.is_valid()) return value->write(out); else return out; } /** * Print. * * \param out output stream */ void print(std::ostream& out) const { if (is_valid()) { return get()->print(out); } } }; /** * Utility class to parse command line options. * * The mapping between a parameter (of any type) and a unique option * has to be defined in the user's application, e.g. * \code{.cpp} #include "Jeep/JParser.hh" int main(int argc, char**argv) { int aap; bool noot; bool mies; try { JParser<> zap; zap['i'] = make_field(aap) = 123; // set default value zap['b'] = make_field(noot); // default is false zap['B'] = make_field(mies); zap(argc, argv); } catch(const std::exception& error) { cerr << error.what() << endl; return 1; } } \endcode * * The behaviour of the parser is different for parameters of type bool. * By default, its value is set to false; it is set to true * when the corresponding option is parsed. * This implies that no data are read and that several options can be * parsed in sequence without repeating the '-' symbol. * * * The syntax for the command line is: * \verbatim program [-