Console: Add lots of documentation
g0dil [Wed, 26 Mar 2008 23:31:13 +0000 (23:31 +0000)]
Console: Code cleanup
Console: Add automatic node naming

git-svn-id: https://svn.berlios.de/svnroot/repos/senf/trunk@767 270642c3-0616-0410-b53a-bc976706d245

12 files changed:
Console/Executor.cc
Console/Executor.hh
Console/Node.cc
Console/Node.cci
Console/Node.cti
Console/Node.hh
Console/ObjectDirectory.cti
Console/ObjectDirectory.hh
Console/Parse.hh
Console/Parse.ih
Console/Server.hh
Console/testServer.cc

index b808a09..517c46c 100644 (file)
@@ -46,7 +46,7 @@ namespace {
 ///////////////////////////////////////////////////////////////////////////
 // senf::console::Executor
 
-prefix_ bool senf::console::Executor::operator()(ParseCommandInfo const & command,
+prefix_ void senf::console::Executor::operator()(ParseCommandInfo const & command,
                                                  std::ostream & output)
 {
     SENF_LOG(( "Executing: " << command ));
@@ -57,7 +57,7 @@ prefix_ bool senf::console::Executor::operator()(ParseCommandInfo const & comman
     try {
         switch(command.builtin()) {
         case ParseCommandInfo::NoBuiltin :
-            traverseToCommand(command.commandPath())(output, command.arguments());
+            traverseCommand(command.commandPath())(output, command.arguments());
             break;
 
         case ParseCommandInfo::BuiltinCD :
@@ -72,14 +72,15 @@ prefix_ bool senf::console::Executor::operator()(ParseCommandInfo const & comman
                 }
                 else {
                     oldCwd_ = cwd_;
-                    cwd_ = traverseTo(command.arguments().begin()[0]).thisptr();
+                    cwd_ = traverseDirectory(command.arguments().begin()[0]).thisptr();
                 }
             }
             break;
             
         case ParseCommandInfo::BuiltinLS : {
-            DirectoryNode const & dir (
-                command.arguments().empty() ? cwd() : traverseTo(command.arguments().begin()[0]));
+            DirectoryNode const & dir ( command.arguments()
+                                        ? traverseDirectory(command.arguments().begin()[0])
+                                        : cwd() );
             for (DirectoryNode::child_iterator i (dir.children().begin());
                  i != dir.children().end(); ++i) {
                 output << i->first;
@@ -93,7 +94,7 @@ prefix_ bool senf::console::Executor::operator()(ParseCommandInfo const & comman
         case ParseCommandInfo::BuiltinPUSHD :
             dirstack_.push_back(cwd_);
             if ( command.arguments() )
-                cwd_ = traverseTo(command.arguments().begin()[0]).thisptr();
+                cwd_ = traverseDirectory(command.arguments().begin()[0]).thisptr();
             break;
             
         case ParseCommandInfo::BuiltinPOPD :
@@ -107,52 +108,60 @@ prefix_ bool senf::console::Executor::operator()(ParseCommandInfo const & comman
             throw ExitException();
 
         case ParseCommandInfo::BuiltinHELP :
-            try {
-                GenericNode & node (
-                    command.arguments() 
-                    ? cwd().traverse(
-                        boost::make_iterator_range(
-                            boost::make_transform_iterator(command.arguments().begin()[0].begin(), TraverseTokens()),
-                            boost::make_transform_iterator(command.arguments().begin()[0].end(), TraverseTokens())))
-                    : cwd() );
-                node.help(output);
-                output << std::flush;
-            }
-            catch (UnknownNodeNameException &) {
-                output << "invalid path" << std::endl;
-            }
+            GenericNode const & node (command.arguments() 
+                                      ? traverseNode(command.arguments().begin()[0])
+                                      : cwd());
+            output << prettyName(typeid(node)) << " at " << node.path() << "\n\n";
+            node.help(output);
+            output << std::flush;
             break;
+
         }
     }
+    catch (InvalidPathException &) {
+        output << "invalid path" << std::endl;
+    }
     catch (InvalidDirectoryException &) {
         output << "invalid directory" << std::endl;
     }
     catch (InvalidCommandException &) {
         output << "invalid command" << std::endl;
     }
-    return true;
+}
+
+prefix_ senf::console::GenericNode &
+senf::console::Executor::traverseNode(ParseCommandInfo::argument_value_type const & path)
+{
+    try {
+        return cwd().traverse(
+            boost::make_iterator_range(
+                boost::make_transform_iterator(path.begin(), TraverseTokens()),
+                boost::make_transform_iterator(path.end(), TraverseTokens())));
+    }
+    catch (std::bad_cast &) {
+        throw InvalidPathException();
+    }
+    catch (UnknownNodeNameException &) {
+        throw InvalidPathException();
+    }
 }
 
 prefix_ senf::console::DirectoryNode &
-senf::console::Executor::traverseTo (ParseCommandInfo::argument_value_type const & path)
+senf::console::Executor::traverseDirectory(ParseCommandInfo::argument_value_type const & path)
 {
     try {
-        return dynamic_cast<DirectoryNode&>(
-            cwd().traverse(
-                boost::make_iterator_range(
-                    boost::make_transform_iterator(path.begin(), TraverseTokens()),
-                    boost::make_transform_iterator(path.end(), TraverseTokens()))));
+        return dynamic_cast<DirectoryNode&>( traverseNode(path) );
     }
     catch (std::bad_cast &) {
         throw InvalidDirectoryException();
     }
-    catch (UnknownNodeNameException &) {
+    catch (InvalidPathException &) {
         throw InvalidDirectoryException();
     }
 }
 
 prefix_ senf::console::CommandNode &
-senf::console::Executor::traverseToCommand(ParseCommandInfo::CommandPathRange const & path)
+senf::console::Executor::traverseCommand(ParseCommandInfo::CommandPathRange const & path)
 {
     try {
         return dynamic_cast<CommandNode &>( cwd().traverse(path) );
index 189eece..93071ac 100644 (file)
 namespace senf {
 namespace console {
 
-    /** \brief
+    /** \brief Execute config/console commands
+
+        The Executor interprets parsed config/console commands and executes them. It manages the
+        current execution context (current directory, directory stack and so on).
+
+        The executor is normally called directly by the parser callback for each command.
+
+        Executing the built-in 'exit' command will throw Executor::ExitException. This exception
+        (which is not derived from std::exception since it's not a real exception) must be handled
+        by the caller.
+
+        All directories are managed using weak pointers. If any of the directories expires (current
+        directory, directory stack, last directory) it will be replaced with the root
+        directory. Directories expire when they are destructed or when they are detached from the
+        config tree root node.
       */
     class Executor
         : boost::noncopyable
@@ -51,7 +65,7 @@ namespace console {
 
         typedef boost::iterator_range< ParseCommandInfo::argument_iterator> Arguments;
 
-        struct ExitException {}; // NOT derived from std::exception !
+        struct ExitException {};        ///< Thrown by built-in 'exit' command
 
         ///////////////////////////////////////////////////////////////////////////
         //\/name Structors and default members
@@ -62,15 +76,19 @@ namespace console {
         ///\}
         ///////////////////////////////////////////////////////////////////////////
 
-        bool operator()(ParseCommandInfo const & command, std::ostream & output);
-        DirectoryNode & cwd() const;
+        void operator()(ParseCommandInfo const & command, std::ostream & output);
+                                        ///< Execute command
+                                        /**< Output will be written to \a output. */
+        DirectoryNode & cwd() const;    ///< Current working directory
 
     protected:
 
     private:
-        DirectoryNode & traverseTo(ParseCommandInfo::argument_value_type const & path);
-        CommandNode & traverseToCommand(ParseCommandInfo::CommandPathRange const & path);
+        GenericNode & traverseNode(ParseCommandInfo::argument_value_type const & path);
+        DirectoryNode & traverseDirectory(ParseCommandInfo::argument_value_type const & path);
+        CommandNode & traverseCommand(ParseCommandInfo::CommandPathRange const & path);
 
+        struct InvalidPathException {};
         struct InvalidDirectoryException {};
         struct InvalidCommandException {};
 
index f21ab66..f74a37b 100644 (file)
@@ -73,20 +73,27 @@ senf::console::DirectoryNode::remove(std::string const & name)
         throw UnknownNodeNameException() << ": '" << name << "'";
     GenericNode::ptr node (i->second);
     children_.erase(i);
+    node->parent_ = 0;
     return node;
 }
 
 prefix_ void senf::console::DirectoryNode::add(GenericNode::ptr node)
 {
     BOOST_ASSERT( ! node->parent() );
+    if (node->name().empty()) {
+        node->name("unnamed");
+        SENF_LOG((senf::log::MESSAGE)("Adding 'unnamed' node"));
+    }
     if (children_.find(node->name()) != children_.end()) {
         unsigned suffix (0);
         std::string newName;
         do {
             ++suffix;
-            newName = node->name() + boost::lexical_cast<std::string>(suffix);
+            newName = node->name() + "-" + boost::lexical_cast<std::string>(suffix);
         } while (children_.find(newName) != children_.end());
-        name(*node, newName);
+        SENF_LOG((senf::log::MESSAGE)("Uniquifying node '" << node->name() << "' to '" 
+                                      << newName << "'"));
+        node->name(newName);
     }
     children_.insert(std::make_pair(node->name(),node));
     node->parent_ = this;
index 915d954..43954f3 100644 (file)
@@ -52,11 +52,6 @@ prefix_ void senf::console::GenericNode::name(std::string const & name)
     name_ = name;
 }
 
-prefix_ void senf::console::GenericNode::name(GenericNode & node, std::string const & name)
-{
-    node.name_ = name;
-}
-
 prefix_ boost::shared_ptr<senf::console::DirectoryNode> senf::console::GenericNode::parent()
     const
 {
@@ -66,8 +61,10 @@ prefix_ boost::shared_ptr<senf::console::DirectoryNode> senf::console::GenericNo
 
 prefix_ senf::console::GenericNode::ptr senf::console::GenericNode::unlink()
 {
-    SENF_ASSERT( parent() );
-    return parent()->remove(name());
+    if (parent_)
+        return parent()->remove(name());
+    else
+        return thisptr();
 }
 
 prefix_ void senf::console::GenericNode::help(std::ostream & output)
@@ -90,10 +87,9 @@ prefix_ senf::console::GenericNode::cptr senf::console::GenericNode::thisptr()
 ///////////////////////////////////////////////////////////////////////////
 // senf::console::DirectoryNode
 
-prefix_ std::auto_ptr<senf::console::DirectoryNode>
-senf::console::DirectoryNode::create()
+prefix_ senf::console::DirectoryNode::ptr senf::console::DirectoryNode::create()
 {
-    return std::auto_ptr<DirectoryNode>(new DirectoryNode());
+    return ptr(new DirectoryNode());
 }
 
 prefix_ senf::console::DirectoryNode &
@@ -113,8 +109,7 @@ senf::console::DirectoryNode::operator()(std::string const & name)
 prefix_ senf::console::DirectoryNode &
 senf::console::DirectoryNode::mkdir(std::string const & name)
 {
-    std::auto_ptr<DirectoryNode> node (create());
-    return add(name, node);
+    return add(name, create());
 }
 
 prefix_ senf::console::DirectoryNode::ChildrenRange senf::console::DirectoryNode::children()
@@ -174,10 +169,10 @@ prefix_ senf::console::SimpleCommandNode::SimpleCommandNode(Function const & fn)
     : fn_ (fn)
 {}
 
-prefix_ std::auto_ptr<senf::console::SimpleCommandNode>
+prefix_ senf::console::SimpleCommandNode::ptr
 senf::console::SimpleCommandNode::create(Function const & fn)
 {
-    return std::auto_ptr<SimpleCommandNode>(new SimpleCommandNode(fn));
+    return ptr(new SimpleCommandNode(fn));
 }
 
 prefix_ senf::console::SimpleCommandNode &
index c56f014..ef80237 100644 (file)
@@ -47,16 +47,6 @@ senf::console::NodeCreateTraits<Object>::Creator::create(DirectoryNode & node,
 
 template <class NodeType>
 prefix_ NodeType & senf::console::DirectoryNode::add(std::string const & name,
-                                                     std::auto_ptr<NodeType> node)
-{
-    GenericNode::ptr p (node);
-    p->name(name);
-    add(p);
-    return static_cast<NodeType &>(*p);
-}
-
-template <class NodeType>
-prefix_ NodeType & senf::console::DirectoryNode::add(std::string const & name,
                                                      boost::shared_ptr<NodeType> node)
 {
     SENF_ASSERT( ! node->parent() );
index 10ac02e..cf48403 100644 (file)
@@ -37,6 +37,7 @@
 #include <boost/type_traits/remove_reference.hpp>
 #include "../Utils/Exception.hh"
 #include "../Utils/mpl.hh"
+#include "../Utils/Logger/SenfLog.hh"
 #include "Parse.hh"
 
 //#include "Node.mpp"
@@ -50,11 +51,25 @@ namespace console {
     class DirectoryNode;
     class CommandNode;
 
-    /** \brief
+    /** \brief Config/console node tree base-class
+
+        GenericNode is the base class of all node objects. There are two basic node types derived
+        from GenericNode:  DirectoryNode and CommandNode.
+
+        All nodes are dynamically allocated and automatically managed using reference counting.
+
+        All nodes are normally linked into a single tree which root node is
+        senf::console::root(). Nodes may however be orphaned (not linked to the tree) either
+        directly (the node has no parent) or indirectly (the node has a parent but is part of an
+        orphaned subtree which is not linked to the root node).
+
+        Every active (non-orphaned) node (except the root() node) has a non-empty node name. This
+        name is assigned to the node when adding the node to the tree.
       */
     class GenericNode 
         : public boost::enable_shared_from_this<GenericNode>
     {
+        SENF_LOG_CLASS_AREA();
     public:
         ///////////////////////////////////////////////////////////////////////////
         // Types
@@ -67,39 +82,56 @@ namespace console {
 
         virtual ~GenericNode();
 
-        std::string const & name() const;
-        boost::shared_ptr<DirectoryNode> parent() const;
+        std::string const & name() const; ///< Node name
+        boost::shared_ptr<DirectoryNode> parent() const; ///< Parent node
+                                        /**< May be null, if the node is the root node or if it is
+                                             not linked to the tree */
 
-        std::string path() const;
+        std::string path() const;       ///< Node path
+                                        /**< The node path is built by joining the names of all
+                                             parent nodes with '/' chars. */
 
-        ptr unlink();
+        ptr unlink();                   ///< Remove node from it's parent directory
+                                        /**< You may either discard the return value and thereby
+                                             dispose the node or may re-attach the node at some
+                                             other place using DirectoryNode::add(). */
 
-        bool active() const;
+        bool active() const;            ///< \c true, if node is attached to the root() node
 
-        void help(std::ostream & output) const;
+        void help(std::ostream & output) const; /// Write help info to \a output
 
-        ptr thisptr();
-        cptr thisptr() const;
+        ptr thisptr();                  ///< Get smart pointer to node
+        cptr thisptr() const;           ///< Get smart pointer to node (const)
 
     protected:
         GenericNode();
 
         void name(std::string const & name);
-        static void name(GenericNode & node, std::string const & name);
-        void parent(DirectoryNode * parent);
 
+#ifndef DOXYGEN
     private:
+#else
+    public:
+#endif
         virtual void v_help(std::ostream & output) const = 0;
+                                        ///< Provide help information
+                                        /**< This member must be implemented in derived classes
+                                             to provide node specific help information. */
 
+    private:
         std::string name_;
         DirectoryNode * parent_;
 
-        friend class intrusive_refcount_base;
         friend class DirectoryNode;
     };
 
     class SimpleCommandNode;
 
+    /** \brief Internal: Node creation helper traits
+        
+        This class is used internally to find out the type of node to create for a specific argument
+        type. 
+     */
     template <class Object>
     struct NodeCreateTraits
     {
@@ -117,13 +149,29 @@ namespace console {
         };
     };
 
-    /** \brief
-        ///\fixme Provide a default name for added nodes if 'name' is empty ?
+    /** \brief Config/console tree directory node
+
+        This node type provides the internal and root nodes of the tree. It allows to add arbitrary
+        children and supports directory traversal.
+        
+        Nodes are normally not instantiated manually but are created by the DirectoryNode via
+        mkdir() or add(). Special add() members however allow externally allocated node objects.
+
+        Nodes may be added to the tree only once, otherwise chaos will ensue. Since nodes are always
+        managed dynamically, there is a special ObjectDirectory proxy template which provides a
+        DirectoryNode facade. ObjectDirectory is used if a class wants to manage it's own directory
+        as a data member.
+
+        Every node is assigned a (new) name when it is added to a directory. If the directory
+        already has an entry of that name, the name is made unique by appending a suffix of the form
+        '-n' where n is a number starting at 1. If the name is empty, int is set to 'unnamed' and
+        then uniquified as above. Automatically providing unique names simplifies adding
+        configuration/console support to generic components.
       */
     class DirectoryNode : public GenericNode
     {
+        SENF_LOG_CLASS_AREA();
         typedef std::map<std::string, GenericNode::ptr> ChildMap;
-
     public:
         ///////////////////////////////////////////////////////////////////////////
         // Types
@@ -139,7 +187,9 @@ namespace console {
         ///\name Structors and default members
         ///\{
 
-        static std::auto_ptr<DirectoryNode> create();
+        static ptr create();            ///< Create node object.
+                                        /**< You should normally use either mkdir() or
+                                             ObjectDirectory instead of create() */
 
         ///\}
         ///////////////////////////////////////////////////////////////////////////
@@ -147,32 +197,91 @@ namespace console {
         ///\{
 
         template <class NodeType>
-        NodeType & add(std::string const & name, std::auto_ptr<NodeType> node);
-
-        template <class NodeType>
         NodeType & add(std::string const & name, boost::shared_ptr<NodeType> node);
+                                        ///< Add node to tree
+                                        /**< Adds the \a node to the tree as a child of \a this
+                                             node. The node is given the name \a name. If a node of
+                                             that name already exists, a numeric suffix of the form
+                                             '-n' is added to the name until the name is unique. If
+                                             \a name is empty, it is set to 'unnamed'. */
 
         template <class Object>
         typename NodeCreateTraits<Object>::NodeType & add (std::string const & name, 
                                                            Object const & ob);
+                                        ///< Generic child node factory
+                                        /**< This member is used to create a new child node of the
+                                             current directory. The type of node created depends on
+                                             the type of argument passed.
+
+                                             The node type is selected by the NodeCreateTraits
+                                             class. To allow adding a specific node type, you need
+                                             to provide an overload for
+                                             <tt>senf_console_add_node</tt> which must be visible at
+                                             when you register the new node.
+                                             \code
+                                             MyNodeType & senf_console_add_node(
+                                                 DirectoryNode & dir,
+                                                 std::string const & name,
+                                                 MySpecialObject const & ob,
+                                                 int)
+                                             {
+                                                 return dir.add(name, MyNodeType::create(ob));
+                                             }
+                                             \endcode
+                                             (Do not forget the last unnamed 'int' parameter which
+                                             is not used but serves to disambiguate the
+                                             overloads). */
 
         GenericNode::ptr remove(std::string const & name);
+                                        ///< Remove node \a name from the tree
+                                        /**< The returned pointer may either be discarded, which
+                                             will automatically dispose the removed node, or it may
+                                             be saved and/or re-attached at some other place in the
+                                             tree. */
 
         DirectoryNode & operator[](std::string const & name) const;
+                                        ///< Get directory child node
+                                        /**< \throws UnknownNodeNameException if a child \a name
+                                                 does not exist. 
+                                             \throws std::bad_cast if the child \a name is not a
+                                                 directory node. */
+
         CommandNode & operator()(std::string const & name) const;
+                                        ///< Get command child node
+                                        /**< \throws UnknownNodeNameException if a child \a name
+                                                 does not exist
+                                             \throws std::bad_cast if the child \a name is not a
+                                                 command node. */
+
         GenericNode & get(std::string const & name) const;
+                                        ///< Get child node
+                                        /**< \throws UnknownNodeNameException if a child \a name
+                                                 does not exist */
 
         DirectoryNode & mkdir(std::string const & name);
+                                        ///< Create sub-directory node
         
         ChildrenRange children() const;
+                                        ///< Return iterator range over all children.
+                                        /**< The returned range is sorted by child name. */
 
         ///\}
         ///////////////////////////////////////////////////////////////////////////
 
         template <class ForwardRange>
         GenericNode & traverse(ForwardRange const & range);
+                                        ///< Traverse node path starting at this node
+                                        /**< The <tt>FordwareRange::value_type</tt> must be
+                                             (convertible to) std::string. Each range element
+                                             constitutes a step along the node traversal.
+
+                                             If the range starts with an empty element, the
+                                             traversal is started at the root() node, otherwise it
+                                             is started at \a this node. The traversal supports '.',
+                                             '..' and ignores further empty elements. */
 
         DirectoryNode & doc(std::string const & doc);
+                                        ///< Set node documentation
 
         ptr thisptr();
         cptr thisptr() const;
@@ -192,22 +301,27 @@ namespace console {
 
     BOOST_TYPEOF_REGISTER_TYPE(DirectoryNode);
 
+    /// Exception: Unknown node name
     struct UnknownNodeNameException : public senf::Exception
     { UnknownNodeNameException() : senf::Exception("Unknown node name") {}};
 
-    // We need this specialization since we cannot passe auto_ptr via const & !!
-    template <class Type>
-    struct NodeCreateTraits< std::auto_ptr<Type> >
-    {};
-
+#ifndef DOXYGEN
     template <class Type>
     struct NodeCreateTraits< boost::shared_ptr<Type> >
     {};
+#endif
+
+    /** \brief Config/console tree command node
+
+        The CommandNode is the base-class for the tree leaf nodes. Concrete command node
+        implementations are derived from this class.
 
-    /** \brief
+        To execute a command, CommandNode::operator()() is called. This abstract virtual function
+        must be implemented in a derived class.
       */
     class CommandNode : public GenericNode
     {
+        SENF_LOG_CLASS_AREA();
     public:
         ///////////////////////////////////////////////////////////////////////////
         // Types
@@ -221,6 +335,11 @@ namespace console {
         ///////////////////////////////////////////////////////////////////////////
 
         virtual void operator()(std::ostream & output, Arguments const & arguments) = 0;
+                                        ///< Called to execute the command
+                                        /**< \param[in] output stream where result messages may be
+                                                 written to
+                                             \param[in] arguments command arguments. This is a
+                                                 range of ranges of ArgumentToken instances. */
 
         ptr thisptr();
         cptr thisptr() const;
@@ -231,14 +350,22 @@ namespace console {
     private:
     };
 
-    /** \brief
+    /** \brief Most simple CommandNode implementation
+
+        This CommandNode implementation simply forwards the \a output and \a arguments arguments to
+        an arbitrary callback.
       */
     class SimpleCommandNode : public CommandNode
     {
+        SENF_LOG_CLASS_AREA();
     public:
         ///////////////////////////////////////////////////////////////////////////
         // Types
 
+        typedef boost::shared_ptr<SimpleCommandNode> ptr;
+        typedef boost::shared_ptr<SimpleCommandNode const> cptr;
+        typedef boost::weak_ptr<SimpleCommandNode> weak_ptr;
+
         typedef boost::function<void (std::ostream &, Arguments const &)> Function;
 
         ///////////////////////////////////////////////////////////////////////////
@@ -248,7 +375,7 @@ namespace console {
         ptr thisptr();
         cptr thisptr() const;
 
-        static std::auto_ptr<SimpleCommandNode> create(Function const & fn);
+        static ptr create(Function const & fn);
 
         SimpleCommandNode & doc(std::string const & doc);
 
@@ -262,9 +389,11 @@ namespace console {
         std::string doc_;
     };
 
+#ifndef DOXYGEN
     template <class Function>
     SimpleCommandNode & senf_console_add_node(DirectoryNode & node, std::string const & name, 
                                               Function const & fn, ...);
+#endif
 
     BOOST_TYPEOF_REGISTER_TYPE(SimpleCommandNode);
 
index 3fb3a62..75b9c9f 100644 (file)
@@ -49,7 +49,7 @@ senf::console::OwnerNodeCreateTraits<Owner,Object>::Creator::create(DirectoryNod
 
 template <class Owner>
 prefix_ senf::console::ObjectDirectory<Owner>::ObjectDirectory(Owner * owner)
-    : node_ (DirectoryNode::create().release()), owner_ (owner)
+    : node_ (DirectoryNode::create()), owner_ (owner)
 {}
 
 template <class Owner>
index ccecf8d..1f26bef 100644 (file)
 namespace senf {
 namespace console {
 
+    /** \brief Internal: Node creation helper traits (ObjectDirectory proxy)
+        
+        This class is used like NodeCreateTraits to customize the child node creation. This trait
+        class is used however by the ObjectDirectory proxy.
+     */
     template <class Owner, class Object>
     struct OwnerNodeCreateTraits
     {
@@ -54,9 +59,41 @@ namespace console {
         };
     };
     
+    /** \brief Internal: Marker base class for all ObjectDirectory proxies
+     */
     struct ObjectDirectoryBase {};
 
-    /** \brief
+    /** \brief DirectoryNode member proxy
+
+        ObjectDirectory is used whenever a class wants to manage it's own directory. The class
+        allows to declare the directory as a public member object. This allows the user of the class
+        to register the directory in the command tree. By using the proxy, the node is automatically
+        detached from the tree (and thereby destroyed) when the object (and thereby the proxy) is
+        destroyed.
+
+        \code
+        class MyClass
+        {
+        public:
+            ObjectDirectory<MyClass> configDir;
+
+            MyClass() : configDir(this) 
+            {
+                configDIr.add(...);
+            }
+        };
+        \endcode
+
+        The ObjectDirectory proxy implements 'add()' to add new children to the proxied
+        DirectoryNode. All add() variants supported by DirectoryNode are supported by
+        ObjectDirectory.
+
+        \idea This proxy could be made obsolete by allowing to allocate node objects
+            statically. This could be achieved by moving back to an intrusive_ptr implementation for
+            normal pointing needs with an added twist: Give each node a smart_ptr member pointing to
+            itself with a null deleter. This allows to create weak_ptr's to the nodes which will
+            automatically expire when the node is deleted (either statically or by the
+            intrusive_ptr).
       */
     template <class Owner>
     class ObjectDirectory : public ObjectDirectoryBase
@@ -80,20 +117,23 @@ namespace console {
         template <class Object>
         typename OwnerNodeCreateTraits<Owner, Object>::NodeType & add(std::string const & name,
                                                                       Object const & ob);
+                                        ///< Create new child node
+                                        /**< Adds a new child node to the (proxied)
+                                             DirectoryNode. How the node is added is configured
+                                             using the OwnerNodeCreateTraits template. The default
+                                             implementation just forwards the call to the proxied
+                                             directory node. */
 
-        DirectoryNode & node() const;
+        DirectoryNode & node() const;   ///< Access the proxied DirectoryNode
 
     protected:
 
     private:
-        static SimpleCommandNode & create(DirectoryNode & node, Owner * owner, 
-                                          std::string const & name, 
-                                          SimpleCommandNode::Function const & fn);
-
         DirectoryNode::ptr node_;
         Owner * owner_;
     };
 
+#ifndef DOXYGEN
     template <class Owner, class Function>
     SimpleCommandNode & senf_console_add_node(
         DirectoryNode & node, Owner & owner, std::string const & name, Function const & fn);
@@ -107,6 +147,8 @@ namespace console {
     DirectoryNode & senf_console_add_node(
         DirectoryNode & dir, std::string const & name, Node const & node, int, 
         typename boost::enable_if< boost::is_convertible<Node*, ObjectDirectoryBase*> >::type * = 0);
+#endif
+
 }}
 
 ///////////////////////////////hh.e////////////////////////////////////////
index 36cde7c..8684f80 100644 (file)
@@ -42,12 +42,16 @@ namespace console {
 
     namespace detail { struct ParserAccess; }
 
-    /** \brief
+    /** \brief Single argument token
+
+        All command arguments are split into tokens by the parser. Each token is returned as an
+        ArgumentToken instance. 
       */
     class ArgumentToken
     {
     public:
-        std::string const & value() const;
+        std::string const & value() const; ///< String value of token
+                                        /**< This value is properly unquoted */
 
     protected:
 
@@ -59,8 +63,16 @@ namespace console {
         friend class detail::ParserAccess;
     };
 
+    /** \brief Console command
 
-    /** \brief
+        Every command parsed is returned in a ParseCommandInfo instance. This information is purely
+        taken from the parser, no semantic information is attached at this point, the config/console
+        is not involved in any why. ParseCommandInfo consist of
+        
+        \li the type of command: builtin or normal command represented by a possibly relative path
+            into the command tree.
+        \li the command
+        \li the arguments. Every argument consists of a range of ArgumentToken instances.
       */
     class ParseCommandInfo
     {
@@ -92,11 +104,22 @@ namespace console {
                               BuiltinEXIT,
                               BuiltinHELP };
 
-        BuiltinCommand builtin() const;
-        CommandPathRange commandPath() const;
-        ArgumentsRange arguments() const;
-        TokensRange tokens() const;
-        
+        BuiltinCommand builtin() const; ///< Command type
+                                        /**< \returns \c NoBuiltin, if the command is an ordinary
+                                             command, otherwise the id of the builtin command */
+        CommandPathRange commandPath() const; ///< Command path
+                                        /**< This is the path to the command if it is not a builtin
+                                             command. Every element of the returned range
+                                             constitutes one path element. If the first element is
+                                             empty, the path is an absolute path, otherwise it is
+                                             relative. If the last element is an empty string, the
+                                             path ends in a '/' char. */
+        ArgumentsRange arguments() const; ///< Command arguments
+                                        /**< The returned range contains one token range for each
+                                             agument. */
+        TokensRange tokens() const;     ///< All argument tokens
+                                        /**< The returned range contains \e all argument tokens in a
+                                             single range not divided into separate arguments. */
     protected:
 
     private:
@@ -123,9 +146,25 @@ namespace console {
         friend class detail::ParserAccess;
     };
 
+    /**< \brief Output ParseCommandInfo instance
+         \related ParseCommandInfo
+      */
     std::ostream & operator<<(std::ostream & stream, ParseCommandInfo const & info);
 
-    /** \brief
+    /** \brief Parse commands
+
+        This class implements a parser for the console/config language. It supports parsing strings
+        as well as files. For every parsed command, a callback function is called.
+
+        \implementation The implementation is based on Boost.Spirit. See the file \ref Parse.ih for
+            the formal language grammar.
+
+        \implementation Parsing an arbitrary iostream is not supported since arbitrary streams are
+            not seekable. If this is needed, it can however be provided using stream iterators and
+            some special iterator adaptors from Boost.Spirit. However, the amount of backtracking
+            needs to be analyzed before this is viable.
+
+        \todo Implement more detailed error reporting and error recovery.
       */
     class CommandParser
         : boost::noncopyable
@@ -146,8 +185,10 @@ namespace console {
         ///@}
         ///////////////////////////////////////////////////////////////////////////
 
-        bool parse(std::string command, Callback cb);
-        bool parseFile(std::string filename, Callback cb);
+        bool parse(std::string command, Callback cb); ///< Parse string
+        bool parseFile(std::string filename, Callback cb); ///< Parse file
+                                        /**< \throws SystemException if the file cannot be
+                                             read. */
 
     private:
         struct Impl;
index 02930e6..f676f10 100644 (file)
@@ -42,6 +42,8 @@ namespace senf {
 namespace console {
 namespace detail {
 
+#ifndef DOXYGEN
+
     struct append_action
     {
         template <class T, class Value>
@@ -349,6 +351,8 @@ namespace detail {
         };
     };
 
+#endif
+
 }}}
 
 ///////////////////////////////ih.e////////////////////////////////////////
index c8d2c7f..685eae2 100644 (file)
@@ -50,8 +50,12 @@ namespace console {
 
     class Client;
 
-    /** \brief
-        ///\fixme Use special non-blocking streambuf
+    /** \brief Interactive console server
+
+        This class provides an interactive console TCP server.
+
+        \todo Add readline support
+        \todo Add interactivity detection using timeout
       */
     class Server
         : boost::noncopyable
@@ -69,9 +73,12 @@ namespace console {
         ~Server();
 
         static Server & start(senf::INet4SocketAddress const & address);
+                                        ///< Start server on given IPv4 address/port
         static Server & start(senf::INet6SocketAddress const & address);
+                                        ///< Start server on given IPv6 address/port
 
-        void name(std::string const & name);
+        void name(std::string const & name); ///< Set server name
+                                        /**< This information is used in the prompt string. */
 
     protected:
 
@@ -94,12 +101,18 @@ namespace console {
         friend class Client;
     };
     
-    /** \brief
+    /** \brief Server client instance
+
+        Whenever a new client connects, a new instance of this class is created. This class shows a
+        command prompt, receives the commands, parses them and then passes (using a CommandParser)
+        and passes the commands to an Executor instance.
 
         \fixme Fix Client::clientData implementation
-            Remove the 'dup' needed here so we don't close the same fd twice (see Client constructor)
-            Make output non-blocking
-            Don't register a new ReadHelper every round
+        \fixme Remove the 'dup' needed here so we don't close the same fd twice (see Client
+            constructor)
+        \fixme Make output non-blocking (use a non-blocking/discarding streambuf) and possibly set
+            socket send buffer size
+        \fixme Don't register a new ReadHelper every round
      */
     class Client
         : public senf::intrusive_refcount
@@ -111,7 +124,8 @@ namespace console {
 
         ~Client();
 
-        void stopClient();
+        void stopClient();              ///< Stop the client
+                                        /**< This will close the client socket. */
 
     protected:
         
index f0d500e..8b3f3da 100644 (file)
@@ -67,23 +67,39 @@ namespace {
         }
     };
 
+    void shutdownServer(std::ostream &,
+                        senf::console::CommandNode::Arguments const &)
+    {
+        senf::Scheduler::instance().terminate();
+        throw senf::console::Executor::ExitException();
+    }
+
 }
 
 int main(int, char const **)
 {
     senf::log::ConsoleTarget::instance().route< senf::SenfLog, senf::log::NOTICE >();
 
-    senf::console::root().doc("This is the console test application");
-    senf::console::root().mkdir("network")
+    senf::console::root()
+        .doc("This is the console test application");
+    senf::console::root()
+        .mkdir("network")
         .doc("Network related settings");
-    senf::console::root()["network"].mkdir("eth0")
+    senf::console::root()["network"]
+        .mkdir("eth0")
         .doc("Ethernet device eth0");
-    senf::console::root().mkdir("server");
-    senf::console::root()["network"].add("route", &fn)
+    senf::console::root()
+        .mkdir("server");
+    senf::console::root()["server"]
+        .add("shutdown", &shutdownServer)
+        .doc("Terminate server application");
+    senf::console::root()["network"]
+        .add("route", &fn)
         .doc("Example of a directly registered function");
 
     TestObject test;
-    senf::console::root().add("testob", test.dir)
+    senf::console::root()
+        .add("testob", test.dir)
         .doc("Example of an instance directory");
 
     senf::console::Server::start( senf::INet4SocketAddress("127.0.0.1:23232") )