changes (part 1) from 1.4.3 branch (XMLConfiguration delimiter, OptionProcessor)

This commit is contained in:
Marian Krivos
2011-11-15 13:59:05 +00:00
parent 97ec3f5bf6
commit cc90b38ae5
6 changed files with 245 additions and 119 deletions

View File

@@ -55,13 +55,13 @@ class Util_API OptionProcessor
/// An OptionProcessor is used to process the command line /// An OptionProcessor is used to process the command line
/// arguments of an application. /// arguments of an application.
/// ///
/// The process() method takes an argument from the command line. /// The process() method takes an argument from the command line.
/// If that argument starts with an option prefix, the argument /// If that argument starts with an option prefix, the argument
/// is further processed. Otherwise, the argument is ignored and /// is further processed. Otherwise, the argument is ignored and
/// false is ignored. The argument must match one of the options /// false is returned. The argument must match one of the options
/// given in the OptionSet that is passed to the OptionProcessor /// given in the OptionSet that is passed to the OptionProcessor
/// with the constructor. If an option is part of a group, at most /// with the constructor. If an option is part of a group, at most
/// one option of the group can be passed to the OptionProcessor. /// one option of the group can be passed to the OptionProcessor.
/// Otherwise an IncompatibleOptionsException is thrown. /// Otherwise an IncompatibleOptionsException is thrown.
/// If the same option is given multiple times, but the option /// If the same option is given multiple times, but the option
/// is not repeatable, a DuplicateOptionException is thrown. /// is not repeatable, a DuplicateOptionException is thrown.
@@ -79,12 +79,22 @@ class Util_API OptionProcessor
/// by a short option name, or another dash, followed by a (partial) /// by a short option name, or another dash, followed by a (partial)
/// long option name. /// long option name.
/// In default mode, the option prefix is a slash '/', followed by /// In default mode, the option prefix is a slash '/', followed by
/// a (partial) long option name. /// a (partial) long option name.
/// If the special option '--' is encountered in Unix mode, all following /// If the special option '--' is encountered in Unix mode, all following
/// options are ignored. /// options are ignored.
///
/// Option arguments can be specified in three ways. If a Unix short option
/// ("-o") is given, the argument directly follows the option name, without
/// any delimiting character or space ("-ovalue"). In default option mode, or if a
/// Unix long option ("--option") is given, the option argument is
/// delimited from the option name with either an equal sign ('=') or
/// a colon (':'), as in "--option=value" or "/option:value". Finally,
/// a required option argument can be specified on the command line after the
/// option, delimited with a space, as in "--option value" or "-o value".
/// The latter only works for required option arguments, not optional ones.
{ {
public: public:
OptionProcessor(const OptionSet& options); OptionProcessor(const OptionSet& options);
/// Creates the OptionProcessor, using the given OptionSet. /// Creates the OptionProcessor, using the given OptionSet.
~OptionProcessor(); ~OptionProcessor();
@@ -127,9 +137,10 @@ private:
const OptionSet& _options; const OptionSet& _options;
bool _unixStyle; bool _unixStyle;
bool _ignore; bool _ignore;
std::set<std::string> _groups; std::set<std::string> _groups;
std::set<std::string> _specifiedOptions; std::set<std::string> _specifiedOptions;
std::string _deferredOption;
}; };

View File

@@ -86,35 +86,68 @@ class Util_API XMLConfiguration: public AbstractConfiguration
/// prop5[1] -> value6 /// prop5[1] -> value6
/// prop5[@id=first] -> value5 /// prop5[@id=first] -> value5
/// prop5[@id='second'] -> value6 /// prop5[@id='second'] -> value6
/// ///
/// Enumerating attributes is not supported. /// Enumerating attributes is not supported.
/// Calling keys("prop3.prop4") will return an empty range. /// Calling keys("prop3.prop4") will return an empty range.
///
/// As a special feature, the delimiter character used to delimit
/// property names can be changed to something other than period ('.') by
/// passing the desired character to the constructor. This allows
/// working with XML documents having element names with periods
/// in them.
{ {
public: public:
XMLConfiguration(); XMLConfiguration();
/// Creates an empty XMLConfiguration. /// Creates an empty XMLConfiguration.
XMLConfiguration(Poco::XML::InputSource* pInputSource); XMLConfiguration(char delim);
/// Creates an XMLConfiguration and loads the XML document from /// Creates an empty XMLConfiguration, using the given
/// the given InputSource. /// delimiter char instead of the default '.'.
XMLConfiguration(std::istream& istr); XMLConfiguration(Poco::XML::InputSource* pInputSource);
/// Creates an XMLConfiguration and loads the XML document from /// Creates an XMLConfiguration and loads the XML document from
/// the given stream. /// the given InputSource.
XMLConfiguration(const std::string& path); XMLConfiguration(Poco::XML::InputSource* pInputSource, char delim);
/// Creates an XMLConfiguration and loads the XML document from /// Creates an XMLConfiguration and loads the XML document from
/// the given path. /// the given InputSource. Uses the given delimiter char instead
/// of the default '.'.
XMLConfiguration(const Poco::XML::Document* pDocument); XMLConfiguration(std::istream& istr);
/// Creates the XMLConfiguration using the given XML document. /// Creates an XMLConfiguration and loads the XML document from
/// the given stream.
XMLConfiguration(const Poco::XML::Node* pNode);
/// Creates the XMLConfiguration using the given XML node.
void load(Poco::XML::InputSource* pInputSource); XMLConfiguration(std::istream& istr, char delim);
/// Loads the XML document containing the configuration data /// Creates an XMLConfiguration and loads the XML document from
/// from the given InputSource. /// the given stream. Uses the given delimiter char instead
/// of the default '.'.
XMLConfiguration(const std::string& path);
/// Creates an XMLConfiguration and loads the XML document from
/// the given path.
XMLConfiguration(const std::string& path, char delim);
/// Creates an XMLConfiguration and loads the XML document from
/// the given path. Uses the given delimiter char instead
/// of the default '.'.
XMLConfiguration(const Poco::XML::Document* pDocument);
/// Creates the XMLConfiguration using the given XML document.
XMLConfiguration(const Poco::XML::Document* pDocument, char delim);
/// Creates the XMLConfiguration using the given XML document.
/// Uses the given delimiter char instead of the default '.'.
XMLConfiguration(const Poco::XML::Node* pNode);
/// Creates the XMLConfiguration using the given XML node.
XMLConfiguration(const Poco::XML::Node* pNode, char delim);
/// Creates the XMLConfiguration using the given XML node.
/// Uses the given delimiter char instead of the default '.'.
void load(Poco::XML::InputSource* pInputSource);
/// Loads the XML document containing the configuration data
/// from the given InputSource.
void load(std::istream& istr); void load(std::istream& istr);
/// Loads the XML document containing the configuration data /// Loads the XML document containing the configuration data
@@ -164,16 +197,17 @@ protected:
~XMLConfiguration(); ~XMLConfiguration();
private: private:
const Poco::XML::Node* findNode(const std::string& key) const; const Poco::XML::Node* findNode(const std::string& key) const;
Poco::XML::Node* findNode(const std::string& key); Poco::XML::Node* findNode(const std::string& key);
static Poco::XML::Node* findNode(std::string::const_iterator& it, const std::string::const_iterator& end, Poco::XML::Node* pNode, bool create = false); Poco::XML::Node* findNode(std::string::const_iterator& it, const std::string::const_iterator& end, Poco::XML::Node* pNode, bool create = false) const;
static Poco::XML::Node* findElement(const std::string& name, Poco::XML::Node* pNode, bool create); static Poco::XML::Node* findElement(const std::string& name, Poco::XML::Node* pNode, bool create);
static Poco::XML::Node* findElement(int index, Poco::XML::Node* pNode, bool create); static Poco::XML::Node* findElement(int index, Poco::XML::Node* pNode, bool create);
static Poco::XML::Node* findElement(const std::string& attr, const std::string& value, Poco::XML::Node* pNode); static Poco::XML::Node* findElement(const std::string& attr, const std::string& value, Poco::XML::Node* pNode);
static Poco::XML::Node* findAttribute(const std::string& name, Poco::XML::Node* pNode, bool create); static Poco::XML::Node* findAttribute(const std::string& name, Poco::XML::Node* pNode, bool create);
Poco::XML::AutoPtr<Poco::XML::Node> _pRoot; Poco::XML::AutoPtr<Poco::XML::Node> _pRoot;
Poco::XML::AutoPtr<Poco::XML::Document> _pDocument; Poco::XML::AutoPtr<Poco::XML::Document> _pDocument;
char _delim;
}; };

View File

@@ -116,15 +116,15 @@ void Application::setup()
_pConfig->add(new SystemConfiguration, PRIO_SYSTEM, false, false); _pConfig->add(new SystemConfiguration, PRIO_SYSTEM, false, false);
_pConfig->add(new MapConfiguration, PRIO_APPLICATION, true, false); _pConfig->add(new MapConfiguration, PRIO_APPLICATION, true, false);
addSubsystem(new LoggingSubsystem); addSubsystem(new LoggingSubsystem);
#if defined(POCO_OS_FAMILY_UNIX) && !defined(POCO_VXWORKS) #if defined(POCO_OS_FAMILY_UNIX) && !defined(POCO_VXWORKS)
_workingDirAtLaunch = Path::current(); _workingDirAtLaunch = Path::current();
#if !defined(_DEBUG) #if !defined(_DEBUG)
Poco::SignalHandler::install(); Poco::SignalHandler::install();
#endif #endif
#else #else
setUnixOptions(false); setUnixOptions(false);
#endif #endif
@@ -368,13 +368,13 @@ void Application::processOptions()
while (it != _args.end() && !_stopOptionsProcessing) while (it != _args.end() && !_stopOptionsProcessing)
{ {
std::string name; std::string name;
std::string value; std::string value;
if (processor.process(*it, name, value)) if (processor.process(*it, name, value))
{ {
if (!name.empty()) // "--" option to end options processing if (!name.empty()) // "--" option to end options processing or deferred argument
{ {
handleOption(name, value); handleOption(name, value);
} }
it = _args.erase(it); it = _args.erase(it);
} }
else ++it; else ++it;
@@ -393,19 +393,19 @@ void Application::getApplicationPath(Poco::Path& appPath) const
if (path.isAbsolute()) if (path.isAbsolute())
{ {
appPath = path; appPath = path;
} }
else else
{ {
appPath = _workingDirAtLaunch; appPath = _workingDirAtLaunch;
appPath.append(path); appPath.append(path);
} }
} }
else else
{ {
if (!Path::find(Environment::get("PATH"), _command, appPath)) if (!Path::find(Environment::get("PATH"), _command, appPath))
appPath = Path(_workingDirAtLaunch, _command); appPath = Path(_workingDirAtLaunch, _command);
appPath.makeAbsolute(); appPath.makeAbsolute();
} }
#elif defined(POCO_OS_FAMILY_WINDOWS) #elif defined(POCO_OS_FAMILY_WINDOWS)
#if defined(POCO_WIN32_UTF8) && !defined(POCO_NO_WSTRING) #if defined(POCO_WIN32_UTF8) && !defined(POCO_NO_WSTRING)
wchar_t path[1024]; wchar_t path[1024];

View File

@@ -173,12 +173,20 @@ int HelpFormatter::calcIndent() const
void HelpFormatter::formatOptions(std::ostream& ostr) const void HelpFormatter::formatOptions(std::ostream& ostr) const
{ {
int optWidth = calcIndent(); int optWidth = calcIndent();
for (OptionSet::Iterator it = _options.begin(); it != _options.end(); ++it) for (OptionSet::Iterator it = _options.begin(); it != _options.end(); ++it)
{ {
formatOption(ostr, *it, optWidth); formatOption(ostr, *it, optWidth);
formatText(ostr, it->description(), _indent, optWidth); if (_indent < optWidth)
ostr << '\n'; {
} ostr << '\n' << std::string(_indent, ' ');
formatText(ostr, it->description(), _indent, _indent);
}
else
{
formatText(ostr, it->description(), _indent, optWidth);
}
ostr << '\n';
}
} }

View File

@@ -65,12 +65,16 @@ void OptionProcessor::setUnixStyle(bool flag)
bool OptionProcessor::process(const std::string& argument, std::string& optionName, std::string& optionArg) bool OptionProcessor::process(const std::string& argument, std::string& optionName, std::string& optionArg)
{ {
if (!_ignore) optionName.clear();
{ optionArg.clear();
if (_unixStyle) if (!_ignore)
return processUnix(argument, optionName, optionArg); {
else if (!_deferredOption.empty())
return processDefault(argument, optionName, optionArg); return processCommon(argument, false, optionName, optionArg);
else if (_unixStyle)
return processUnix(argument, optionName, optionArg);
else
return processDefault(argument, optionName, optionArg);
} }
return false; return false;
} }
@@ -80,9 +84,15 @@ void OptionProcessor::checkRequired() const
{ {
for (OptionSet::Iterator it = _options.begin(); it != _options.end(); ++it) for (OptionSet::Iterator it = _options.begin(); it != _options.end(); ++it)
{ {
if (it->required() && _specifiedOptions.find(it->fullName()) == _specifiedOptions.end()) if (it->required() && _specifiedOptions.find(it->fullName()) == _specifiedOptions.end())
throw MissingOptionException(it->fullName()); throw MissingOptionException(it->fullName());
} }
if (!_deferredOption.empty())
{
std::string optionArg;
const Option& option = _options.getOption(_deferredOption, false);
option.process(_deferredOption, optionArg); // will throw MissingArgumentException
}
} }
@@ -133,9 +143,20 @@ bool OptionProcessor::processDefault(const std::string& argument, std::string& o
bool OptionProcessor::processCommon(const std::string& optionStr, bool isShort, std::string& optionName, std::string& optionArg) bool OptionProcessor::processCommon(const std::string& optionStr, bool isShort, std::string& optionName, std::string& optionArg)
{ {
if (optionStr.empty()) throw EmptyOptionException(); if (!_deferredOption.empty())
const Option& option = _options.getOption(optionStr, isShort); {
const std::string& group = option.group(); const Option& option = _options.getOption(_deferredOption, false);
std::string optionWithArg(_deferredOption);
_deferredOption.clear();
optionWithArg += '=';
optionWithArg += optionStr;
option.process(optionWithArg, optionArg);
optionName = option.fullName();
return true;
}
if (optionStr.empty()) throw EmptyOptionException();
const Option& option = _options.getOption(optionStr, isShort);
const std::string& group = option.group();
if (!group.empty()) if (!group.empty())
{ {
if (_groups.find(group) != _groups.end()) if (_groups.find(group) != _groups.end())
@@ -143,12 +164,17 @@ bool OptionProcessor::processCommon(const std::string& optionStr, bool isShort,
else else
_groups.insert(group); _groups.insert(group);
} }
if (_specifiedOptions.find(option.fullName()) != _specifiedOptions.end() && !option.repeatable()) if (_specifiedOptions.find(option.fullName()) != _specifiedOptions.end() && !option.repeatable())
throw DuplicateOptionException(option.fullName()); throw DuplicateOptionException(option.fullName());
_specifiedOptions.insert(option.fullName()); _specifiedOptions.insert(option.fullName());
option.process(optionStr, optionArg); if (option.argumentRequired() && ((!isShort && optionStr.find_first_of(":=") == std::string::npos) || (isShort && optionStr.length() == option.shortName().length())))
optionName = option.fullName(); {
return true; _deferredOption = option.fullName();
return true;
}
option.process(optionStr, optionArg);
optionName = option.fullName();
return true;
} }

View File

@@ -51,38 +51,85 @@ namespace Poco {
namespace Util { namespace Util {
XMLConfiguration::XMLConfiguration() XMLConfiguration::XMLConfiguration():
_delim('.')
{ {
} }
XMLConfiguration::XMLConfiguration(Poco::XML::InputSource* pInputSource) XMLConfiguration::XMLConfiguration(char delim):
_delim(delim)
{ {
load(pInputSource);
} }
XMLConfiguration::XMLConfiguration(std::istream& istr) XMLConfiguration::XMLConfiguration(Poco::XML::InputSource* pInputSource):
_delim('.')
{ {
load(istr); load(pInputSource);
} }
XMLConfiguration::XMLConfiguration(const std::string& path) XMLConfiguration::XMLConfiguration(Poco::XML::InputSource* pInputSource, char delim):
_delim(delim)
{ {
load(path); load(pInputSource);
} }
XMLConfiguration::XMLConfiguration(const Poco::XML::Document* pDocument) XMLConfiguration::XMLConfiguration(std::istream& istr):
_delim('.')
{ {
load(pDocument); load(istr);
} }
XMLConfiguration::XMLConfiguration(const Poco::XML::Node* pNode) XMLConfiguration::XMLConfiguration(std::istream& istr, char delim):
_delim(delim)
{ {
load(pNode); load(istr);
}
XMLConfiguration::XMLConfiguration(const std::string& path):
_delim('.')
{
load(path);
}
XMLConfiguration::XMLConfiguration(const std::string& path, char delim):
_delim(delim)
{
load(path);
}
XMLConfiguration::XMLConfiguration(const Poco::XML::Document* pDocument):
_delim('.')
{
load(pDocument);
}
XMLConfiguration::XMLConfiguration(const Poco::XML::Document* pDocument, char delim):
_delim(delim)
{
load(pDocument);
}
XMLConfiguration::XMLConfiguration(const Poco::XML::Node* pNode):
_delim('.')
{
load(pNode);
}
XMLConfiguration::XMLConfiguration(const Poco::XML::Node* pNode, char delim):
_delim(delim)
{
load(pNode);
} }
@@ -294,10 +341,10 @@ Poco::XML::Node* XMLConfiguration::findNode(const std::string& key)
} }
Poco::XML::Node* XMLConfiguration::findNode(std::string::const_iterator& it, const std::string::const_iterator& end, Poco::XML::Node* pNode, bool create) Poco::XML::Node* XMLConfiguration::findNode(std::string::const_iterator& it, const std::string::const_iterator& end, Poco::XML::Node* pNode, bool create) const
{ {
if (pNode && it != end) if (pNode && it != end)
{ {
if (*it == '[') if (*it == '[')
{ {
++it; ++it;
@@ -336,15 +383,15 @@ Poco::XML::Node* XMLConfiguration::findNode(std::string::const_iterator& it, con
if (it != end) ++it; if (it != end) ++it;
return findNode(it, end, findElement(Poco::NumberParser::parse(index), pNode, create), create); return findNode(it, end, findElement(Poco::NumberParser::parse(index), pNode, create), create);
} }
} }
else else
{ {
while (it != end && *it == '.') ++it; while (it != end && *it == _delim) ++it;
std::string key; std::string key;
while (it != end && *it != '.' && *it != '[') key += *it++; while (it != end && *it != _delim && *it != '[') key += *it++;
return findNode(it, end, findElement(key, pNode, create), create); return findNode(it, end, findElement(key, pNode, create), create);
} }
} }
else return pNode; else return pNode;
} }