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 00033 #include <boost/lexical_cast.hpp> 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( const std::string &name, const std::string &value ) 00139 : mName( name ), mValue( value ) 00140 {} 00141 00143 const std::string& getName() const { return mName; } 00145 std::string getValue() const { return mValue; } 00148 template<typename T> 00149 T getValue() const { return boost::lexical_cast<T>( mValue ); } 00150 00152 void setValue( const std::string &value ) { mValue = value; } 00154 template<typename T> 00155 void setValue( const T &value ) { mValue = boost::lexical_cast<std::string>( value ); } 00156 00157 private: 00158 std::string mName, mValue; 00159 }; 00160 00162 class ParseOptions { 00163 public: 00165 ParseOptions() : mParseComments( false ), mCollapseCData( true ) {} 00166 00168 ParseOptions& parseComments( bool parse = true ) { mParseComments = parse; return *this; } 00170 ParseOptions& collapseCData( bool collapse = true ) { mCollapseCData = collapse; return *this; } 00171 00173 bool getParseComments() const { return mParseComments; } 00175 void setParseComments( bool parseComments = true ) { mParseComments = parseComments; } 00177 bool getCollapseCData() const { return mCollapseCData; } 00179 void setCollapseCData( bool collapseCData = true ) { mCollapseCData = collapseCData; } 00180 00181 private: 00182 bool mParseComments, mCollapseCData; 00183 }; 00184 00186 typedef enum NodeType { NODE_UNKNOWN, NODE_DOCUMENT, NODE_ELEMENT, NODE_CDATA, NODE_COMMENT }; 00187 00189 XmlTree() : mParent( 0 ), mNodeType( NODE_ELEMENT ) {} 00190 00193 explicit XmlTree( DataSourceRef dataSource, ParseOptions parseOptions = ParseOptions() ) { 00194 loadFromDataSource( dataSource, this, parseOptions ); 00195 } 00196 00198 explicit XmlTree( const std::string &xmlString, ParseOptions parseOptions = ParseOptions() ); 00199 00201 explicit XmlTree( const std::string &tag, const std::string &value, XmlTree *parent = 0, NodeType type = NODE_ELEMENT ) 00202 : mTag( tag ), mValue( value ), mParent( parent ), mNodeType( type ) 00203 {} 00204 00206 static XmlTree createDoc() { return XmlTree( "", "", 0, NODE_DOCUMENT ); } 00207 00209 NodeType getNodeType() const { return mNodeType; } 00211 void setNodeType( NodeType type ) { mNodeType = type; } 00213 bool isDocument() const { return mNodeType == NODE_DOCUMENT; } 00215 bool isElement() const { return mNodeType == NODE_ELEMENT; } 00217 bool isCData() const { return mNodeType == NODE_CDATA; } 00219 bool isComment() const { return mNodeType == NODE_COMMENT; } 00220 00222 const std::string& getTag() const { return mTag; } 00224 void setTag( const std::string &tag ) { mTag = tag; } 00225 00227 std::string getValue() const { return mValue; } 00229 template<typename T> 00230 T getValue() const { return boost::lexical_cast<T>( mValue ); } 00232 template<typename T> 00233 T getValue( const T &defaultValue ) const { try { return boost::lexical_cast<T>( mValue ); } catch( ... ) { return defaultValue; } } 00234 00236 void setValue( const std::string &value ) { mValue = value; } 00238 template<typename T> 00239 T setValue( const T &value ) { mValue = boost::lexical_cast<std::string>( value ); } 00240 00242 bool hasParent() const { return mParent != NULL; } 00244 XmlTree& getParent() { return *mParent; } 00246 const XmlTree& getParent() const { return *mParent; } 00247 00249 Iter find( const std::string &relativePath, bool caseSensitive = false, char separator = '/' ) { return Iter( *this, relativePath, caseSensitive, separator ); } 00251 ConstIter find( const std::string &relativePath, bool caseSensitive = false, char separator = '/' ) const { return ConstIter( *this, relativePath, caseSensitive, separator ); } 00253 bool hasChild( const std::string &relativePath, bool caseSensitive = false, char separator = '/' ) const; 00254 00256 XmlTree& getChild( const std::string &relativePath, bool caseSensitive = false, char separator = '/' ); 00258 const XmlTree& getChild( const std::string &relativePath, bool caseSensitive = false, char separator = '/' ) const; 00260 std::list<XmlTree>& getChildren() { return mChildren; } 00262 const std::list<XmlTree>& getChildren() const { return mChildren; } 00263 00265 std::list<Attr>& getAttributes() { return mAttributes; } 00267 const std::list<Attr>& getAttributes() const { return mAttributes; } 00268 00270 const Attr& getAttribute( const std::string &attrName ) const; 00271 00274 template<typename T> 00275 T getAttributeValue( const std::string &attrName ) const { return getAttribute( attrName ).getValue<T>(); } 00278 template<typename T> 00279 T getAttributeValue( const std::string &attrName, const T &defaultValue ) const { try { return getAttribute( attrName ).getValue<T>(); } catch( ... ) { return defaultValue; } } 00280 00282 void setAttribute( const std::string &attrName, const std::string &value ); 00284 template<typename T> 00285 void setAttribute( const std::string &attrName, const T &value ) { setAttribute( attrName, boost::lexical_cast<std::string>( value ) ); } 00287 bool hasAttribute( const std::string &attrName ) const; 00289 std::string getPath( char separator = '/' ) const; 00290 00292 Iter begin() { return Iter( &mChildren ); } 00294 Iter begin( const std::string &filterPath, bool caseSensitive = false, char separator = '/' ) { return Iter( *this, filterPath, caseSensitive, separator ); } 00296 ConstIter begin() const { return ConstIter( &mChildren ); } 00298 ConstIter begin( const std::string &filterPath, bool caseSensitive = false, char separator = '/' ) const { return ConstIter( *this, filterPath, caseSensitive, separator ); } 00300 Iter end() { return Iter( &mChildren, mChildren.end() ); } 00302 ConstIter end() const { return ConstIter( &mChildren, mChildren.end() ); } 00304 void push_back( const XmlTree &newChild ); 00305 00307 std::string getDocType() const { return mDocType; } 00309 void setDocType( const std::string &docType ) { mDocType = docType; } 00310 00312 friend std::ostream& operator<<( std::ostream &out, const XmlTree &xml ); 00314 void write( DataTargetRef target, bool createDocument = true ); 00315 00317 class Exception : public cinder::Exception { 00318 }; 00319 00321 class ExcChildNotFound : public XmlTree::Exception { 00322 public: 00323 ExcChildNotFound( const XmlTree &node, const std::string &childPath ) throw(); 00324 00325 virtual const char* what() const throw() { return mMessage; } 00326 00327 private: 00328 char mMessage[2048]; 00329 }; 00330 00332 class ExcAttrNotFound : public XmlTree::Exception { 00333 public: 00334 ExcAttrNotFound( const XmlTree &node, const std::string &attrName ) throw(); 00335 00336 virtual const char* what() const throw() { return mMessage; } 00337 00338 private: 00339 char mMessage[2048]; 00340 }; 00341 00343 class ExcUnknownNodeType : public cinder::Exception { 00344 }; 00345 00347 std::shared_ptr<rapidxml::xml_document<char> > createRapidXmlDoc( bool createDocument = false ) const; 00348 00349 private: 00350 XmlTree* getNodePtr( const std::string &relativePath, bool caseSensitive, char separator ) const; 00351 void appendRapidXmlNode( rapidxml::xml_document<char> &doc, rapidxml::xml_node<char> *parent ) const; 00352 00353 static std::list<XmlTree>::const_iterator findNextChildNamed( const std::list<XmlTree> &sequence, std::list<XmlTree>::const_iterator firstCandidate, const std::string &searchTag, bool caseSensitive ); 00354 00355 NodeType mNodeType; 00356 std::string mTag; 00357 std::string mValue; 00358 std::string mDocType; // only used on NodeType::NODE_DOCUMENT 00359 XmlTree *mParent; 00360 std::list<XmlTree> mChildren; 00361 std::list<Attr> mAttributes; 00362 00363 static void loadFromDataSource( DataSourceRef dataSource, XmlTree *result, const ParseOptions &parseOptions ); 00364 }; 00365 00366 std::ostream& operator<<( std::ostream &out, const XmlTree &xml ); 00367 00368 } // namespace cinder 00369 00370 namespace std { 00371 00373 template<> 00374 struct iterator_traits<cinder::XmlTree::Iter> { 00375 typedef cinder::XmlTree value_type; 00376 typedef ptrdiff_t difference_type; 00377 typedef forward_iterator_tag iterator_category; 00378 typedef cinder::XmlTree* pointer; 00379 typedef cinder::XmlTree& reference; 00380 }; 00381 00382 template<> 00383 struct iterator_traits<cinder::XmlTree::ConstIter> { 00384 typedef cinder::XmlTree value_type; 00385 typedef ptrdiff_t difference_type; 00386 typedef forward_iterator_tag iterator_category; 00387 typedef const cinder::XmlTree* pointer; 00388 typedef const cinder::XmlTree& reference; 00389 }; 00391 00392 } // namespace std