Main Page | Namespace List | Class Hierarchy | Alphabetical List | Compound List | File List | Namespace Members | Compound Members

node.h

Go to the documentation of this file.
00001 #ifndef DVXML_NODE_H
00002 #define DVXML_NODE_H
00003 // $Id: node.h,v 1.24 2003/09/08 14:11:01 dvermeir Exp $
00004 #include <stdexcept>
00005 #include <iostream>
00006 #include <iterator>
00007 #include <dvutil/tostring.h>
00008 #include <xmlwrapp/xmlwrapp.h>
00009 #include <dvxml/tohtml.h>
00010 #include <dvxml/exception.h>
00011 #include <dvxml/noconst.h>
00012 #include <dvxml/dv_forward_iterator.h>
00013 
00014 namespace Dv {
00015 namespace Xml {
00016 
00017 /** Convenience interface to xml::node.
00018  * There are two classes:
00019  *  - Dv::Xml::Node which is publicly derived from xml::node. All it
00020  *  adds to xml::node are constructors using std::string insteaf of
00021  *  const char*.
00022  *  - Dv::Xml::Node::Ref which encapsulates a xml::node::iterator
00023  *  (there is no version for xml::node::const_iterator). Thus a
00024  *  Dv::Xml::Node::Ref object can be thought of as a pointer into
00025  *  an XML tree.
00026  *
00027  * The intention is that Dv::Xml::Node is only used for "roots" in
00028  * XML tree structures, as in
00029  * \code
00030  * Dv::Xml::Node node("rootlabel");
00031  * \endcode
00032  *
00033  * For all other operations, Dv::Xml::Node::Ref should be used.
00034  * A strange feature is that Dv::Xml::Node::Ref::operator*
00035  * returns a reference to itself. The reason is that returning
00036  * a reference to a Dv::Xml::Node would involve copying the
00037  * underlying xml::node. On the other hand, returning a reference
00038  * to the underlying xml::node would imply that the convenient member
00039  * functions of Dv::Xml::Node::Ref would not be available on the
00040  * result of Dv::Xml::Node::Ref::operator* .
00041  *
00042  * For convenience, some Dv::Xml::Node::Ref operations are also
00043  * available on Dv::Xml::Node, either by direct delegation (e.g.
00044  * Dv::Xml::Node::operator[], Dv::Xml::Node::operator()) or
00045  * via a user-defined convertor that converst a Dv::Xml::Node to
00046  * a Dv::Xml::Node::Ref referring to this node.
00047  *
00048  * The main difference with the underlying xmlwrapp interface  is that
00049  * many operations are available through expressions. E.g. \a
00050  * (n/"login"/"name")("first_name"), with \a n a Dv::Xml::Node of
00051  * Dv::Xml::Node::Ref object, * refers to the \a first_name attribute of
00052  * the node obtained from \a n by taking the first "login" child and then
00053  * the first "name" child.
00054  *
00055  * Intuitively, for a Dv::Xml::Node::Ref \a n, \a n/"label" returns
00056  * a Dv::Xml::Node::Ref referring to the first child node with \a "label"
00057  * as name. The increment operator returns the next child with the
00058  * same name. E.g.
00059  * \code
00060  * Dv::Xml::Node::Ref n;
00061  * ..
00062  * Dv::Xml::Node::Ref c(n/"label");
00063  * ++c // refer to 2nd child of n with name "label"
00064  * \endcode
00065  * 
00066  * The \a > operator can be used to create new child nodes.
00067  * E.g. \a n>"label" returns a new child of \a n with name \a "label".
00068  * The same operator also works with a Dv::Xml::Node argument.
00069  * \code
00070  * Dv::Xml::Node::Ref n;
00071  * Dv::Xml::Node::Ref c;
00072  * (n >> c) // attach a copy of c as a child to n
00073  * \endcode
00074  *
00075  * Attributes can be set and accessed using \a operator[] and
00076  * \a operator() respectively.
00077  * \code
00078  * Dv::Xml::Node::Ref n;
00079  * n["first_name"] = "blabla"; // set attribute
00080  * if (n("first_name") != "blabla") // obtain attribute value
00081  *   throw std::logic_error("BUG");
00082  * \endcode
00083  *
00084  * The text content of a Dv::Xml::Node::Ref can be obtained by a conversion
00085  * (to std::string) operator. Assigning a std::string to a
00086  * Dv::Xml::Node::Ref has the same effect as xml::node::set_content().
00087  *
00088  */
00089 class Node: public xml::node {
00090 public:
00091   class Ref;
00092 
00093   /** Auxiliary class for attribute access. 
00094    * Dv::Xml::Node::Ref::operator[](const std::string) returns an
00095    * AttributeReference object.
00096    */
00097   class AttributeReference {
00098   friend class Ref;
00099   public:
00100     /** Set attribute referred to by this reference.
00101      * @param value to assign to attribute.
00102      * @return value
00103      */
00104     const std::string& operator=(const std::string& value);
00105 
00106     /** Set attribute referred to by this reference, after converting
00107      * the parameter object to a std::string. The conversion is done
00108      * using Dv::Util::tostring<T>.
00109      * @param t value to assign, after conversion, to attribute.
00110      * @return value
00111      */
00112     template <typename T>
00113     const std::string& operator=(const T& t) {
00114       return this->operator=(Dv::Util::tostring<T>(t));
00115       }
00116 
00117     /** Retrieve value of attribute referred to by this reference.
00118      * @return value of attribute referred to by this reference, or
00119      *  empty string if there is no attribute value associated with
00120      *  this reference.
00121      */
00122     std::string str() const;
00123 
00124     /** Retrieve value of attribute referred to by this reference.
00125      * @return value of attribute referred to by this reference, or
00126      *  empty string if there is no attribute value associated with
00127      *  this reference.
00128      */
00129     operator std::string() const { return str(); }
00130 
00131     /** Explicit conversion to any type that can be converted by
00132      *  Dv::Util::fromstring.
00133      * @param t lvalue referring to object resulting from the conversion
00134      * @return reference to first parameter (t).
00135      */
00136     template<typename T>
00137     T& conv(T& t) const {
00138       if (! Dv::Util::fromstring(t, str()) )
00139         throw Dv::Xml::Exception("Cannot convert AttributeReference to T");
00140       return t;
00141       }
00142   
00143     /** Template user-defined conversion function.
00144      * @warning This will not work for std::string (and others), unless
00145      *   the "assignment" syntax is used for explicit initializtion,
00146      *   as illustrated by the following example.
00147      * \code
00148      * Dv::Xml::Node::Ref n;
00149      * std::string s1(n["a"]); // error
00150      * std::string s2 = n["a"]; // OK
00151      * \endcode
00152      * The initialization of @a s1 is ambiguous: \a n["a"] may be converted
00153      * to a (single) paramater for a  std::string ctor using at least two
00154      * instantiations of the user-defined conversion template below:
00155      * - std::string(const char*)
00156      * - std::string(const std::string&)
00157      */
00158     template<typename T>
00159     operator T() const {
00160       typename noconst<T>::mutable_type t;
00161       conv(t);
00162       return t;
00163       }
00164 
00165     friend std::ostream& operator<<(std::ostream& os, const AttributeReference& a) {
00166       return os << a.str();
00167       }
00168 
00169   private:
00170     /** Constructor 
00171      * @param n reference to xml::node for which the attribute is
00172      * considered
00173      * @param name of the attribute
00174      */
00175     AttributeReference(const xml::node::iterator n, const std::string& name);
00176 
00177     const std::string name;
00178     xml::node::iterator node;
00179   };
00180 
00181   /** A reference to an xml::node object. */
00182 //  class Ref: public std::iterator<std::forward_iterator_tag, Dv::Xml::Node::Ref>  {
00183 //  class Ref: public std::forward_iterator<Dv::Xml::Node::Ref>  {
00184   class Ref: public dv_forward_iterator<Dv::Xml::Node::Ref> {
00185   friend class Node;
00186   friend class Document;
00187   public:
00188   
00189     /** Construct a Dv::Xml::Node::Ref out of a (copy of a) xml::node object.
00190      * @param node xml::node which will be reference by the new Dv::Xml::Node::Ref object.
00191      */
00192     explicit Ref(xml::node& node);
00193   
00194     /** @return name of underlying xml::node 
00195      * @exception Dv::Xml::Exception if this Dv::Xml::Node::Ref 
00196      *   does not refer to a valid xml::node.
00197      * @sa Dv::Xml::Node::Ref::nil
00198      */
00199     std::string name() const throw (Dv::Xml::Exception);
00200   
00201     /** Set name of underlying xml::node 
00202      * @param nm new name of node
00203      * @return *this
00204      * @exception Dv::Xml::Exception if this Dv::Xml::Node::Ref does not refer
00205      *   to a valid xml::node.
00206      * @sa Dv::Xml::Node::Ref::nil
00207      */
00208     Dv::Xml::Node::Ref& name(const std::string& nm) throw (Dv::Xml::Exception);
00209   
00210     /** Is this a "non-null" Dv::Xml::Node::Ref, i.e. does it refer to a valid
00211      *   xml::node?
00212      * @return true iff this Dv::Xml::Node::Ref does not refer to a valid xml::node.
00213      */
00214     bool nil() const { return nil_; }
00215   
00216     /** Is this not a valid Dv::Xml::Node::Ref, i.e. does it not refer to a valid
00217      *   xml::node?
00218      * @return true iff this Dv::Xml::Node::Ref does not refer to a valid xml::node.
00219      * @sa Dv::Xml::Node::Ref::nil
00220      */
00221     bool operator!() const { return nil(); }
00222   
00223     /** Retrieve underlying xml::node for this Dv::Xml::Node::Ref.
00224      * This function can also be used to check whether this is a nil node.
00225      * \code
00226      * Dv::Xml::Node::Ref n(root/"child"/"grandchild");
00227      * if (n) {
00228      *   ..
00229      *   }
00230      * \endcode
00231      * @return pointer to xml::node to which this Dv::Xml::Node::Ref
00232      *   refers, or 0 if it is a "nil" node.
00233      * @sa Dv::Xml::Node::Ref::nil
00234      */
00235     operator const xml::node*() const;
00236   
00237     /** Retrieve child node with matching name 
00238      * @param ref reference to xml::node of which child is to be found
00239      * @param name to match in returned child node
00240      * @return Dv::Xml::Node::Ref referring to first child of this node that
00241      *  has name or nil.
00242      * @exception Dv::Xml::Exception if this Dv::Xml::Node::Ref does not refer to a valid
00243      *   xml::node.
00244      * @sa Dv::Xml::Node::Ref::nil
00245      */
00246     friend Dv::Xml::Node::Ref operator/(const Dv::Xml::Node::Ref& ref, const std::string& name)
00247       throw (Dv::Xml::Exception);
00248   
00249     /** Return Ref that points past the last child of this node.
00250      * @return nil Dv::Xml::Node::Ref.
00251      * @exception Dv::Xml::Exception if this Dv::Xml::Node::Ref does not refer to a valid 
00252      *  xml::node.
00253      */
00254     Ref end() const throw (Dv::Xml::Exception);
00255   
00256     /** Return next child of parent with same name 
00257      * @return Dv::Xml::Node::Ref referring to next sibling of this node that
00258      *  has the same name.
00259      * @exception Dv::Xml::Exception if this Dv::Xml::Node::Ref
00260      *   does not refer to a valid xml::node.
00261      */
00262     Ref operator++() throw (Dv::Xml::Exception);
00263   
00264     /** Dummy dereference operator, const version.
00265      * This member function is needed to support forward iterator interface.
00266      * @return reference to self. 
00267      */
00268     const Dv::Xml::Node::Ref& operator*() const { return *this; }
00269   
00270     /** Dummy dereference operator, non-const version.
00271      * This member function is needed to support forward iterator interface.
00272      * @return reference to self. 
00273      */
00274     Dv::Xml::Node::Ref& operator*() { return *this; }
00275   
00276     /** Equality test. 
00277      * @param n Dv::Xml::Node::Ref to compare with.
00278      * @param m Dv::Xml::Node::Ref to compare with.
00279      * @return true iff both nodes are nil or they refer to the same xml::node.
00280      */
00281     friend bool operator==(const Dv::Xml::Node::Ref& n, const Dv::Xml::Node::Ref& m);
00282   
00283     /** Inequality test. 
00284      * @param m Dv::Xml::Node::Ref to compare with.
00285      * @param n Dv::Xml::Node::Ref to compare with.
00286      * @return true iff the nodes are not equal
00287      * @sa Dv::Xml::Node::Ref::operator==
00288      */
00289     friend bool operator!=(const Dv::Xml::Node::Ref m, const Dv::Xml::Node::Ref& n) {
00290         return ! (m == n); 
00291         }
00292   
00293     /** Append new child node with given name.
00294      * @param ref to node to which a new child node will be appended.
00295      * @param name for new child node.
00296      * @return Dv::Xml::Node::Ref referring to new child of this node that
00297      *  has name.
00298      * @exception Dv::Xml::Exception if \a ref does not refer to 
00299      *  a valid xml::node.
00300      */
00301     friend Dv::Xml::Node::Ref operator>>(const Dv::Xml::Node::Ref& ref, const std::string& name)
00302       throw (Dv::Xml::Exception);
00303   
00304     /** Append copy of node to children of this node.
00305      * @param ref reference to xml::node to which a child will be added.
00306      * @param child a copy of which will be added to the children of this node.
00307      * @return Dv::Xml::Node::Ref referring to the new child of this node.
00308      * @exception Dv::Xml::Exception if either of the parameters does not refer to a 
00309      *  valid xml::node.
00310      */
00311     friend Dv::Xml::Node::Ref operator>>(const Dv::Xml::Node::Ref& ref,
00312       const Dv::Xml::Node::Ref& child) throw (Dv::Xml::Exception);
00313   
00314     /** Check whether this node has an attribute value for given key.
00315      * @param name key of attribute
00316      * @return true iff this node has an attribute value of this key.
00317      * @exception Dv::Xml::Exception if this Dv::Xml::Node::Ref does not refer
00318      *  to a valid xml::node.
00319      */
00320     bool defined(const std::string& name) const throw (Dv::Xml::Exception);
00321   
00322     /** Retrieve attribute value. 
00323      * @param name key of attribute
00324      * @return value of attribute
00325      * @exception Dv::Xml::Exception if this node has no attribute for key
00326      * "name" or this node does not refer to a valid xml::node.
00327      */
00328     const std::string operator()(const std::string& name) const throw (Dv::Xml::Exception);
00329   
00330     /** Set attribute value. 
00331      * @param name key of attribute
00332      * @return AttributeReference that can be used to either retrieve
00333      *   the current value of assign a new one. If there is no
00334      *   attribute value, the empty string will be returned (and
00335      *   stored).
00336      * @exception Dv::Xml::Exception if this Dv::Xml::Node::Ref does not refer
00337      *  to a valid xml::node.
00338      * @sa Dv::Xml::Node::AttributeReference
00339      * \code
00340      * Dv::Xml::Node::Ref n;
00341      * ..
00342      * n["name"] = "fred";
00343      * std::cout << n["name"] << std::endl;
00344      * \endcode
00345      */
00346     AttributeReference operator[](const std::string& name) throw (Dv::Xml::Exception);
00347   
00348     /** Retrieve text content. 
00349      * Note that the resulting string is trimmed, i.e. leading and
00350      * trailing white space is removed. To prevent trimming: use
00351      * Dv::Xml::Node::Ref::str(false).
00352      * @return text content of text-xml::node children of this
00353      *   Dv::Xml::Node::Ref.
00354      * @exception Dv::Xml::Exception if this Dv::Xml::Node::Ref does not refer 
00355      *  to a valid xml::node.
00356      * @sa Dv::Xml::Node::Ref::str
00357      */
00358     operator std::string() const throw (Dv::Xml::Exception);
00359 
00360     /** Template user-defined conversion function.
00361      * @warning This will not work for std::string (and others), unless
00362      *   the "assignment" syntax is used for explicit initializtion,
00363      *   as illustrated by the following example.
00364      * \code
00365      * Dv::Xml::Node::Ref n;
00366      * std::string s1(n["a"]); // error
00367      * std::string s2 = n["a"]; // OK
00368      * \endcode
00369      * The initialization of @a s1 is ambiguous: \a n["a"] may be converted
00370      * to a (single) paramater for a  std::string ctor using at least two
00371      * instantiations of the user-defined conversion template below:
00372      * - std::string(const char*)
00373      * - std::string(const std::string&)
00374      */
00375      template<typename T>
00376      operator T() const {
00377        typename noconst<T>::mutable_type t;
00378        Dv::Util::fromstring(t, str());
00379        return t;
00380        }
00381   
00382     /** Retrieve text content. 
00383      * @return text content of text-xml::node children of this
00384      *   Dv::Xml::Node::Ref.
00385      * @param trim if true, remove leading and trailing white space from result.
00386      * @exception Dv::Xml::Exception if this Dv::Xml::Node::Ref does not refer 
00387      *  to a valid xml::node.
00388      */
00389     std::string str(bool trim=true) const throw (Dv::Xml::Exception);
00390   
00391   
00392     /** Set text content.
00393      * @param text to be used as content for this node.
00394      * @return reference to this node. All children of this node will 
00395      *  have been zapped and a new single child xml::node of type text
00396      *  will have been inserted.
00397      * @exception Dv::Xml::Exception if this Dv::Xml::Node::Ref does not
00398      *  refer to a valid xml::node.
00399      */
00400     Ref& operator=(const std::string& text) throw (Dv::Xml::Exception);
00401 
00402     /** Set content of node referred to by this reference, after converting
00403      * the parameter object to a std::string. The conversion is done
00404      * using Dv::Util::tostring<T>.
00405      * @param t value to assign, after conversion, to attribute.
00406      * @return string representation of @a t.
00407      */
00408     template <typename T>
00409     const std::string operator=(const T& t) {
00410       return this->operator=(Dv::Util::tostring<T>(t));
00411       }
00412   
00413     /** Replace this node by another one. The replacement will
00414      * take place in the tree of which this Ref is a part.
00415      * @param ref reference to xml::node of which a copy will replace this node.
00416      * @return reference to this Dv::Xml::Node::Ref.
00417      * @warning the xml::node to which ref refers will be copied.
00418      */
00419     Ref& replace(const Ref& ref);
00420   
00421   protected:
00422     /** Constructor.
00423      * @param it iterator, possibly referring to xml::node
00424      * @param nil true iff it does not refer to a valid xml::node.
00425      */
00426     explicit Ref(xml::node::iterator it, bool nil=false);
00427     /** Constructor.
00428      * @param it const_iterator, possibly referring to xml::node
00429      * @param nil true iff it does not refer to a valid xml::node.
00430      */
00431     explicit Ref(xml::node::const_iterator it, bool nil=false);
00432     /** Throw an exception if this Dv::Xml::Node::Ref object does
00433      * not refer to a valid xml::node.
00434      * @param message to be used in the generated exception.
00435      */
00436     void assert_valid(const std::string& message) const throw (Dv::Xml::Exception);
00437   
00438   private:
00439     /** Refers to xml::node, if valid. */
00440     xml::node::iterator it_;
00441     /** False iff it_ refers to a valid xml::node. */
00442     bool nil_;
00443   };
00444 
00445   /** Default constructor. */
00446   Node(): xml::node() {}
00447 
00448   /** Create an xml::node with a given name.
00449    * @param name of new xml::node.
00450    */
00451   explicit Node(const std::string& name): xml::node(name.c_str()) {}
00452 
00453   /** Create an xml::node with a given name and content.
00454    * @param name of new xml::node.
00455    * @param content text content of new xml::node.
00456    */
00457   Node(const std::string& name, const std::string& content): 
00458     // xml::node(name.c_str(), Dv::Xml::to_html(content).c_str()) {}
00459     xml::node(name.c_str(), content.c_str()) {}
00460 
00461   /** Return a Dv::Xml::Node::Ref object referring to this Node. */
00462   Ref ref() const { 
00463     return Ref(const_cast<xml::node*>(static_cast<const xml::node*>(this))->self()); 
00464     }
00465 
00466   /** Return a Dv::Xml::Node::Ref object referring to this Node. */
00467   operator Ref() { return ref(); }
00468 
00469   /** Set attribute value. 
00470    * @param name key of attribute
00471    * @return Dv::Xml::Node::AttributeReference that can be used to either retrieve
00472    *   the current value of assign a new one. If there
00473    * @sa Dv::Xml::Node::AttributeReference
00474    * \code
00475    * Dv::Xml::Node::Ref n;
00476    * ..
00477    * n["name"] = "fred";
00478    * std::cout << n["name"] << std::endl;
00479    * \endcode
00480    */
00481   AttributeReference operator[](const std::string& name) { return ref()[name]; }
00482 
00483   /** Retrieve attribute value. 
00484    * @param name key of attribute
00485    * @return value of attribute
00486    * @exception Dv::Xml::Exception if this node has no attribute for key "name".
00487    */
00488   const std::string operator()(const std::string& name) const throw (Dv::Xml::Exception) { 
00489     return ref()(name); 
00490     }
00491 
00492   /** Return Ref that points past the last child of this node.
00493    * @return nil Dv::Xml::Node::Ref.
00494    */
00495   const Ref end() const { return ref().end(); }
00496 
00497   /** @return name of underlying xml::node */
00498   std::string name() const { return get_name(); }
00499 
00500 };
00501 
00502 /** Print an XML tree rooted at the node referred to by a
00503  *   Dv::Xml::Node::Ref object. 
00504  * @param os stream to print to
00505  * @param ref reference to xml::node (may be invalid)
00506  * @warning The function will print "nil" if the object does
00507  *   not refer to a valid xml::node.
00508  */
00509 std::ostream&
00510 operator<<(std::ostream& os, const Dv::Xml::Node::Ref& ref);
00511 
00512 }}
00513 
00514 
00515 #endif

dvxml-0.1.4 [19 September, 2003]