Utils/Console: Extend enum parsing to ignore case and accept arbitrary unique initial...
g0dil [Tue, 3 Mar 2009 09:39:42 +0000 (09:39 +0000)]
git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@1139 270642c3-0616-0410-b53a-bc976706d245

Utils/Console/Traits.cc
Utils/Console/Traits.hh
Utils/Console/Traits.ih
Utils/Console/Traits.test.cc

index 1f082eb..c1e1cbc 100644 (file)
@@ -39,10 +39,21 @@ prefix_ long senf::console::detail::parseEnum(EnumTable const & table,
     if (tokens.size() != 1)
         throw SyntaxErrorException("parameter syntax error");
 
-    EnumTable::left_map::const_iterator i (table.left.find(tokens.begin()[0].value()));
-    if (i == table.left.end())
-        throw SyntaxErrorException("parameter syntax error: invalid enum value");
-    return i->second;
+    std::string sym (tokens.begin()[0].value());
+    boost::algorithm::to_lower(sym);
+    EnumTable::left_map::const_iterator i1 (table.left.lower_bound(sym));
+    EnumTable::left_map::const_iterator i2 (table.left.lower_bound(sym+"\xff"));
+    if (i1 == i2)
+        throw SyntaxErrorException("parameter syntax error: invalid enum value: ")
+            << tokens.begin()[0].value();
+    long v (i1->second);
+    if (boost::algorithm::to_lower_copy(i1->first) == sym)
+        return v;
+    ++i1;
+    if (i1 != i2)
+        throw SyntaxErrorException("parameter syntax error: ambiguous enum value: ")
+            << tokens.begin()[0].value();
+    return v;
 }
 
 prefix_ std::string senf::console::detail::formatEnum(EnumTable const & table, long value)
index f7fa1e4..df2a6a8 100644 (file)
@@ -184,6 +184,10 @@ namespace console {
         This macro will register an enum type and it's enumerators defined at namespace scope. See
         \ref SENF_CONSOLE_REGISTER_ENUM_MEMBER to register a member enum type.
 
+        \note All enumerator values must be unique ignoring case.
+
+        The enum parser will accept any unique initial substring ignoring case as valid enum value.
+
         \ingroup console_commands
      */
 #   define SENF_CONSOLE_REGISTER_ENUM(Type, Values) \
index b9f3b1b..bac962a 100644 (file)
@@ -33,6 +33,7 @@
 #include <boost/preprocessor/facilities/empty.hpp>
 #include <boost/bimap.hpp>
 #include <boost/assign/list_inserter.hpp>
+#include <boost/algorithm/string/case_conv.hpp>
 #include "../../Utils/singleton.hh"
 
 ///////////////////////////////ih.p////////////////////////////////////////
@@ -42,45 +43,41 @@ namespace console {
 namespace detail {
 
 #ifndef DOXYGEN
+    struct StringILess
+    {
+        bool operator()(std::string const & left, std::string const & right) const
+            { return boost::algorithm::to_lower_copy(left) 
+                  < boost::algorithm::to_lower_copy(right); }
+    };
 
-    typedef boost::bimap<std::string, long> EnumTable;
+    typedef boost::bimap<boost::bimaps::set_of<std::string, StringILess>, long> EnumTable;
 
     long parseEnum(EnumTable const & table, ParseCommandInfo::TokensRange const & tokens);
     std::string formatEnum(EnumTable const & table, long value);
 
-    template <class EnumType>
-    struct EnumTraits : public senf::singleton< EnumTraits<EnumType> >
-    {
-        using senf::singleton< EnumTraits<EnumType> >::instance;
-        EnumTable table;
-    };
-
 #   define SENF_CONSOLE_REGISTER_ENUM_ELT(r,d,e) \
-        (BOOST_PP_STRINGIZE(e), static_cast<long>(d e))
+        ( BOOST_PP_STRINGIZE(e), static_cast<long>(d e) )
 
 #   define SENF_CONSOLE_REGISTER_ENUM_(Prefix, Type, Values)                                      \
-        void senf_console_init_enum_table(Prefix Type)                                            \
+        senf::console::detail::EnumTable & senf_console_enum_table(Prefix Type)                   \
         {                                                                                         \
-            senf::console::detail::EnumTraits<Prefix Type> & traits (                             \
-                senf::console::detail::EnumTraits<Prefix Type>::instance() );                     \
-            if (traits.table.empty())                                                             \
-                boost::assign::insert(traits.table)                                               \
+            static senf::console::detail::EnumTable table;                                        \
+            if (table.empty())                                                                    \
+                boost::assign::insert(table)                                                      \
                     BOOST_PP_SEQ_FOR_EACH( SENF_CONSOLE_REGISTER_ENUM_ELT, Prefix, Values );      \
+            return table;                                                                         \
         }                                                                                         \
         void senf_console_parse_argument(                                                         \
             senf::console::ParseCommandInfo::TokensRange const & tokens, Prefix Type & out)       \
         {                                                                                         \
-            senf_console_init_enum_table( Prefix Type() );                                        \
             out = static_cast<Prefix Type>(                                                       \
                 senf::console::detail::parseEnum(                                                 \
-                    senf::console::detail::EnumTraits<Prefix Type>::instance().table, tokens));   \
+                    senf_console_enum_table( Prefix Type() ), tokens) );                          \
         }                                                                                         \
         void senf_console_format_value(Prefix Type value, std::ostream & os)                      \
         {                                                                                         \
-            senf_console_init_enum_table( Prefix Type() );                                        \
             os << senf::console::detail::formatEnum(                                              \
-                senf::console::detail::EnumTraits<Prefix Type>::instance().table,                 \
-                static_cast<long>(value) );                                                       \
+                senf_console_enum_table( Prefix Type() ), static_cast<long>(value) );             \
         }
 
 #endif
index 054f993..2e54245 100644 (file)
@@ -40,8 +40,8 @@
 ///////////////////////////////cc.p////////////////////////////////////////
 
 namespace {
-    enum TestEnum { Foo, Bar };
-    SENF_CONSOLE_REGISTER_ENUM( TestEnum, (Foo)(Bar) );
+    enum TestEnum { Foo, Bar, FooBar };
+    SENF_CONSOLE_REGISTER_ENUM( TestEnum, (Foo)(Bar)(FooBar) );
 
     TestEnum test (TestEnum value) { return value; }
 
@@ -139,6 +139,24 @@ BOOST_AUTO_UNIT_TEST(enumSupport)
         parser.parse("test/member MemberBar",
                      boost::bind<void>( boost::ref(executor), boost::ref(ss), _1 )) );
     BOOST_CHECK_EQUAL( ss.str(), "MemberBar\n" );
+
+    ss.str("");
+    SENF_CHECK_NO_THROW(
+        parser.parse("test/test foob",
+                     boost::bind<void>( boost::ref(executor), boost::ref(ss), _1 )) );
+    BOOST_CHECK_EQUAL( ss.str(), "FooBar\n" );
+
+    ss.str("");
+    SENF_CHECK_NO_THROW(
+        parser.parse("test/test b",
+                     boost::bind<void>( boost::ref(executor), boost::ref(ss), _1 )) );
+    BOOST_CHECK_EQUAL( ss.str(), "Bar\n" );
+
+    ss.str("");
+    SENF_CHECK_NO_THROW(
+        parser.parse("test/test foo",
+                     boost::bind<void>( boost::ref(executor), boost::ref(ss), _1 )) );
+    BOOST_CHECK_EQUAL( ss.str(), "Foo\n" );
 }