00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
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;
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 }
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 }