9bf37ecb2f5300ea154e4f3f0e3693f6dab76201
[senf.git] / Console / Node.hh
1 // $Id$
2 //
3 // Copyright (C) 2008 
4 // Fraunhofer Institute for Open Communication Systems (FOKUS)
5 // Competence Center NETwork research (NET), St. Augustin, GERMANY
6 //     Stefan Bund <g0dil@berlios.de>
7 //
8 // This program is free software; you can redistribute it and/or modify
9 // it under the terms of the GNU General Public License as published by
10 // the Free Software Foundation; either version 2 of the License, or
11 // (at your option) any later version.
12 //
13 // This program is distributed in the hope that it will be useful,
14 // but WITHOUT ANY WARRANTY; without even the implied warranty of
15 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16 // GNU General Public License for more details.
17 //
18 // You should have received a copy of the GNU General Public License
19 // along with this program; if not, write to the
20 // Free Software Foundation, Inc.,
21 // 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
22
23 /** \file
24     \brief Node public header */
25
26 /** \defgroup node_tree The console/config file-system node tree
27     
28     The console/config node tree is the central data-structure of the library. Into this tree, all
29     commands and parameters are entered. The tree is then exposed using a file-system like
30     interface.
31     
32     \autotoc
33
34     \section console_tree The tree
35
36     We will start by giving a more complete example. This example contains most of the stuff needed
37     for using the console/config library.
38
39     \code
40     // Define callback function.
41     void mycommand(std::ostream & os, senf::console::ParseCommandInfo const & command)
42     {
43         // ...
44         os << "!! Important message ...\n";
45     }
46
47     class SomeClass
48     {
49     public:
50         // Declare a directory node (proxy) for use by this class. This must be public so we can add
51         // it to the node tree later.
52         senf::console::ScopedDirectory<SomeClass> dir;
53
54         SomeClass() : dir(this) 
55         {
56             // You may document the directory here or later when adding it to the tree
57             dir.doc("Manager for something");
58
59             // Add a member function (the pointer-to-member is automatically bound to this instance)
60             dir.add("member", &SomeClass::member)
61                 .doc("Do the member operation");
62         }
63
64         void member(std::ostream & os, senf::console::ParseCommandInfo const & command)
65         {
66             // ...
67         }
68     };
69
70     int main(int, char**)
71     {
72         // Provide global documentation
73         senf::console::root()
74             .doc("This is someServer server");
75
76         // Add a new directory to the root and document it. All the mutators return the node object
77         // itself so operations can be chained.
78         senf::console::DirectoryNode & mydir (
79                 .mkdir("myserver")
80                 .doc("My server specific directory"));
81
82         // Add a command to that directory
83         mydir.add("mycommand", &mycommand)
84             .doc("mycommand <foo> [<bar>]\n\n"
85                  "If <bar> is given, flurgle the <foo>, otherwise burgle it");
86
87         // Create a SomeClass instance and add it's directory.
88         SomeClass someClass;
89         mydir.add("someClass", someClass.dir);
90
91         // Start the interactive console server
92         senf::console::Server::start(senf::INet4SocketAddress(senf::INet4Address::None, 23232u))
93             .name("someServer");
94     }
95     \endcode
96
97     \subsection console_nodes Node types
98
99     The console/config library tree consists of two basic node types:
100     
101     \li senf::console::DirectoryNode provides internal nodes with an arbitrary number of children
102     \li senf::console::CommandNode describes a command entry in the tree
103
104     senf::console::CommandNode is the base-class of all command nodes of which there are several,
105     depending on the type of command.
106
107     There is a single root node, the senf::console::DirectoryNode called senf::console::root(). From
108     this node, the tree is traversed.
109
110     All nodes are allocated on the heap and are managed using a smart pointer.
111     
112     \subsection console_manipulate Manipulating the node tree
113
114     There are several ways to add nodes to the tree:
115
116     \li A senf::console::DirectoryNode can be added using senf::console::DirectoryNode::mkdir().
117     \li An arbitrary node can be created and then (possibly later) added to the tree using the
118         corresponding senf::console::DirectoryNode::add() overload.
119     \li A senf::console::CommandNode is normally added to the tree by directly adding a callback
120         using one of the overloaded senf::console::DirectoryNode::add() members.
121
122     When directly adding a node callback, the type of node added depends on the type of
123     callback. The callback types which can be added are listed at \ref console_callbacks.
124     
125     \code
126     void callback(std::ostream & os, senf::console::ParseCommandInfo const & command) { ... }
127     // ...
128     myDirectory.add("foo",&callback);
129     \endcode
130
131     Every node is identified among it's siblings by it's name. The name of the node is set when
132     adding the node to the tree. If the name is empty or non-unique, a unique name will be
133     automatically provided.
134
135     To remove a node from the tree, just use the nodes senf::console::GenericNode::unlink() or the
136     parents senf::console::DirectoryNode::remove() member. This call removes the node from it's
137     parent and returns a (smart) node pointer.
138
139     \li If you ignore the return value, the node (and it's children) will be deleted.
140     \li Alternatively, you may store away the node and re-attach it later.
141     \li An node (or subtree) can be moved to a different place by unlinking the node at it's old
142         place and re-adding it at it's new location.
143     \li To rename a node, unlink and re-add it with a different name.
144
145     \code
146     myDirectory.add("bar", myDirectory.remove("foo"));
147     \endcode
148
149     \subsection console_node_param Assigning additional node parameters
150
151     Depending on the node type added, additional node parameters may be set. For example, every node
152     has a documentation parameter which is used by the online-help system. To assign these
153     parameters, the node exposes corresponding member functions. Since
154     senf::console::DirectoryNode::add() returns the newly added node by reference, additional
155     parameters may just be added to the end of the add command:
156     \code
157     myDirectory.add("foo",&fooCallback).doc("The foo method");
158     \endcode
159     Since the parameter setters all return the node reference, additional parameters may just be
160     added to the end of the command.
161     
162     \subsection console_tree_traverse Traversing the tree
163
164     The simplest way to access tree elements is to save the return value of the
165     senf::console::DirectoryNode::add() members. However, saving the reference will not ensure, that
166     the node is not removed. If the node might be removed from the tree, you should use a smart
167     pointer (either <tt>ptr</tt> or <tt>weak_ptr</tt>) to hold the node.
168
169     Another possibility is to traverse the tree explicitly. For this purpose, the operators '[]' and
170     '()' have been overloaded in senf::console::DirectoryNode.
171     \code
172     senf::console::root().getDirectory("myDirectory").getCommand("foo")
173     \\ or more concise but otherwise completely identical
174     senf::console::root()["myDirectory"]("foo")
175     \endcode
176
177     getDirectory and the '[]' operator will return a senf::console::DirectoryNode whereas getCommand
178     and the '()' operator will return a senf::console::CommandNode. If the node is not found or is
179     not of the correct type, an exception will be raised.
180
181     \section console_object_dir Assigning a directory to an object instance
182
183     Most objects will register several commands. So it makes sense for these objects to manage their
184     own directory. Since directories are however allocated on the heap, they cannot be directly
185     added to a class. To facilitate this usage, the senf::console::ScopedDirectory is used. This
186     class provides a senf::console::DirectoryNode facade. Internally, it automatically creates a
187     senf::console::DirectoryNode to which all calls are forwarded.
188
189     The senf::console::ScopedDirectory member should be declared public. This allows the user of the
190     class to add the node to the tree.
191  */
192
193 #ifndef HH_Node_
194 #define HH_Node_ 1
195
196 // Custom includes
197 #include <map>
198 #include <boost/shared_ptr.hpp>
199 #include <boost/weak_ptr.hpp>
200 #include <boost/enable_shared_from_this.hpp>
201 #include <boost/utility.hpp>
202 #include <boost/range/iterator_range.hpp>
203 #include <boost/typeof/typeof.hpp>
204 #include <boost/type_traits/remove_reference.hpp>
205 #include "../Utils/Exception.hh"
206 #include "../Utils/mpl.hh"
207 #include "../Utils/Logger/SenfLog.hh"
208 #include "../Utils/type_traits.hh"
209 #include "Parse.hh"
210
211 //#include "Node.mpp"
212 ///////////////////////////////hh.p////////////////////////////////////////
213
214 namespace senf {
215 namespace console {
216
217     class DirectoryNode;
218     class CommandNode;
219
220     /** \brief Config/console node tree base-class
221
222         GenericNode is the base class of all node objects. There are two basic node types derived
223         from GenericNode:  DirectoryNode and CommandNode.
224
225         All nodes are dynamically allocated and automatically managed using reference counting.
226
227         All nodes are normally linked into a single tree which root node is
228         senf::console::root(). Nodes may however be orphaned (not linked to the tree) either
229         directly (the node has no parent) or indirectly (the node has a parent but is part of an
230         orphaned subtree which is not linked to the root node).
231
232         Every active (non-orphaned) node (except the root() node) has a non-empty node name. This
233         name is assigned to the node when adding the node to the tree.
234
235         \ingroup node_tree
236       */
237     class GenericNode 
238         : public boost::enable_shared_from_this<GenericNode>
239     {
240         SENF_LOG_CLASS_AREA();
241     public:
242         ///////////////////////////////////////////////////////////////////////////
243         // Types
244
245         typedef boost::shared_ptr<GenericNode> ptr;
246         typedef boost::shared_ptr<GenericNode const> cptr;
247         typedef boost::weak_ptr<GenericNode> weak_ptr;
248
249         ///////////////////////////////////////////////////////////////////////////
250
251         virtual ~GenericNode();
252
253         std::string const & name() const; ///< Node name
254         boost::shared_ptr<DirectoryNode> parent() const; ///< Parent node
255                                         /**< May be null, if the node is the root node or if it is
256                                              not linked to the tree */
257
258         std::string path() const;       ///< Node path
259                                         /**< The node path is built by joining the names of all
260                                              parent nodes with '/' chars. */
261
262         ptr unlink();                   ///< Remove node from it's parent directory
263                                         /**< You may either discard the return value and thereby
264                                              dispose the node or may re-attach the node at some
265                                              other place using DirectoryNode::add(). */
266
267         bool active() const;            ///< \c true, if node is attached to the root() node
268
269         void help(std::ostream & output) const; /// Write help info to \a output
270
271         ptr thisptr();                  ///< Get smart pointer to node
272         cptr thisptr() const;           ///< Get smart pointer to node (const)
273
274     protected:
275         GenericNode();
276
277         void name(std::string const & name);
278
279 #ifndef DOXYGEN
280     private:
281 #else
282     public:
283 #endif
284         virtual void v_help(std::ostream & output) const = 0;
285                                         ///< Provide help information
286                                         /**< This member must be implemented in derived classes
287                                              to provide node specific help information. */
288
289     private:
290         std::string name_;
291         DirectoryNode * parent_;
292
293         friend class DirectoryNode;
294     };
295
296     class SimpleCommandNode;
297
298     /** \brief Internal: Node creation helper traits
299         
300         This class is used internally to find out the type of node to create for a specific argument
301         type. 
302      */
303     template <class Object>
304     struct NodeCreateTraits
305     {
306         typedef BOOST_TYPEOF_TPL( senf_console_add_node( 
307                                       * static_cast<DirectoryNode *>(0),
308                                       * static_cast<std::string const *>(0),
309                                       * static_cast<Object const *>(0),
310                                       0) ) base_type;
311         typedef typename senf::remove_cvref<base_type>::type value_type;
312
313         typedef typename value_type::node_type NodeType;
314         typedef typename value_type::return_type result_type;
315
316         /// Internal
317         struct Creator {
318             static result_type create(DirectoryNode & node, std::string const & name, 
319                                       Object const & ob);
320         };
321     };
322
323     /** \brief Config/console tree directory node
324
325         This node type provides the internal and root nodes of the tree. It allows to add arbitrary
326         children and supports directory traversal.
327         
328         Nodes are normally not instantiated manually but are created by the DirectoryNode via
329         mkdir() or add(). Special add() members however allow externally allocated node objects.
330
331         Nodes may be added to the tree only once, otherwise chaos will ensue. Since nodes are always
332         managed dynamically, there is a special ScopedDirectory proxy template which provides a
333         DirectoryNode facade. ScopedDirectory is used if a class wants to manage it's own directory
334         as a data member.
335
336         Every node is assigned a (new) name when it is added to a directory. If the directory
337         already has an entry of that name, the name is made unique by appending a suffix of the form
338         '-n' where n is a number starting at 1. If the name is empty, int is set to 'unnamed' and
339         then uniquified as above. Automatically providing unique names simplifies adding
340         configuration/console support to generic components.
341
342         \ingroup node_tree
343       */
344     class DirectoryNode : public GenericNode
345     {
346         SENF_LOG_CLASS_AREA();
347         typedef std::map<std::string, GenericNode::ptr> ChildMap;
348     public:
349         ///////////////////////////////////////////////////////////////////////////
350         // Types
351
352         typedef boost::shared_ptr<DirectoryNode> ptr;
353         typedef boost::shared_ptr<DirectoryNode const> cptr;
354         typedef boost::weak_ptr<DirectoryNode> weak_ptr;
355
356         typedef boost::iterator_range<ChildMap::const_iterator> ChildrenRange;
357         typedef ChildMap::const_iterator child_iterator;
358
359         typedef DirectoryNode node_type;
360         typedef DirectoryNode & return_type;
361
362         ///////////////////////////////////////////////////////////////////////////
363         ///\name Structors and default members
364         ///\{
365
366         static ptr create();            ///< Create node object.
367                                         /**< You should normally use either mkdir() or
368                                              ScopedDirectory instead of create() */
369
370         ///\}
371         ///////////////////////////////////////////////////////////////////////////
372         ///\name Children
373         ///\{
374
375         template <class NodeType>
376         NodeType & add(std::string const & name, boost::shared_ptr<NodeType> node);
377                                         ///< Add node to tree
378                                         /**< Adds the \a node to the tree as a child of \a this
379                                              node. The node is given the name \a name. If a node of
380                                              that name already exists, a numeric suffix of the form
381                                              '-n' is added to the name until the name is unique. If
382                                              \a name is empty, it is set to 'unnamed'. */
383
384         template <class Object>
385         typename NodeCreateTraits<Object>::result_type add (std::string const & name, 
386                                                             Object const & ob);
387                                         ///< Generic child node factory
388                                         /**< This member is used to create a new child node of the
389                                              current directory. The type of node created depends on
390                                              the type of argument passed.
391
392                                              The node type is selected by the NodeCreateTraits
393                                              class. To allow adding a specific node type, you need
394                                              to provide an overload for
395                                              <tt>senf_console_add_node</tt> which must be visible at
396                                              when you register the new node.
397                                              \code
398                                              MyNodeType & senf_console_add_node(
399                                                  DirectoryNode & dir,
400                                                  std::string const & name,
401                                                  MySpecialObject const & ob,
402                                                  int)
403                                              {
404                                                  return dir.add(name, MyNodeType::create(ob));
405                                              }
406                                              \endcode
407                                              (Do not forget the last unnamed 'int' parameter which
408                                              is not used but serves to disambiguate the
409                                              overloads). */
410
411         GenericNode::ptr remove(std::string const & name);
412                                         ///< Remove node \a name from the tree
413                                         /**< The returned pointer may either be discarded, which
414                                              will automatically dispose the removed node, or it may
415                                              be saved and/or re-attached at some other place in the
416                                              tree. */
417
418         bool hasChild(std::string const & name) const;
419                                         ///< \c true, if there is a child with name \a name
420
421         GenericNode & get(std::string const & name) const;
422                                         ///< Get child node
423                                         /**< \throws UnknownNodeNameException if a child \a name
424                                                  does not exist */
425
426         DirectoryNode & getDirectory(std::string const & name) const;
427                                         ///< Get directory child node
428                                         /**< Same as operator[]
429                                              \throws UnknownNodeNameException if a child \a name
430                                                  does not exist. 
431                                              \throws std::bad_cast if the child \a name is not a
432                                                  directory node. */
433         
434         DirectoryNode & operator[](std::string const & name) const;
435                                         ///< Get directory child node
436                                         /**< Same as getDirectory
437                                              \throws UnknownNodeNameException if a child \a name
438                                                  does not exist. 
439                                              \throws std::bad_cast if the child \a name is not a
440                                                  directory node. */
441
442         CommandNode & getCommand(std::string const & name) const;
443                                         ///< Get command child node
444                                         /**< Same as operator()
445                                              \throws UnknownNodeNameException if a child \a name
446                                                  does not exist
447                                              \throws std::bad_cast if the child \a name is not a
448                                                  command node. */
449
450         CommandNode & operator()(std::string const & name) const;
451                                         ///< Get command child node
452                                         /**< Same as getCommand()
453                                              \throws UnknownNodeNameException if a child \a name
454                                                  does not exist
455                                              \throws std::bad_cast if the child \a name is not a
456                                                  command node. */
457
458         DirectoryNode & mkdir(std::string const & name);
459                                         ///< Create sub-directory node
460         
461         ChildrenRange children() const;
462                                         ///< Return iterator range over all children.
463                                         /**< The returned range is sorted by child name. */
464
465         ///\}
466         ///////////////////////////////////////////////////////////////////////////
467
468         template <class ForwardRange>
469         GenericNode & traverse(ForwardRange const & range);
470                                         ///< Traverse node path starting at this node
471                                         /**< The <tt>ForwardRange::value_type</tt> must be
472                                              (convertible to) std::string. Each range element
473                                              constitutes a step along the node traversal.
474
475                                              If the range starts with an empty element, the
476                                              traversal is started at the root() node, otherwise it
477                                              is started at \a this node. The traversal supports '.',
478                                              '..' and ignores further empty elements. */
479
480         DirectoryNode & doc(std::string const & doc);
481                                         ///< Set node documentation
482
483         ptr thisptr();
484         cptr thisptr() const;
485
486     protected:
487         DirectoryNode();
488
489     private:
490         void add(GenericNode::ptr node);
491         virtual void v_help(std::ostream & output) const;
492
493         ChildMap children_;
494         std::string doc_;
495
496         friend DirectoryNode & root();
497     };
498
499     /// Exception: Unknown node name
500     struct UnknownNodeNameException : public senf::Exception
501     { UnknownNodeNameException() : senf::Exception("Unknown node name") {}};
502
503 #ifndef DOXYGEN
504     template <class Type>
505     struct NodeCreateTraits< boost::shared_ptr<Type> >
506     {};
507 #endif
508
509     struct SyntaxErrorException : public senf::Exception
510     {
511         explicit SyntaxErrorException(std::string const & msg = "");
512
513         virtual char const * what() const throw();
514     };
515
516     /** \brief Config/console tree command node
517
518         The CommandNode is the base-class for the tree leaf nodes. Concrete command node
519         implementations are derived from this class.
520
521         To execute a command, CommandNode::operator()() or CommandNode::execute() is called.
522
523         \ingroup node_tree
524       */
525     class CommandNode : public GenericNode
526     {
527         SENF_LOG_CLASS_AREA();
528     public:
529         ///////////////////////////////////////////////////////////////////////////
530         // Types
531
532         typedef boost::shared_ptr<CommandNode> ptr;
533         typedef boost::shared_ptr<CommandNode const> cptr;
534         typedef boost::weak_ptr<CommandNode> weak_ptr;
535
536         ///////////////////////////////////////////////////////////////////////////
537
538         void execute(std::ostream & output, ParseCommandInfo const & command) const;
539                                         ///< Execute the command
540                                         /**< Same as operator()()
541                                              \param[in] output stream where result messages may be
542                                                  written to
543                                              \param[in] arguments command arguments. This is a
544                                                  range of ranges of ArgumentToken instances. */
545
546         void operator()(std::ostream & output, ParseCommandInfo const & command) const;
547                                         ///< Execute the command
548                                         /**< Same as execute()
549                                              \param[in] output stream where result messages may be
550                                                  written to
551                                              \param[in] arguments command arguments. This is a
552                                                  range of ranges of ArgumentToken instances. */
553
554         ptr thisptr();
555         cptr thisptr() const;
556
557     protected:
558         CommandNode();
559
560 #ifndef DOXYGEN
561     private:
562 #endif
563         virtual void v_execute(std::ostream & output, ParseCommandInfo const & command) const = 0;
564                                         ///< Called to execute the command
565                                         /**< \param[in] output stream where result messages may be
566                                                  written to
567                                              \param[in] arguments command arguments. This is a
568                                                  range of ranges of ArgumentToken instances. */
569
570     private:
571     };
572
573     /** \brief Most simple CommandNode implementation
574
575         This CommandNode implementation simply forwards the \a output and \a arguments arguments to
576         an arbitrary callback.
577  
578         \ingroup node_tree
579      */
580     class SimpleCommandNode : public CommandNode
581     {
582         SENF_LOG_CLASS_AREA();
583     public:
584         ///////////////////////////////////////////////////////////////////////////
585         // Types
586
587         typedef boost::shared_ptr<SimpleCommandNode> ptr;
588         typedef boost::shared_ptr<SimpleCommandNode const> cptr;
589         typedef boost::weak_ptr<SimpleCommandNode> weak_ptr;
590
591         typedef boost::function<void (std::ostream &, ParseCommandInfo const &)> Function;
592
593         typedef SimpleCommandNode node_type;
594         typedef SimpleCommandNode & return_type;
595
596         ///////////////////////////////////////////////////////////////////////////
597         ///\name Structors and default members
598         ///\{
599
600         static ptr create(Function const & fn);
601
602         ///\}
603         ///////////////////////////////////////////////////////////////////////////
604
605         ptr thisptr();
606         cptr thisptr() const;
607
608         SimpleCommandNode & doc(std::string const & doc);
609
610     protected:
611         SimpleCommandNode(Function const & fn);
612
613     private:
614         virtual void v_help(std::ostream & output) const;
615         virtual void v_execute(std::ostream & output, ParseCommandInfo const & command) const;
616         
617
618         Function fn_;
619         std::string doc_;
620     };
621
622 #ifndef DOXYGEN
623     template <class Function>
624     SimpleCommandNode & senf_console_add_node(DirectoryNode & node, std::string const & name, 
625                                               Function const & fn, ...);
626 #endif
627
628     DirectoryNode & root();
629
630 }}
631
632 #include BOOST_TYPEOF_INCREMENT_REGISTRATION_GROUP()
633
634 BOOST_TYPEOF_REGISTER_TYPE(senf::console::DirectoryNode)
635 BOOST_TYPEOF_REGISTER_TYPE(senf::console::SimpleCommandNode)
636
637
638 ///////////////////////////////hh.e////////////////////////////////////////
639 #include "Node.cci"
640 #include "Node.ct"
641 #include "Node.cti"
642 #endif
643
644 \f
645 // Local Variables:
646 // mode: c++
647 // fill-column: 100
648 // comment-column: 40
649 // c-file-style: "senf"
650 // indent-tabs-mode: nil
651 // ispell-local-dictionary: "american"
652 // compile-command: "scons -u test"
653 // End: