00001 /* 00002 * The information in this file is 00003 * Copyright(c) 2007 Ball Aerospace & Technologies Corporation 00004 * and is subject to the terms and conditions of the 00005 * GNU Lesser General Public License Version 2.1 00006 * The license text is available from 00007 * http://www.gnu.org/licenses/lgpl.html 00008 */ 00009 00010 #ifndef XMLWRITER_H 00011 #define XMLWRITER_H 00012 00013 #include "StringUtilities.h" 00014 #include "XercesIncludes.h" 00015 #include "xmlbase.h" 00016 00017 #include <algorithm> 00018 #include <iterator> 00019 #include <sstream> 00020 #include <stack> 00021 #include <string> 00022 #include <vector> 00023 00024 class Font; 00025 00026 /** @file xmlwriter.h 00027 * @brief XML utilities and functionality for writing/generating a DOM 00028 */ 00029 00030 /** 00031 * This class generates a DOM tree and associated XML. 00032 * Many of the functions in this class use the \b current DOM node. 00033 * The \b current DOM node is determined as follows: 00034 * <ul> 00035 * <li> If the function takes a \e pOwner argument and one is specified, this is the \b current DOM node. 00036 * <li> If the \b addpoint stack is not empty, the top of the stack is the \b current DOM node. 00037 * <li> Finally, the root element of the DOM tree is used as the \b current DOM node. 00038 * </ul> 00039 * This allows a caller to extend the depth of the DOM tree by pushing and popping DOM elements 00040 * to the \b addpoint stack. The following example code generates this DOM tree. 00041 * 00042 * @code 00043 * <topLevel> 00044 * <child a="42" b="This is an attribute"> 00045 * <grandchild> 00046 * Here is some text. 00047 * </grandchild> 00048 * </child> 00049 * <anotherchild val="1 2 3 4"/> 00050 * </topLevel> 00051 * @endcode 00052 * 00053 * @code 00054 * XMLWriter xml("topLevel"); 00055 * xml.pushAddPoint(xml.addElement("child")); 00056 * xml.addAttr("a", 42); 00057 * xml.addAttr("b", "This is an attribute"); 00058 * xml.pushAddPoint(xml.addElement("grandchild")); 00059 * xml.addText("Here is some text."); 00060 * xml.popAddPoint(); 00061 * xml.popAddPoint(); 00062 * vector<int> intVect; 00063 * intVect.push_back(1); 00064 * intVect.push_back(2); 00065 * intVect.push_back(3); 00066 * intVect.push_back(4); 00067 * xml.pushAddPoint(xml.addElement("anotherchild")); 00068 * xml.addAttr("val", intVect); 00069 * string xmlString = xml.writeToString(); 00070 * @endcode 00071 * 00072 * @ingroup app_xml 00073 * 00074 * @par requirements 00075 * Apache Xerces-C++ version 3.1.1 00076 */ 00077 class XMLWriter : public XmlBase 00078 { 00079 public: 00080 //@{ 00081 /** 00082 * Create an %XMLWriter. 00083 * 00084 * @param pRootElementName 00085 * The name of the top level element 00086 * 00087 * @param pLog 00088 * Optional MessageLog to be passed to XmlBase 00089 * 00090 * @param useNamespace 00091 * If \c true, the app namespace will be exported, if \c false, no namespace will be exported. 00092 * 00093 * @throw XmlBase::XmlException 00094 * When unable to create the Xerces DOM document. 00095 */ 00096 XMLWriter(const char* pRootElementName, MessageLog* pLog = NULL, bool useNamespace = true); 00097 00098 /** 00099 * Create an %XMLWriter. 00100 * 00101 * @param rootElementName 00102 * The name of the top level element 00103 * 00104 * @param xmlNamespace 00105 * Use this namespace instead of the default one. 00106 * 00107 * @param pLog 00108 * Optional MessageLog to be passed to XmlBase 00109 * 00110 * @param useNamespace 00111 * If \c true, the app namespace will be exported, if \c false, no namespace will be exported. 00112 * 00113 * @throw XmlBase::XmlException 00114 * When unable to create the Xerces DOM document. 00115 */ 00116 XMLWriter(const std::string& rootElementName, const std::string& xmlNamespace, MessageLog* pLog = NULL, 00117 bool useNamespace = true); 00118 00119 /** 00120 * Destroy and cleanup the %XMLWriter object. 00121 */ 00122 ~XMLWriter(); 00123 //@} 00124 00125 //@{ 00126 /** 00127 * Add an attribute to the current DOM node 00128 * This templated class converts \e value to 00129 * a string using a std::stringstream. 00130 * 00131 * @param pName 00132 * The name of the attribute. 00133 * 00134 * @param value 00135 * The value of the attribute. \b T 00136 * must have \e operator<< defined for 00137 * a std::stringstream. 00138 * 00139 * @return Returns \c true on success and \c false on failure. 00140 * 00141 * @see std::stringstream 00142 */ 00143 template <class T> 00144 bool addAttr(const char* pName, T value) 00145 { 00146 return addAttr(pName, static_cast<std::string>(StringUtilities::toXmlString(value)), NULL); 00147 } 00148 00149 /** 00150 * Add a vector attribute to the current DOM node 00151 * This templated class converts \e value to 00152 * an XML list of strings using a std::stringstream. 00153 * 00154 * @param pName 00155 * The name of the attribute. 00156 * 00157 * @param value 00158 * The value of the attribute. \b T 00159 * must have \e operator<< defined for 00160 * a std::stringstream. 00161 * 00162 * @return Returns \c true on success and \c false on failure. 00163 * 00164 * @see std::stringstream 00165 */ 00166 template<class T> 00167 bool addAttr(const char* pName, std::vector<T> value) 00168 { 00169 std::stringstream buf; 00170 std::copy(value.begin(), value.end(), std::ostream_iterator<T>(buf, " ")); 00171 return addAttr(pName, buf.str(), NULL); 00172 } 00173 00174 /** 00175 * Add a string attribute to the specified DOM node. 00176 * 00177 * @param pName 00178 * The name of the attribute. 00179 * 00180 * @param pValue 00181 * The value of the attribute. 00182 * 00183 * @param pOwner 00184 * The DOM node which will have the attribute. 00185 * 00186 * @return Returns \c true on success and \c false on failure. 00187 */ 00188 bool addAttr(const char* pName, const char* pValue, XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* pOwner); 00189 00190 /** 00191 * Add a string attribute to the specified DOM node. 00192 * 00193 * @param pName 00194 * The name of the attribute. 00195 * 00196 * @param value 00197 * The value of the attribute. 00198 * 00199 * @param pOwner 00200 * The DOM node which will have the attribute. 00201 * 00202 * @return Returns \c true on success and \c false on failure. 00203 */ 00204 bool addAttr(const char* pName, const std::string value, XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* pOwner) 00205 { 00206 return addAttr(pName, value.c_str(), pOwner); 00207 } 00208 //@} 00209 00210 //@{ 00211 /** 00212 * Add an element DOM node. 00213 * 00214 * @param pName 00215 * The name of the element. 00216 * @param pOwner 00217 * The optional DOM node which will be the parent 00218 * of this element. If not specified, the current 00219 * DOM node is used as the parent. 00220 * 00221 * @return The new DOM element or \c NULL on failure. 00222 */ 00223 XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* addElement(const char* pName, 00224 XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* pOwner = NULL); 00225 //@} 00226 00227 //@{ 00228 /** 00229 * Add an element DOM node. 00230 * 00231 * @param name 00232 * The name of the element. 00233 * 00234 * @param pOwner 00235 * The optional DOM node which will be the parent 00236 * of this element. If not specified, the current 00237 * DOM node is used as the parent. 00238 * 00239 * @return The new DOM element or \c NULL on failure. 00240 */ 00241 XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* addElement(const std::string& name, 00242 XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* pOwner = NULL) 00243 { 00244 return addElement(name.c_str(), pOwner); 00245 } 00246 //@} 00247 00248 //@{ 00249 /** 00250 * Add an element DOM node. 00251 * 00252 * @param pName 00253 * The name of the element. 00254 * 00255 * @param font 00256 * The font specification to be written in the XMLWriter. 00257 * 00258 * @param pOwner 00259 * The optional DOM node which will be the parent 00260 * of this element. If not specified, the current 00261 * DOM node is used as the parent. 00262 * 00263 * @return The new DOM element or \c NULL on failure. 00264 */ 00265 XERCES_CPP_NAMESPACE_QUALIFIER DOMElement* addFontElement(const char* pName, const Font& font, 00266 XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* pOwner = NULL); 00267 //@} 00268 00269 //@{ 00270 /** 00271 * Add a value as a text DOM node. 00272 * This templated class converts \e value to 00273 * a string using a std::stringstream. 00274 * 00275 * @param value 00276 * The value of the text node. \b T must 00277 * have \e operator<< defined on std::stringstream. 00278 * 00279 * @param pOwner 00280 * The optional DOM node which will be the parent 00281 * of this element. If not specified, the current 00282 * DOM node is used as the parent. 00283 * 00284 * @see std::stringstream 00285 * 00286 * @return The new DOM text node or \c NULL on failure. 00287 */ 00288 template<class T> 00289 XERCES_CPP_NAMESPACE_QUALIFIER DOMText* addText(T value, XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* pOwner = NULL) 00290 { 00291 std::stringstream buf; 00292 buf << value; 00293 return addText(buf.str(), pOwner); 00294 } 00295 00296 /** 00297 * Add a vector of values as a text DOM node. 00298 * This templated class converts \e value to 00299 * an XML list of strings using a std::stringstream. 00300 * 00301 * @param value 00302 * The value of the text node. \b T must 00303 * have \e operator<< defined on std::stringstream. 00304 * 00305 * @param pOwner 00306 * The optional DOM node which will be the parent 00307 * of this element. If not specified, the current 00308 * DOM node is used as the parent. 00309 * 00310 * @return The new DOM text node or \c NULL on failure. 00311 * 00312 * @see std::stringstream 00313 */ 00314 template<class T> 00315 XERCES_CPP_NAMESPACE_QUALIFIER DOMText* addText(std::vector<T> value, 00316 XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* pOwner = NULL) 00317 { 00318 std::stringstream buf; 00319 std::copy(value.begin(), value.end(), std::ostream_iterator<T>(buf, " ")); 00320 return addText(buf.str(), pOwner); 00321 } 00322 00323 /** 00324 * Add a string value as a text DOM node. 00325 * 00326 * @param pValue 00327 * The value of the text node. 00328 * 00329 * @param pOwner 00330 * The optional DOM node which will be the parent 00331 * of this element. If not specified, the current 00332 * DOM node is used as the parent. 00333 * 00334 * @return The new DOM text node or \c NULL on failure. 00335 */ 00336 XERCES_CPP_NAMESPACE_QUALIFIER DOMText* addText(const char* pValue, 00337 XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* pOwner = NULL); 00338 00339 /** 00340 * Add a string value as a text DOM node. 00341 * 00342 * @param value 00343 * The value of the text node. 00344 * 00345 * @param pOwner 00346 * The optional DOM node which will be the parent 00347 * of this element. If not specified, the current 00348 * DOM node is used as the parent. 00349 * 00350 * @return The new DOM text node or \c NULL on failure. 00351 */ 00352 XERCES_CPP_NAMESPACE_QUALIFIER DOMText* addText(const std::string& value, 00353 XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* pOwner = NULL) 00354 { 00355 return addText(value.c_str(),pOwner); 00356 } 00357 //@} 00358 00359 //@{ 00360 /** 00361 * Determine if a named element exists. 00362 * This will check descendents of \e pOwner, if 00363 * specified or descendents of the current DOM node. 00364 * 00365 * @param pName 00366 * The name of the element to find. 00367 * 00368 * @param pOwner 00369 * The optional DOM node which will be searched. 00370 * If not specified, the current DOM node is used 00371 * as the parent. 00372 * 00373 * @return Returns \c true if the element exists and \c false if it does not exist. 00374 */ 00375 bool elementExists(const char* pName, XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* pOwner = NULL); 00376 00377 /** 00378 * Remove a named element from a DOM tree. 00379 * This will remove an element if it is a child of 00380 * \e pOwner, if specified or if it is a child of 00381 * the current DOM node otherwise. 00382 * 00383 * @param pName 00384 * The name of the element to remove. 00385 * 00386 * @param pOwner 00387 * The optional DOM node which will be searched. 00388 * If not specified, the current DOM node is used 00389 * as the parent. 00390 */ 00391 void removeElement(const char* pName, XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* pOwner = NULL); 00392 00393 /** 00394 * Remove a specified node from a DOM tree. 00395 * This will remove a node if it is a child of 00396 * \e pOwner, if specified or if it is a child of 00397 * the current DOM node otherwise. 00398 * 00399 * @param pChild 00400 * The DOM node to remove. 00401 * 00402 * @param pOwner 00403 * The optional DOM node which will be searched. 00404 * If not specified, the current DOM node is used 00405 * as the parent. 00406 */ 00407 void removeChild(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* pChild, 00408 XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* pOwner = NULL); 00409 //@} 00410 00411 //@{ 00412 /** 00413 * This flag determines if only a single child DOM node 00414 * with a name can be added. If set, child DOM nodes with 00415 * the name of a newly added DOM node are removed. If clear, 00416 * multiple DOM nodes with the same name may be added. 00417 * 00418 * @param v 00419 * The new value of the flag. 00420 * 00421 * @return The old value of the flag. 00422 */ 00423 bool setSingleChildInstance(bool v) 00424 { 00425 bool r(mSingleChildInstance); 00426 mSingleChildInstance = v; 00427 return r; 00428 } 00429 //@} 00430 00431 //@{ 00432 /** 00433 * Write a DOM tree to a file as XML. 00434 * 00435 * @param pFp 00436 * File pointer that the DOM tree will be written to. 00437 * 00438 * @throw XmlBase::XmlException 00439 * When Xerces is not able to generate the XML. 00440 */ 00441 void writeToFile(FILE* pFp); 00442 00443 /** 00444 * Write a DOM tree to a string as XML. 00445 * 00446 * @return The DOM tree as a string of XML. 00447 * 00448 * @throw XmlBase::XmlException 00449 * When Xerces is not able to generate the XML; 00450 * or when the \e char type is not 1 byte in size. 00451 * (this should not occur on any modern computer) 00452 */ 00453 std::string writeToString(); 00454 //@} 00455 00456 //@{ 00457 /** 00458 * Push a new current DOM node to the add point stack. 00459 * See the class documentation for XMLWriter for further 00460 * information on add points. 00461 * 00462 * @param pNode 00463 * The new current DOM node. If this is \c NULL, the document 00464 * root is pushed onto the stack. 00465 * 00466 * @see XMLWriter 00467 */ 00468 void pushAddPoint(XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* pNode = NULL); 00469 00470 /** 00471 * Pop the current DOM node from the add point stack. 00472 * See the class documentation for XMLWriter for further 00473 * information on add points. 00474 * 00475 * @return The DOM node which was just popped from the stack. 00476 * 00477 * @see XMLWriter 00478 */ 00479 XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* popAddPoint(); 00480 00481 /** 00482 * Find out what DOM node is on top of the add point stack without removing the node. 00483 * See the class documentation for XMLWriter for further 00484 * information on add points. 00485 * 00486 * @return The DOM node which is on the top of the stack. 00487 * 00488 * @see XMLWriter 00489 */ 00490 XERCES_CPP_NAMESPACE_QUALIFIER DOMNode* peekAddPoint(); 00491 //@} 00492 00493 private: 00494 XERCES_CPP_NAMESPACE_QUALIFIER DOMDocument* mpDoc; 00495 std::stack<XERCES_CPP_NAMESPACE_QUALIFIER DOMNode*> mpAddPoint; 00496 XERCES_CPP_NAMESPACE_QUALIFIER DOMImplementation* mpImpl; 00497 bool mSingleChildInstance; 00498 XMLCh* mpWithNamespace; 00499 }; 00500 00501 /** Explicit template specializations can not go in the class declaration 00502 as this is not supported in Studio 11. 00503 **/ 00504 00505 template<> bool XMLWriter::addAttr<std::string>(const char* pName, std::string value); 00506 template<> bool XMLWriter::addAttr<char*>(const char* pName, char* pValue); 00507 template<> bool XMLWriter::addAttr<const char*>(const char* pName, const char* pValue); 00508 00509 /** 00510 * This class is intended to be used by the XML_ADD_POINT macro and should not 00511 * be used directly. 00512 */ 00513 class XmlAddPoint 00514 { 00515 public: 00516 XmlAddPoint(XMLWriter& writer, const std::string& name) : 00517 mWriter(writer), 00518 mFirstTime(true) 00519 { 00520 writer.pushAddPoint(writer.addElement(name.c_str())); 00521 } 00522 ~XmlAddPoint() 00523 { 00524 mWriter.popAddPoint(); 00525 } 00526 bool isFirstTime() 00527 { 00528 bool firstTime = mFirstTime; 00529 mFirstTime = false; 00530 return firstTime; 00531 } 00532 private: 00533 XmlAddPoint& operator=(const XmlAddPoint& rhs); 00534 00535 XMLWriter& mWriter; 00536 bool mFirstTime; 00537 }; 00538 00539 /** 00540 * Adds an add-point to the XML and to the C++ stack. 00541 * 00542 * This macro causes an add-point to be inserted into the writer, and 00543 * simultaneously added to an RAII object on the C++ stack. This causes the 00544 * indentation of the C++ writing code to match the indentation of the 00545 * resulting XML. This in turn makes it easier to visualize the structure of 00546 * the resulting XML and makes it impossible mismatch the pushing and popping 00547 * of add points. 00548 * 00549 * @code 00550 * XML_ADD_POINT (writer, my_add_point) // pushes an add point on the writer 00551 * { 00552 * writer.addAttribute("my_attr", myValue); 00553 * } // pops the add point from the writer 00554 * @endcode 00555 * 00556 * @param writer 00557 * The XMLWriter to push the new element / add-point on to. 00558 * 00559 * @param name 00560 * The name for the new element. 00561 */ 00562 #define XML_ADD_POINT(writer, name) \ 00563 for (XmlAddPoint name(writer, #name); name.isFirstTime(); ) 00564 00565 /** 00566 * This method is intended to be called from the XML_ADD_CONTAINER macro and 00567 * should not be called directly. 00568 */ 00569 template <class Iter> 00570 void writeContainerElements(XMLWriter& writer, Iter pStartIter, Iter pStopIter) 00571 { 00572 for (Iter pIter = pStartIter; pIter != pStopIter; ++pIter) 00573 { 00574 XML_ADD_POINT (writer, element) 00575 { 00576 writer.addAttr("value", *pIter); 00577 } 00578 } 00579 } 00580 00581 /** 00582 * Writes a container of data to the specified XMLWriter 00583 * 00584 * @code 00585 * XML_ADD_POINT (writer, my_data) 00586 * { 00587 * vector<int> v(12, 65); // a vector of 12 65's 00588 * XML_ADD_CONTAINER(writer, value, v.begin(), v.end()); 00589 * } 00590 * @endcode 00591 * The above code will write out the vector of 12 values to the specified 00592 * XMLWriter. The data can be read back in later with readContainerElements. 00593 * 00594 * @param writer 00595 * The XMLWriter to write the data to. 00596 * 00597 * @param name 00598 * The name to give each value. 00599 * 00600 * @param startIter 00601 * A container iterator specifying the first element to write. 00602 * 00603 * @param stopIter 00604 * A container iterator specifying where to stop writing. 00605 */ 00606 #define XML_ADD_CONTAINER(writer, name, startIter, stopIter) \ 00607 XML_ADD_POINT (writer, name) \ 00608 { \ 00609 writeContainerElements(writer, startIter, stopIter); \ 00610 } 00611 00612 #endif