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] |