00001 #pragma ident "$Id: AppOption.hpp 2972 2011-11-10 12:10:39Z yanweignss $"
00002
00008 #ifndef GPSTK_APPOPTION_HPP
00009 #define GPSTK_APPOPTION_HPP
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033 #include <string>
00034 #include <set>
00035 #include "String.hpp"
00036 #include "Exception.hpp"
00037 #include "DebugUtils.hpp"
00038
00039 namespace gpstk
00040 {
00041
00042
00043
00044
00045 class AbstractOptionCallback
00046 {
00047 public:
00048 virtual void invoke(const std::string& name,
00049 const std::string& value) const = 0;
00050
00051 virtual AbstractOptionCallback* clone() const = 0;
00052
00053 virtual ~AbstractOptionCallback(){}
00054
00055 protected:
00056 AbstractOptionCallback(){}
00057 AbstractOptionCallback(const AbstractOptionCallback&){}
00058 };
00059
00060 template <class C>
00061 class OptionCallback: public AbstractOptionCallback
00062 {
00063 public:
00064 typedef void (C::*Callback)(const std::string& name,
00065 const std::string& value);
00066
00067 OptionCallback(C* pObject, Callback method)
00068 : _pObject(pObject), _method(method) { GPSTK_CHECK_PTR(pObject); }
00069
00070 OptionCallback(const OptionCallback& cb)
00071 : AbstractOptionCallback(cb), _pObject(cb._pObject),_method(cb._method)
00072 {}
00073
00074 ~OptionCallback(){}
00075
00076 OptionCallback& operator = (const OptionCallback& cb)
00077 {
00078 if (&cb != this)
00079 {
00080 this->_pObject = cb._pObject;
00081 this->_method = cb._method;
00082 }
00083
00084 return *this;
00085 }
00086
00087 void invoke(const std::string& name, const std::string& value) const
00088 { (_pObject->*_method)(name, value); }
00089
00090 AbstractOptionCallback* clone() const
00091 { return new OptionCallback(_pObject, _method); }
00092
00093 protected:
00094 OptionCallback(){}
00095
00096 C* _pObject;
00097 Callback _method;
00098 };
00099
00101
00136 class Option
00137 {
00138 public:
00139 Option()
00140 : _required(false), _repeatable(false),
00141 _argRequired(false),_pCallback(0) {}
00142
00143
00144 Option(const Option& option)
00145 : _shortName(option._shortName),
00146 _fullName(option._fullName),
00147 _description(option._description),
00148 _required(option._required),
00149 _repeatable(option._repeatable),
00150 _argName(option._argName),
00151 _argRequired(option._argRequired),
00152 _group(option._group),
00153 _pCallback(option._pCallback)
00154 { if (_pCallback) _pCallback = _pCallback->clone();}
00155
00156
00157 Option(const std::string& fullName, const std::string& shortName)
00158 : _shortName(shortName),
00159 _fullName(fullName),
00160 _required(false),
00161 _repeatable(false),
00162 _argRequired(false),
00163 _pCallback(0) {}
00164
00165
00166 Option(const std::string& fullName,
00167 const std::string& shortName,
00168 const std::string& description,
00169 bool required=false)
00170 : _shortName(shortName),
00171 _fullName(fullName),
00172 _description(description),
00173 _required(required),
00174 _repeatable(false),
00175 _argRequired(false),
00176 _pCallback(0) {}
00177
00178
00179 Option(const std::string& fullName,
00180 const std::string& shortName,
00181 const std::string& description,
00182 bool required,
00183 const std::string& argName,
00184 bool argRequired=false)
00185 : _shortName(shortName),
00186 _fullName(fullName),
00187 _description(description),
00188 _required(required),
00189 _repeatable(false),
00190 _argName(argName),
00191 _argRequired(argRequired),
00192 _pCallback(0) {}
00193
00194 ~Option() { delete _pCallback; }
00195
00196 Option& operator = (const Option& option)
00197 {
00198 if (&option != this)
00199 {
00200 Option tmp(option);
00201 swap(tmp);
00202 }
00203 return *this;
00204 }
00205
00206 void swap(Option& option)
00207 {
00208 std::swap(_shortName, option._shortName);
00209 std::swap(_fullName, option._fullName);
00210 std::swap(_description, option._description);
00211 std::swap(_required, option._required);
00212 std::swap(_repeatable, option._repeatable);
00213 std::swap(_argName, option._argName);
00214 std::swap(_argRequired, option._argRequired);
00215 std::swap(_group, option._group);
00216 std::swap(_pCallback, option._pCallback);
00217 }
00218
00219 Option& shortName(const std::string& name)
00220 {
00221 _shortName = name;
00222 return *this;
00223 }
00224
00225 Option& fullName(const std::string& name)
00226 {
00227 _fullName = name;
00228 return *this;
00229 }
00230
00231 Option& description(const std::string& text)
00232 {
00233 _description = text;
00234 return *this;
00235 }
00236
00237 Option& required(bool flag)
00238 {
00239 _required = flag;
00240 return *this;
00241 }
00242
00243 Option& repeatable(bool flag)
00244 {
00245 _repeatable = flag;
00246 return *this;
00247 }
00248
00249 Option& argument(const std::string& name, bool required = true)
00250 {
00251 _argName = name;
00252 _argRequired = required;
00253 return *this;
00254 }
00255
00256 Option& noArgument()
00257 {
00258 _argName.clear();
00259 _argRequired = false;
00260 return *this;
00261 }
00262
00263 Option& group(const std::string& group)
00264 {
00265 _group = group;
00266 return *this;
00267 }
00268
00269 Option& callback(const AbstractOptionCallback& cb)
00270 {
00271 _pCallback = cb.clone();
00272 return *this;
00273 }
00274
00275 const std::string& shortName() const
00276 { return _shortName; }
00277
00278 const std::string& fullName() const
00279 { return _fullName; }
00280
00281 const std::string& description() const
00282 { return _description; }
00283
00284 bool required() const
00285 { return _required; }
00286
00287 bool repeatable() const
00288 { return _repeatable; }
00289
00290 bool takesArgument() const
00291 { return !_argName.empty(); }
00292
00293 bool argumentRequired() const
00294 { return _argRequired; }
00295
00296 const std::string& argumentName() const
00297 { return _argName; }
00298
00299 const std::string& group() const
00300 { return _group; }
00301
00302 AbstractOptionCallback* callback() const
00303 { return _pCallback; }
00304
00305 bool matchesShort(const std::string& option) const
00306 {
00307 return option.length() > 0
00308 && !_shortName.empty() && option.compare(0, _shortName.length(), _shortName) == 0;
00309 }
00310
00311 bool matchesFull(const std::string& option) const
00312 {
00313 std::string::size_type pos = option.find_first_of(":=");
00314 std::string::size_type len = pos == std::string::npos ? option.length() : pos;
00315 return len == _fullName.length()
00316 && icompare(option, 0, len, _fullName, 0, len) == 0;
00317 }
00318
00319 bool matchesPartial(const std::string& option) const
00320 {
00321 std::string::size_type pos = option.find_first_of(":=");
00322 std::string::size_type len = pos == std::string::npos ? option.length() : pos;
00323 return option.length() > 0
00324 && icompare(option, 0, len, _fullName, 0, len) == 0;
00325 }
00326
00327 inline void process(const std::string& option, std::string& arg) const;
00328
00329 protected:
00330 std::string _shortName;
00331 std::string _fullName;
00332 std::string _description;
00333 bool _required;
00334 bool _repeatable;
00335 std::string _argName;
00336 bool _argRequired;
00337 std::string _group;
00338 AbstractOptionCallback* _pCallback;
00339
00340 };
00341
00342 inline void Option::process(const std::string& option,
00343 std::string& arg) const
00344 {
00345 std::string::size_type pos = option.find_first_of(":=");
00346 std::string::size_type len = (pos==std::string::npos)?option.length():pos;
00347 if (icompare(option, 0, len, _fullName, 0, len) == 0)
00348 {
00349 if (takesArgument())
00350 {
00351 if (argumentRequired() && pos == std::string::npos)
00352 GPSTK_THROW(Exception(_fullName+" requires "+argumentName()));
00353 if (pos != std::string::npos)
00354 arg.assign(option, pos + 1, option.length() - pos - 1);
00355 else
00356 arg.clear();
00357 }
00358 else if (pos != std::string::npos)
00359 {
00360 GPSTK_THROW(Exception("Unexpected argument "+option));
00361 }
00362 else arg.clear();
00363 }
00364 else if (!_shortName.empty() &&
00365 option.compare(0, _shortName.length(), _shortName) == 0)
00366 {
00367 if (takesArgument())
00368 {
00369 if (argumentRequired() && option.length() == _shortName.length())
00370 GPSTK_THROW(Exception(_shortName+" requires "+argumentName()));
00371 arg.assign(option, _shortName.length(),
00372 option.length()-_shortName.length());
00373 }
00374 else if (option.length() != _shortName.length())
00375 {
00376 GPSTK_THROW(Exception("Unexpected argument "+option));
00377 }
00378 else arg.clear();
00379 }
00380 else GPSTK_THROW(Exception("Unknown option "+option));
00381
00382 }
00383
00384
00385 class OptionSet
00386 {
00387 public:
00388 typedef std::vector<Option> OptionVec;
00389 typedef OptionVec::const_iterator Iterator;
00390
00391 OptionSet(){}
00392
00393 OptionSet(const OptionSet& options)
00394 : _options(options._options) {}
00395
00396 ~OptionSet() {}
00397
00398 OptionSet& operator = (const OptionSet& options)
00399 {
00400 if (&options != this) _options = options._options;
00401 return *this;
00402 }
00403
00404 void addOption(const Option& option)
00405 {
00406 GPSTK_ASSERT(!option.fullName().empty());
00407
00408 for (Iterator it=begin(); it != end(); it++)
00409 {
00410 if (it->fullName() == option.fullName())
00411 {
00412 GPSTK_THROW(Exception(it->fullName()));
00413 }
00414 }
00415
00416 _options.push_back(option);
00417 }
00418
00427 bool hasOption(const std::string& name, bool matchShort = false) const
00428 {
00429 bool found = false;
00430 for (Iterator it = _options.begin(); it != _options.end(); ++it)
00431 {
00432 if ((matchShort && it->matchesShort(name)) ||
00433 (!matchShort && it->matchesFull(name)) )
00434 {
00435 if (!found) found = true;
00436 else return false;
00437 }
00438 }
00439 return found;
00440 }
00441
00452 const Option& getOption(const std::string& name,
00453 bool matchShort = false) const
00454 {
00455 const Option* pOption = 0;
00456 for (Iterator it = _options.begin(); it != _options.end(); ++it)
00457 {
00458 if ((matchShort && it->matchesShort(name)) ||
00459 (!matchShort && it->matchesPartial(name)))
00460 {
00461 if (!pOption)
00462 {
00463 pOption = &*it;
00464 if (!matchShort && it->matchesFull(name)) break;
00465 }
00466 else if (!matchShort && it->matchesFull(name))
00467 {
00468 pOption = &*it;
00469 break;
00470 }
00471 else GPSTK_THROW(Exception("Ambiguous Option " + name));
00472 }
00473 }
00474 if (pOption) return *pOption;
00475 else GPSTK_THROW(Exception("Unknown Option " + name));
00476 }
00477
00478 Iterator begin() const { return _options.begin(); }
00479
00480 Iterator end() const { return _options.end(); }
00481
00482 protected:
00483 OptionVec _options;
00484
00485 };
00486
00487
00518 class OptionProcessor
00519 {
00520 public:
00521 OptionProcessor(const OptionSet& options)
00522 :_options(options),_unixStyle(true),_ignore(false){}
00523
00524 ~OptionProcessor() {}
00525
00526 void setUnixStyle(bool flag)
00527 { _unixStyle = flag; }
00528
00529
00530 bool isUnixStyle() const
00531 { return _unixStyle; }
00532
00533
00534 bool process(const std::string& argument,
00535 std::string& optionName,
00536 std::string& optionArg)
00537 {
00538 if (!_ignore)
00539 {
00540 if (_unixStyle) return processUnix(argument, optionName, optionArg);
00541 else return processDefault(argument, optionName, optionArg);
00542 }
00543 return false;
00544 }
00545
00546
00547 void checkRequired() const
00548 {
00549 for (OptionSet::Iterator it = _options.begin(); it != _options.end(); ++it)
00550 {
00551 if (it->required() &&
00552 _specifiedOptions.find(it->fullName())==_specifiedOptions.end())
00553 GPSTK_THROW(Exception(it->fullName()));
00554 }
00555 }
00556
00557
00558 protected:
00559 inline bool processUnix(const std::string& argument,
00560 std::string& optionName,
00561 std::string& optionArg );
00562
00563 inline bool processDefault(const std::string& argument,
00564 std::string& optionName,
00565 std::string& optionArg);
00566
00567 inline bool processCommon(const std::string& optionStr,
00568 bool isShort,
00569 std::string& optionName,
00570 std::string& optionArg);
00571
00572 const OptionSet& _options;
00573 bool _unixStyle;
00574 bool _ignore;
00575 std::set<std::string> _groups;
00576 std::set<std::string> _specifiedOptions;
00577
00578 };
00579
00580 inline bool OptionProcessor::processUnix(const std::string& argument,
00581 std::string& optionName,
00582 std::string& optionArg )
00583 {
00584 std::string::const_iterator it = argument.begin();
00585 std::string::const_iterator end = argument.end();
00586 if (it != end)
00587 {
00588 if (*it == '-')
00589 {
00590 ++it;
00591 if (it != end)
00592 {
00593 if (*it == '-')
00594 {
00595 ++it;
00596 if (it == end)
00597 {
00598 _ignore = true;
00599 return true;
00600 }
00601 else return processCommon(std::string(it, end), false, optionName, optionArg);
00602 }
00603 else return processCommon(std::string(it, end), true, optionName, optionArg);
00604 }
00605 }
00606 }
00607 return false;
00608
00609 }
00610
00611 inline bool OptionProcessor::processDefault(const std::string& argument,
00612 std::string& optionName,
00613 std::string& optionArg)
00614 {
00615 std::string::const_iterator it = argument.begin();
00616 std::string::const_iterator end = argument.end();
00617 if (it != end)
00618 {
00619 if (*it == '/')
00620 {
00621 ++it;
00622 return processCommon(std::string(it, end), false, optionName, optionArg);
00623 }
00624 }
00625 return false;
00626
00627 }
00628
00629 inline bool OptionProcessor::processCommon(const std::string& optionStr,
00630 bool isShort,
00631 std::string& optionName,
00632 std::string& optionArg)
00633 {
00634 if (optionStr.empty()) throw Exception();
00635 const Option& option = _options.getOption(optionStr, isShort);
00636 const std::string& group = option.group();
00637 if (!group.empty())
00638 {
00639 if (_groups.find(group) != _groups.end())
00640 {
00641 GPSTK_THROW(Exception(option.fullName()));
00642 }
00643 else
00644 _groups.insert(group);
00645 }
00646 if (_specifiedOptions.find(option.fullName())!=_specifiedOptions.end() &&
00647 !option.repeatable())
00648 {
00649 GPSTK_THROW(Exception(option.fullName()));
00650 }
00651 _specifiedOptions.insert(option.fullName());
00652 option.process(optionStr, optionArg);
00653 optionName = option.fullName();
00654
00655 return true;
00656
00657 }
00658
00659
00660 class HelpFormatter
00661 {
00662 public:
00663 HelpFormatter(const OptionSet& options)
00664 : _options(options),
00665 _width(78),
00666 _tabWidth(4),
00667 _indent(0),
00668 _unixStyle(true) { _indent = calcIndent(); }
00669
00670 ~HelpFormatter(){}
00671
00672 void setCommand(const std::string& command)
00673 { _command = command; }
00674
00675 const std::string& getCommand() const
00676 { return _command; }
00677
00678 void setUsage(const std::string& usage)
00679 { _usage = usage; }
00680
00681 const std::string& getUsage() const
00682 { return _usage; }
00683
00684 void setHeader(const std::string& header)
00685 { _header = header; }
00686
00687 const std::string& getHeader() const
00688 { return _header; }
00689
00690 void setFooter(const std::string& footer)
00691 { _footer = footer; }
00692
00693 const std::string& getFooter() const
00694 { return _footer; }
00695
00696 void format(std::ostream& ostr) const
00697 {
00698 ostr << "Usage: " << _command;
00699 if (!_usage.empty())
00700 {
00701 ostr << ' ';
00702 formatText(ostr, _usage, (int) _command.length() + 1);
00703 }
00704 ostr << '\n';
00705 if (!_header.empty())
00706 {
00707 formatText(ostr, _header, 0);
00708 ostr << "\n\n";
00709 }
00710 ostr << "Options: " << "\n";
00711 formatOptions(ostr);
00712 if (!_footer.empty())
00713 {
00714 ostr << '\n';
00715 formatText(ostr, _footer, 0);
00716 ostr << '\n';
00717 }
00718 }
00719
00720 void setWidth(int width)
00721 { _width = width; }
00722
00723 int getWidth() const
00724 { return _width; }
00725
00726 void setIndent(int indent)
00727 { _indent = indent; }
00728
00729 int getIndent() const
00730 { return _indent; }
00731
00732 void setAutoIndent()
00733 { _indent = calcIndent(); }
00734
00735 void setUnixStyle(bool flag)
00736 { _unixStyle = flag; }
00737
00738 bool isUnixStyle() const
00739 { return _unixStyle; }
00740
00741 std::string shortPrefix() const
00742 { return _unixStyle ? "-" : "/"; }
00743
00744 std::string longPrefix() const
00745 { return _unixStyle ? "--" : "/"; }
00746
00747 protected:
00748
00749 int calcIndent() const
00750 {
00751 int indent = 0;
00752 for (OptionSet::Iterator it = _options.begin(); it != _options.end(); ++it)
00753 {
00754 int shortLen = (int) it->shortName().length();
00755 int fullLen = (int) it->fullName().length();
00756 int n = 0;
00757 if (_unixStyle && shortLen > 0)
00758 {
00759 n += shortLen + (int) shortPrefix().length();
00760 if (it->takesArgument())
00761 n += (int) it->argumentName().length() + (it->argumentRequired() ? 0 : 2);
00762 if (fullLen > 0) n += 2;
00763 }
00764 if (fullLen > 0)
00765 {
00766 n += fullLen + (int) longPrefix().length();
00767 if (it->takesArgument())
00768 n += 1 + (int) it->argumentName().length() + (it->argumentRequired() ? 0 : 2);
00769 }
00770 n += 2;
00771 if (n > indent)
00772 indent = n;
00773 }
00774 return indent;
00775 }
00776
00777 void formatOptions(std::ostream& ostr) const
00778 {
00779 int optWidth = calcIndent();
00780 for (OptionSet::Iterator it = _options.begin(); it != _options.end(); ++it)
00781 {
00782 formatOption(ostr, *it, optWidth);
00783 formatText(ostr, it->description(), _indent, optWidth);
00784 ostr << '\n';
00785 }
00786 }
00787
00788 void formatOption(std::ostream& ostr,
00789 const Option& option, int width) const
00790 {
00791 int shortLen = (int) option.shortName().length();
00792 int fullLen = (int) option.fullName().length();
00793
00794 int n = 0;
00795 if (_unixStyle && shortLen > 0)
00796 {
00797 ostr << " " << shortPrefix() << option.shortName();
00798 n += (int) shortPrefix().length() + (int) option.shortName().length();
00799 if (option.takesArgument())
00800 {
00801 if (!option.argumentRequired()) { ostr << '['; ++n; }
00802 ostr << option.argumentName();
00803 n += (int) option.argumentName().length();
00804 if (!option.argumentRequired()) { ostr << ']'; ++n; }
00805 }
00806 if (fullLen > 0) { ostr << ", "; n += 2; }
00807 }
00808 if (fullLen > 0)
00809 {
00810 ostr << longPrefix() << option.fullName();
00811 n += (int) longPrefix().length() + (int) option.fullName().length();
00812 if (option.takesArgument())
00813 {
00814 if (!option.argumentRequired()) { ostr << '['; ++n; }
00815 ostr << '=';
00816 ++n;
00817 ostr << option.argumentName();
00818 n += (int) option.argumentName().length();
00819 if (!option.argumentRequired()) { ostr << ']'; ++n; }
00820 }
00821 }
00822 while (n < width) { ostr << ' '; ++n; }
00823 }
00824
00825 void formatText(std::ostream& ostr,
00826 const std::string& text, int indent) const
00827 {
00828 formatText(ostr, text, indent, indent);
00829 }
00830
00831 void formatText(std::ostream& ostr,
00832 const std::string& text,
00833 int indent, int firstIndent) const
00834 {
00835 int pos = firstIndent;
00836 int maxWordLen = _width - indent;
00837 std::string word;
00838 for (std::string::const_iterator it = text.begin(); it != text.end(); ++it)
00839 {
00840 if (*it == '\n')
00841 {
00842 clearWord(ostr, pos, word, indent);
00843 ostr << '\n';
00844 pos = 0;
00845 while (pos < indent) { ostr << ' '; ++pos; }
00846 }
00847 else if (*it == '\t')
00848 {
00849 clearWord(ostr, pos, word, indent);
00850 if (pos < _width) ++pos;
00851 while (pos < _width && pos % _tabWidth != 0)
00852 {
00853 ostr << ' ';
00854 ++pos;
00855 }
00856 }
00857 else if (*it == ' ')
00858 {
00859 clearWord(ostr, pos, word, indent);
00860 if (pos < _width) { ostr << ' '; ++pos; }
00861 }
00862 else
00863 {
00864 if (word.length() == maxWordLen)
00865 {
00866 clearWord(ostr, pos, word, indent);
00867 }
00868 else word += *it;
00869 }
00870 }
00871 clearWord(ostr, pos, word, indent);
00872 }
00873
00874 void formatWord(std::ostream& ostr, int& pos,
00875 const std::string& word, int indent) const
00876 {
00877 if (pos + word.length() > _width)
00878 {
00879 ostr << '\n';
00880 pos = 0;
00881 while (pos < indent) { ostr << ' '; ++pos; }
00882 }
00883 ostr << word;
00884 pos += (int) word.length();
00885 }
00886
00887 void clearWord(std::ostream& ostr, int& pos,
00888 std::string& word, int indent) const
00889 {
00890 formatWord(ostr, pos, word, indent);
00891 word.clear();
00892 }
00893
00894 private:
00895 HelpFormatter(const HelpFormatter&);
00896
00897 HelpFormatter& operator = (const HelpFormatter&);
00898
00899 const OptionSet& _options;
00900
00901 int _width;
00902 int _tabWidth;
00903 int _indent;
00904
00905 std::string _command;
00906 std::string _usage;
00907 std::string _header;
00908 std::string _footer;
00909
00910 bool _unixStyle;
00911
00912 };
00913
00914 }
00915
00916
00917 #endif //GPSTK_OPTION_HPP
00918