Console: Extended boolean parsing / formatting
g0dil [Fri, 30 May 2008 16:23:49 +0000 (16:23 +0000)]
git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@864 270642c3-0616-0410-b53a-bc976706d245

Console/Mainpage.dox
Console/Server.ih
Console/Traits.cci [new file with mode: 0644]
Console/Traits.hh
Console/Traits.test.cc

index 46f8a05..b0440b0 100644 (file)
@@ -29,6 +29,7 @@
 
     \autotoc
 
+
     \section console_intro Introduction
 
     There are three parts to the Config/console library:
@@ -47,6 +48,7 @@
     commands which set their respective parameter, however the library allows commands to do much
     more than just that.
 
+
     \section console_example Example
 
     The following example shows a \e very short summary on how to integrate the config/console
 
     \see \ref console_testserver for a complete example application
 
+
     \section intro_usage Access
 
     There are several ways to access the node tree:
 
     \see console_access
 
+
     \section intro_nodes The node tree
 
     The basic idea is, that the console/config library manages a directory structure of parameters
 
     \autotoc
 
+
     \section console_access_config Configuration support
 
     The configuration support of the Console/Config library revolves around the ConfigSource
     node used during parsing and it is also possible to restrict parsing to a command subset. See
     \ref console_access_partial.
 
+
     \subsection console_access_file Configuration files
 
     <table class="senf fixedwidth">
     and a user specific configuration file) see \ref console_access_multiple and add one (or more)
     senf::console::FileConfig() source to a senf::console::ConfigBundle.
 
+
     \subsubsection console_access_file_syntax Configuration file syntax
 
     Configuration files are written in a simple configuration language. This language is almost
 
     \see \ref console_parser
 
+
     \subsection console_access_options Command line options
 
     <table class="senf fixedwidth">
     See \ref senf::console::ProgramOptions for the source specific additional parameters. These
     apply to senf::console::ProgramOptions and to the senf::console::OptionsConfig() source.
 
+
     \subsubsection console_access_options_syntax Options syntax
 
     Command line options are primarily parsed as long-options. Long options start with '--'. Further
 
     (Beware, that the second argument to \c alias() is \e not shell quoted). 
 
+
     \subsection console_access_root Changing the root node
 
     When used in it's default state, parsing will always interpret all commands relative to the
     selectively choose commands from the node tree which are to be made accessible for
     configuration. See \ref node_tree.
 
+
     \subsection console_access_partial Partial / incremental configuration
 
     Another feature provided by senf::console::ConfigBundle and all helper classes is partial
     important: It allows a subsystem to parse it's configuration parameters irrespective of any
     links pointing to nodes of that subsystem.
 
+
     \subsection console_access_multiple Multiple sources
 
     Most of the time, an application will utilize multiple configuration sources: A global
     order they are specified, so in this case, the command line options will override any options
     specified in one of the configuration files.
 
+
     \section console_access_console The network console
 
     To make the network console accessible, it must be initialized when the program is started:
     </pre>
     \endhtmlonly
 
+    It is possible to start multiple server consoles by calling \c start() multiple times with
+    different ports/addresses. Each server can be configured separately (e.g. root node, mode ...).q
+
+
     \subsection console_serverclient Server and Client objects
 
     The senf::console::Server and senf::console::Client objects offer further API calls. To access
     a path component will be completed automatically and transparently to the corresponding full
     name.
 
+
     \subsection console_noninteractive Non-interactive network console
 
     After a new connection is established, the console server waits a short time for data to arrive.
 
     \autotoc
 
+
     \section console_cmdadd Adding commands and setting attributes
 
     Basically, all commands are added using senf::console::DirectoryNode::add(). What exactly
     
     To greatly simplify parsing complex commands, we turn to automatic argument parsing. 
 
+
     \subsection console_autoadd Adding
 
     Automatically parsed commands are registered by just adding a callback which has the correct
     </pre>
     \endhtmlonly
 
+
     \subsection command_overload Overloading
 
     Automatically parsed commands can be overloaded: You can register multiple commands under the
 
     \see senf::console::VariableAttributor for the complete attribute interface
 
+
     \subsection console_varchange Change notification
 
     A \e handler can be set to be called, whenever the variable is changed. It will be called with a
     After this setup, \c varChanged will be called, whenever the value has changed.
 
 
-    \section console_args Registering special argument types
+    \section console_args Special argument types
 
     By default, argument types which can be read and written using \c iostreams are automatically
     supported. Other types need to be registered explicitly
 
 
+    \subsection console_args_bool Boolean arguments and return values
+
+    The console library by default formats boolean values using the strings \c true and \c false for
+    their representation. When parsing a boolean value, most sensible representations will be
+    accepted:
+    
+    <table class="senf">
+    <tr><td>\c true</td>    <td>\c false</td>    <td>\ref senf::console::formatTrueFalse</td></tr>
+    <tr><td>\c on</td>      <td>\c off</td>      <td>\ref senf::console::formatOnOff</td></tr>
+    <tr><td>\c enabled</td> <td>\c disabled</td> <td>\ref senf::console::formatEnabledDisabled</td></tr>
+    <tr><td>\c yes</td>     <td>\c no</td>       <td>\ref senf::console::formatYesNo</td></tr>
+    <tr><td><em>non-zero integer</em></td><td>\c 0</td><td>\ref senf::console::formatOneZero</td></tr>
+    </table>
+
+    The boolean parser will accept these values in any (mixed) case and accepts any unique initial
+    substring (e.g. \c Y / \c N).
+
+    The last column lists explicit formatters which can be set to customize the return value
+    formatting of a registered overload accordingly.
+
+    
     \subsection console_args_enum Registering enum types
 
     Enum types are a special case, since it is not possible, to find a string representation for the
     \endhtmlonly
 
 
-    \subsection console_args_custom Customizing argument and return value parsing/formatting
+    \subsection console_args_custom Extending the library to support additional types
 
     To support or customize parsing/formatting of other types, they need to be registered. In it's
     simplest case, this works, by just providing an appropriate overload for
index b107867..925fd64 100644 (file)
@@ -62,6 +62,8 @@ namespace detail {
     /** \brief Internal: Nonblocking boost::iostreams::sink
 
         The sink discards data if the output socket would.
+
+        \fixme Don't throw exceptions ... set stream error indicator (if at all)
      */
     class NonblockingSocketSink 
         : public boost::iostreams::sink
diff --git a/Console/Traits.cci b/Console/Traits.cci
new file mode 100644 (file)
index 0000000..6625e60
--- /dev/null
@@ -0,0 +1,119 @@
+// $Id$
+//
+// Copyright (C) 2008 
+// Fraunhofer Institute for Open Communication Systems (FOKUS)
+// Competence Center NETwork research (NET), St. Augustin, GERMANY
+//     Stefan Bund <g0dil@berlios.de>
+//
+// This program is free software; you can redistribute it and/or modify
+// it under the terms of the GNU General Public License as published by
+// the Free Software Foundation; either version 2 of the License, or
+// (at your option) any later version.
+//
+// This program is distributed in the hope that it will be useful,
+// but WITHOUT ANY WARRANTY; without even the implied warranty of
+// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+// GNU General Public License for more details.
+//
+// You should have received a copy of the GNU General Public License
+// along with this program; if not, write to the
+// Free Software Foundation, Inc.,
+// 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+
+/** \file
+    \brief Traits inline non-template implementation */
+
+#include "Traits.ih"
+
+// Custom includes
+#include <boost/algorithm/string/predicate.hpp>
+
+#define prefix_ inline
+///////////////////////////////cci.p///////////////////////////////////////
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::ArgumentTraits<bool>
+
+prefix_ void
+senf::console::ArgumentTraits<bool>::parse(ParseCommandInfo::TokensRange const & tokens,
+                                           bool & out)
+{
+    if (tokens.size() != 1)
+        throw SyntaxErrorException("argument syntax error");
+
+    if ( boost::istarts_with(std::string("true"), tokens.begin()->value())
+         || boost::istarts_with(std::string("enabled"), tokens.begin()->value())
+         || boost::istarts_with(std::string("yes"), tokens.begin()->value())
+         || boost::iequals(std::string("on"), tokens.begin()->value()) )
+        out = true;
+    else if (boost::istarts_with(std::string("false"), tokens.begin()->value())
+             || boost::istarts_with(std::string("disabled"), tokens.begin()->value())
+             || boost::istarts_with(std::string("no"), tokens.begin()->value())
+             || (boost::istarts_with(std::string("off"), tokens.begin()->value())
+                 && tokens.begin()->value().size() >= 2) )
+        out = false;
+    else {
+        int v (0);
+        senf::console::parse(tokens, v);
+        out = v;
+    }
+}
+
+prefix_ std::string senf::console::ArgumentTraits<bool>::description()
+{
+    return "bool";
+}
+
+prefix_ std::string senf::console::ArgumentTraits<bool>::str(bool value)
+{
+    return value ? "true" : "false";
+}
+
+///////////////////////////////////////////////////////////////////////////
+// senf::console::ReturnValueTraits<bool>
+
+prefix_ void senf::console::ReturnValueTraits<bool>::format(bool value, std::ostream & os)
+{
+    formatTrueFalse(value, os);
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+prefix_ void senf::console::formatTrueFalse(bool value, std::ostream & os)
+{
+    os << (value ? "true" : "false");
+}
+
+prefix_ void senf::console::formatYesNo(bool value, std::ostream & os)
+{
+    os << (value ? "yes" : "no");
+}
+
+prefix_ void senf::console::formatEnabledDisabled(bool value, std::ostream & os)
+{
+    os << (value ? "enabled" : "disabled");
+}
+
+prefix_ void senf::console::formatOnOff(bool value, std::ostream & os)
+{
+    os << (value ? "on" : "off");
+}
+
+prefix_ void senf::console::formatOneZero(bool value, std::ostream & os)
+{
+    os << (value ? "0" : "1");
+}
+
+///////////////////////////////cci.e///////////////////////////////////////
+#undef prefix_
+
+\f
+// Local Variables:
+// mode: c++
+// fill-column: 100
+// comment-column: 40
+// c-file-style: "senf"
+// indent-tabs-mode: nil
+// ispell-local-dictionary: "american"
+// compile-command: "scons -u test"
+// End:
index 69533b4..b647dd4 100644 (file)
@@ -133,6 +133,44 @@ namespace console {
     template <class Type>
     void parse(ParseCommandInfo::TokensRange const & tokens, Type & out);
 
+#ifndef DOXYGEN
+
+    // Parse bool: true/false, yes/no, enabled/disabled, 0/1
+    template <>
+    struct ArgumentTraits<bool>
+    {
+        typedef bool type;
+
+        static void parse(ParseCommandInfo::TokensRange const & tokens, bool & out);
+        static std::string description();
+        static std::string str(bool value);
+    };
+
+    template <>
+    struct ReturnValueTraits<bool>
+    {
+        typedef bool type;
+
+        static void format(bool value, std::ostream & os);
+    };
+
+#endif
+
+    /** \brief Format boolean value as \c true / \c false */
+    void formatTrueFalse(bool value, std::ostream & os);
+
+    /** \brief Format boolean value as \c yes / \c no */
+    void formatYesNo(bool value, std::ostream & os);
+
+    /** \brief Format boolean value as \c enabled / \c disabled */
+    void formatEnabledDisabled(bool value, std::ostream & os);
+    
+    /** \brief Format boolean value as \c on / \c off */
+    void formatOnOff(bool value, std::ostream & os);
+
+    /** \brief Format boolean value as \c 1 / \c 0 */
+    void formatOneZero(bool value, std::ostream & os);
+
     /** \brief Register enum type for argument parsing
 
         Enum types need to be registered explicitly to support parsing. 
@@ -169,7 +207,7 @@ namespace console {
 }}
 
 ///////////////////////////////hh.e////////////////////////////////////////
-//#include "Traits.cci"
+#include "Traits.cci"
 #include "Traits.ct"
 #include "Traits.cti"
 #endif
index b11c4b5..bc122ea 100644 (file)
@@ -50,7 +50,49 @@ namespace {
         static MemberEnum test (MemberEnum value) { return value; }
     };
     SENF_CONSOLE_REGISTER_ENUM_MEMBER( TestClass, MemberEnum, (MemberFoo)(MemberBar) );
-    
+
+    bool boolTest(bool value) { return value; }
+}
+
+BOOST_AUTO_UNIT_TEST(boolTraits)
+{
+    senf::console::Executor executor;
+    senf::console::CommandParser parser;
+    senf::console::ScopedDirectory<> dir;
+    senf::console::root().add("test", dir);
+
+    dir.add("test", &boolTest);
+
+    std::stringstream ss;
+    BOOST_CHECK_NO_THROW(
+        parser.parse("test/test true; test/test false",
+                     boost::bind<void>( boost::ref(executor), boost::ref(ss), _1 )) );
+    BOOST_CHECK_EQUAL( ss.str(), "true\n" "false\n" );
+
+    ss.str("");
+    BOOST_CHECK_NO_THROW(
+        parser.parse("test/test enabled; test/test disabled",
+                     boost::bind<void>( boost::ref(executor), boost::ref(ss), _1 )) );
+    BOOST_CHECK_EQUAL( ss.str(), "true\n" "false\n" );
+
+    ss.str("");
+    BOOST_CHECK_NO_THROW(
+        parser.parse("test/test yes; test/test no",
+                     boost::bind<void>( boost::ref(executor), boost::ref(ss), _1 )) );
+    BOOST_CHECK_EQUAL( ss.str(), "true\n" "false\n" );
+
+    ss.str("");
+    BOOST_CHECK_NO_THROW(
+        parser.parse("test/test Y; test/test enA",
+                     boost::bind<void>( boost::ref(executor), boost::ref(ss), _1 )) );
+    BOOST_CHECK_EQUAL( ss.str(), "true\n" "true\n" );
+
+    dir.add("test2", &boolTest).formatter( senf::console::formatEnabledDisabled );
+    ss.str("");
+    BOOST_CHECK_NO_THROW(
+        parser.parse("test/test2 0; test/test2 -1",
+                     boost::bind<void>( boost::ref(executor), boost::ref(ss), _1 )) );
+    BOOST_CHECK_EQUAL( ss.str(), "disabled\n" "enabled\n" );
 }
 
 BOOST_AUTO_UNIT_TEST(enumSupport)