00001 /* 00002 Copyright (c) 2010, The Cinder Project 00003 All rights reserved. 00004 00005 This code is designed for use with the Cinder C++ library, http://libcinder.org 00006 00007 Redistribution and use in source and binary forms, with or without modification, are permitted provided that 00008 the following conditions are met: 00009 00010 * Redistributions of source code must retain the above copyright notice, this list of conditions and 00011 the following disclaimer. 00012 * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and 00013 the following disclaimer in the documentation and/or other materials provided with the distribution. 00014 00015 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED 00016 WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 00017 PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 00018 ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED 00019 TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 00020 HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00021 NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 00022 POSSIBILITY OF SUCH DAMAGE. 00023 */ 00024 00025 #pragma once 00026 00027 #include "cinder/Cinder.h" 00028 #include "cinder/Stream.h" 00029 #include "cinder/DataSource.h" 00030 #include "cinder/DataTarget.h" 00031 #include "cinder/Exception.h" 00032 #include "cinder/Utilities.h" 00033 00034 #include <iterator> 00035 #include <string> 00036 #include <vector> 00037 #include <list> 00038 00040 namespace rapidxml { 00041 template<class Ch> class xml_document; 00042 template<class Ch> class xml_node; 00043 }; 00045 00046 namespace cinder { 00047 00048 class XmlTree { 00049 public: 00051 class ConstIter { 00052 public: 00054 ConstIter( const std::list<XmlTree> *sequence ); 00055 ConstIter( const std::list<XmlTree> *sequence, std::list<XmlTree>::const_iterator iter ); 00056 ConstIter( const XmlTree &root, const std::string &filterPath, bool caseSensitive = false, char separator = '/' ); 00058 00060 const XmlTree& operator*() const { return *mIterStack.back(); } 00062 const XmlTree* operator->() const { return &(*mIterStack.back()); } 00063 00065 ConstIter& operator++() { 00066 increment(); 00067 return *this; 00068 } 00069 00071 const ConstIter operator++(int) { 00072 ConstIter prev( *this ); 00073 ++(*this); 00074 return prev; 00075 } 00076 00077 bool operator!=( const ConstIter &rhs ) { return ( mSequenceStack.back() != rhs.mSequenceStack.back() ) || ( mIterStack.back() != rhs.mIterStack.back() ); } 00078 bool operator==( const ConstIter &rhs ) { return ( mSequenceStack.back() == rhs.mSequenceStack.back() ) && ( mIterStack.back() == rhs.mIterStack.back() ); } 00079 00080 protected: 00082 void increment(); 00083 void setToEnd( const std::list<XmlTree> *seq ); 00084 bool isDone() const; 00085 00086 std::vector<const std::list<XmlTree>*> mSequenceStack; 00087 std::vector<std::list<XmlTree>::const_iterator> mIterStack; 00088 std::vector<std::string> mFilter; 00089 bool mCaseSensitive; 00091 }; 00092 00094 class Iter : public XmlTree::ConstIter { 00095 public: 00097 Iter( std::list<XmlTree> *sequence ) 00098 : ConstIter( sequence ) 00099 {} 00100 00101 Iter( std::list<XmlTree> *sequence, std::list<XmlTree>::iterator iter ) 00102 : ConstIter( sequence, iter ) 00103 {} 00104 00105 Iter( XmlTree &root, const std::string &filterPath, bool caseSensitive, char separator ) 00106 : ConstIter( root, filterPath, caseSensitive, separator ) 00107 {} 00109 00110 00112 XmlTree& operator*() const { return const_cast<XmlTree&>(*mIterStack.back()); } 00114 XmlTree* operator->() const { return const_cast<XmlTree*>( &(*mIterStack.back()) ); } 00115 00117 Iter& operator++() { 00118 increment(); 00119 return *this; 00120 } 00121 00123 const Iter operator++(int) { 00124 Iter prev( *this ); 00125 ++(*this); 00126 return prev; 00127 } 00128 00129 bool operator!=( const Iter &rhs ) { return ( mSequenceStack.back() != rhs.mSequenceStack.back() ) || ( mIterStack.back() != rhs.mIterStack.back() ); } 00130 bool operator==( const Iter &rhs ) { return ( mSequenceStack.back() == rhs.mSequenceStack.back() ) && ( mIterStack.back() == rhs.mIterStack.back() ); } 00131 00132 }; 00133 00135 class Attr { 00136 public: 00138 Attr( XmlTree *xml, const std::string &name, const std::string &value ) 00139 : mXml( xml ), mName( name ), mValue( value ) 00140 {} 00141 00143 operator const std::string&() const { return mValue; } 00145 template<typename T> 00146 Attr& operator=( const T& val ) { mValue = toString( val ); mXml->setAttribute( mName, mValue ); return *this; } 00147 00149 template<typename T> 00150 T as() const { return fromString<T>( mValue ); } 00151 00153 bool empty() const { return mValue.empty(); } 00154 00156 const std::string& getName() const { return mName; } 00158 std::string getValue() const { return mValue; } 00161 template<typename T> 00162 T getValue() const { return fromString<T>( mValue ); } 00163 00165 void setValue( const std::string &value ) { mValue = value; } 00167 template<typename T> 00168 void setValue( const T &value ) { mValue = boost::lexical_cast<std::string>( value ); } 00169 00170 private: 00171 XmlTree *mXml; 00172 std::string mName, mValue; 00173 }; 00174 00176 class ParseOptions { 00177 public: 00179 ParseOptions() : mParseComments( false ), mCollapseCData( true ), mIgnoreDataChildren( true ) {} 00180 00182 ParseOptions& parseComments( bool parse = true ) { mParseComments = parse; return *this; } 00184 ParseOptions& collapseCData( bool collapse = true ) { mCollapseCData = collapse; return *this; } 00186 ParseOptions& ignoreDataChildren( bool ignore = true ) { setIgnoreDataChildren( ignore ); return *this; } 00187 00189 bool getParseComments() const { return mParseComments; } 00191 void setParseComments( bool parseComments = true ) { mParseComments = parseComments; } 00193 bool getCollapseCData() const { return mCollapseCData; } 00195 void setCollapseCData( bool collapseCData = true ) { mCollapseCData = collapseCData; } 00197 bool getIgnoreDataChildren() const { return mIgnoreDataChildren; } 00199 void setIgnoreDataChildren( bool ignore = true ) { mIgnoreDataChildren = ignore; } 00200 00201 private: 00202 bool mParseComments, mCollapseCData, mIgnoreDataChildren; 00203 }; 00204 00206 typedef enum { NODE_UNKNOWN, NODE_DOCUMENT, NODE_ELEMENT, NODE_CDATA, NODE_COMMENT, NODE_DATA } NodeType; 00207 00209 XmlTree() : mParent( 0 ), mNodeType( NODE_ELEMENT ) {} 00210 00212 XmlTree( const XmlTree &rhs ); 00213 XmlTree& operator=( const XmlTree &rhs ); 00214 00217 explicit XmlTree( DataSourceRef dataSource, ParseOptions parseOptions = ParseOptions() ) { 00218 loadFromDataSource( dataSource, this, parseOptions ); 00219 } 00220 00222 explicit XmlTree( const std::string &xmlString, ParseOptions parseOptions = ParseOptions() ); 00223 00225 explicit XmlTree( const std::string &tag, const std::string &value, XmlTree *parent = 0, NodeType type = NODE_ELEMENT ) 00226 : mTag( tag ), mValue( value ), mParent( parent ), mNodeType( type ) 00227 {} 00228 00230 static XmlTree createDoc() { return XmlTree( "", "", 0, NODE_DOCUMENT ); } 00231 00233 NodeType getNodeType() const { return mNodeType; } 00235 void setNodeType( NodeType type ) { mNodeType = type; } 00237 bool isDocument() const { return mNodeType == NODE_DOCUMENT; } 00239 bool isElement() const { return mNodeType == NODE_ELEMENT; } 00241 bool isCData() const { return mNodeType == NODE_CDATA; } 00243 bool isComment() const { return mNodeType == NODE_COMMENT; } 00244 00246 const std::string& getTag() const { return mTag; } 00248 void setTag( const std::string &tag ) { mTag = tag; } 00249 00251 std::string getValue() const { return mValue; } 00253 template<typename T> 00254 T getValue() const { return boost::lexical_cast<T>( mValue ); } 00256 template<typename T> 00257 T getValue( const T &defaultValue ) const { try { return boost::lexical_cast<T>( mValue ); } catch( ... ) { return defaultValue; } } 00258 00260 void setValue( const std::string &value ) { mValue = value; } 00262 template<typename T> 00263 void setValue( const T &value ) { mValue = boost::lexical_cast<std::string>( value ); } 00264 00266 bool hasParent() const { return mParent != NULL; } 00268 XmlTree& getParent() { return *mParent; } 00270 const XmlTree& getParent() const { return *mParent; } 00271 00273 Iter find( const std::string &relativePath, bool caseSensitive = false, char separator = '/' ) { return Iter( *this, relativePath, caseSensitive, separator ); } 00275 ConstIter find( const std::string &relativePath, bool caseSensitive = false, char separator = '/' ) const { return ConstIter( *this, relativePath, caseSensitive, separator ); } 00277 bool hasChild( const std::string &relativePath, bool caseSensitive = false, char separator = '/' ) const; 00278 00280 XmlTree& getChild( const std::string &relativePath, bool caseSensitive = false, char separator = '/' ); 00282 const XmlTree& getChild( const std::string &relativePath, bool caseSensitive = false, char separator = '/' ) const; 00284 std::list<XmlTree>& getChildren() { return mChildren; } 00286 const std::list<XmlTree>& getChildren() const { return mChildren; } 00287 00289 std::list<Attr>& getAttributes() { return mAttributes; } 00291 const std::list<Attr>& getAttributes() const { return mAttributes; } 00292 00294 const Attr& getAttribute( const std::string &attrName ) const; 00295 00297 const Attr operator[]( const std::string &attrName ) const { if( hasAttribute( attrName ) ) return getAttribute(attrName); else return Attr( const_cast<XmlTree*>( this ), attrName, "" ); } 00299 Attr operator[]( const std::string &attrName ) { if( hasAttribute( attrName ) ) return getAttribute(attrName); else return Attr( this, attrName, "" ); } 00300 00302 const XmlTree& operator/( const std::string &childName ) const { return getChild( childName ); } 00304 XmlTree& operator/( const std::string &childName ) { return getChild( childName ); } 00305 00308 template<typename T> 00309 T getAttributeValue( const std::string &attrName ) const { return getAttribute( attrName ).getValue<T>(); } 00312 template<typename T> 00313 T getAttributeValue( const std::string &attrName, const T &defaultValue ) const { 00314 if( hasAttribute( attrName ) ) { 00315 try { 00316 return getAttribute( attrName ).getValue<T>(); 00317 } 00318 catch(...) { 00319 return defaultValue; 00320 } 00321 } 00322 else return defaultValue; 00323 } 00324 00326 XmlTree& setAttribute( const std::string &attrName, const std::string &value ); 00328 template<typename T> 00329 XmlTree& setAttribute( const std::string &attrName, const T &value ) { return setAttribute( attrName, boost::lexical_cast<std::string>( value ) ); } 00331 bool hasAttribute( const std::string &attrName ) const; 00333 std::string getPath( char separator = '/' ) const; 00334 00336 Iter begin() { return Iter( &mChildren ); } 00338 Iter begin( const std::string &filterPath, bool caseSensitive = false, char separator = '/' ) { return Iter( *this, filterPath, caseSensitive, separator ); } 00340 ConstIter begin() const { return ConstIter( &mChildren ); } 00342 ConstIter begin( const std::string &filterPath, bool caseSensitive = false, char separator = '/' ) const { return ConstIter( *this, filterPath, caseSensitive, separator ); } 00344 Iter end() { return Iter( &mChildren, mChildren.end() ); } 00346 ConstIter end() const { return ConstIter( &mChildren, mChildren.end() ); } 00348 void push_back( const XmlTree &newChild ); 00349 00351 std::string getDocType() const { return mDocType; } 00353 void setDocType( const std::string &docType ) { mDocType = docType; } 00354 00356 friend std::ostream& operator<<( std::ostream &out, const XmlTree &xml ); 00358 void write( DataTargetRef target, bool createDocument = true ); 00359 00361 class Exception : public cinder::Exception { 00362 }; 00363 00365 class ExcChildNotFound : public XmlTree::Exception { 00366 public: 00367 ExcChildNotFound( const XmlTree &node, const std::string &childPath ) throw(); 00368 00369 virtual const char* what() const throw() { return mMessage; } 00370 00371 private: 00372 char mMessage[2048]; 00373 }; 00374 00376 class ExcAttrNotFound : public XmlTree::Exception { 00377 public: 00378 ExcAttrNotFound( const XmlTree &node, const std::string &attrName ) throw(); 00379 00380 virtual const char* what() const throw() { return mMessage; } 00381 00382 private: 00383 char mMessage[2048]; 00384 }; 00385 00387 class ExcUnknownNodeType : public cinder::Exception { 00388 }; 00389 00391 std::shared_ptr<rapidxml::xml_document<char> > createRapidXmlDoc( bool createDocument = false ) const; 00392 00393 private: 00394 XmlTree* getNodePtr( const std::string &relativePath, bool caseSensitive, char separator ) const; 00395 void appendRapidXmlNode( rapidxml::xml_document<char> &doc, rapidxml::xml_node<char> *parent ) const; 00396 00397 static std::list<XmlTree>::const_iterator findNextChildNamed( const std::list<XmlTree> &sequence, std::list<XmlTree>::const_iterator firstCandidate, const std::string &searchTag, bool caseSensitive ); 00398 00399 NodeType mNodeType; 00400 std::string mTag; 00401 std::string mValue; 00402 std::string mDocType; // only used on NodeType::NODE_DOCUMENT 00403 XmlTree *mParent; 00404 std::list<XmlTree> mChildren; 00405 std::list<Attr> mAttributes; 00406 00407 static void loadFromDataSource( DataSourceRef dataSource, XmlTree *result, const ParseOptions &parseOptions ); 00408 }; 00409 00410 std::ostream& operator<<( std::ostream &out, const XmlTree &xml ); 00411 00412 } // namespace cinder 00413 00414 namespace std { 00415 00417 template<> 00418 struct iterator_traits<cinder::XmlTree::Iter> { 00419 typedef cinder::XmlTree value_type; 00420 typedef ptrdiff_t difference_type; 00421 typedef forward_iterator_tag iterator_category; 00422 typedef cinder::XmlTree* pointer; 00423 typedef cinder::XmlTree& reference; 00424 }; 00425 00426 template<> 00427 struct iterator_traits<cinder::XmlTree::ConstIter> { 00428 typedef cinder::XmlTree value_type; 00429 typedef ptrdiff_t difference_type; 00430 typedef forward_iterator_tag iterator_category; 00431 typedef const cinder::XmlTree* pointer; 00432 typedef const cinder::XmlTree& reference; 00433 }; 00435 00436 } // namespace std