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 <string>
00035 #include <vector>
00036
00038 namespace rapidxml {
00039 template<class Ch> class xml_document;
00040 template<class Ch> class xml_node;
00041 };
00043
00044 namespace cinder {
00045
00046 class XmlTree {
00047 public:
00048
00050 typedef std::list<std::unique_ptr<XmlTree> > Container;
00052
00054 class ConstIter {
00055 public:
00057 ConstIter( const Container *sequence );
00058 ConstIter( const Container *sequence, Container::const_iterator iter );
00059 ConstIter( const XmlTree &root, const std::string &filterPath, bool caseSensitive = false, char separator = '/' );
00061
00063 const XmlTree& operator*() const { return **mIterStack.back(); }
00065 const XmlTree* operator->() const { return &(**mIterStack.back()); }
00066
00068 ConstIter& operator++() {
00069 increment();
00070 return *this;
00071 }
00072
00074 const ConstIter operator++(int) {
00075 ConstIter prev( *this );
00076 ++(*this);
00077 return prev;
00078 }
00079
00080 bool operator!=( const ConstIter &rhs ) { return ( mSequenceStack.back() != rhs.mSequenceStack.back() ) || ( mIterStack.back() != rhs.mIterStack.back() ); }
00081 bool operator==( const ConstIter &rhs ) { return ( mSequenceStack.back() == rhs.mSequenceStack.back() ) && ( mIterStack.back() == rhs.mIterStack.back() ); }
00082
00083 protected:
00085 void increment();
00086 void setToEnd( const Container *seq );
00087 bool isDone() const;
00088
00089 std::vector<const Container*> mSequenceStack;
00090 std::vector<Container::const_iterator> mIterStack;
00091 std::vector<std::string> mFilter;
00092 bool mCaseSensitive;
00094 };
00095
00097 class Iter : public XmlTree::ConstIter {
00098 public:
00100 Iter( Container *sequence )
00101 : ConstIter( sequence )
00102 {}
00103
00104 Iter( Container *sequence, Container::iterator iter )
00105 : ConstIter( sequence, iter )
00106 {}
00107
00108 Iter( XmlTree &root, const std::string &filterPath, bool caseSensitive, char separator )
00109 : ConstIter( root, filterPath, caseSensitive, separator )
00110 {}
00112
00113
00115 XmlTree& operator*() const { return const_cast<XmlTree&>(**mIterStack.back()); }
00117 XmlTree* operator->() const { return const_cast<XmlTree*>( &(**mIterStack.back()) ); }
00118
00120 Iter& operator++() {
00121 increment();
00122 return *this;
00123 }
00124
00126 const Iter operator++(int) {
00127 Iter prev( *this );
00128 ++(*this);
00129 return prev;
00130 }
00131
00132 bool operator!=( const Iter &rhs ) { return ( mSequenceStack.back() != rhs.mSequenceStack.back() ) || ( mIterStack.back() != rhs.mIterStack.back() ); }
00133 bool operator==( const Iter &rhs ) { return ( mSequenceStack.back() == rhs.mSequenceStack.back() ) && ( mIterStack.back() == rhs.mIterStack.back() ); }
00134
00135 };
00136
00138 class Attr {
00139 public:
00141 Attr( XmlTree *xml, const std::string &name, const std::string &value )
00142 : mXml( xml ), mName( name ), mValue( value )
00143 {}
00144
00146 operator const std::string&() const { return mValue; }
00148 template<typename T>
00149 Attr& operator=( const T& val ) { mValue = toString( val ); mXml->setAttribute( mName, mValue ); return *this; }
00150
00151 bool operator==( const char *rhs ) const { return mValue == rhs; }
00152 bool operator==( const std::string &rhs ) const { return mValue == rhs; }
00153 bool operator!=( const char *rhs ) const { return mValue != rhs; }
00154 bool operator!=( const std::string &rhs ) const { return mValue != rhs; }
00155
00157 template<typename T>
00158 T as() const { return fromString<T>( mValue ); }
00159
00161 bool empty() const { return mValue.empty(); }
00162
00164 const std::string& getName() const { return mName; }
00166 std::string getValue() const { return mValue; }
00169 template<typename T>
00170 T getValue() const { return fromString<T>( mValue ); }
00171
00173 void setValue( const std::string &value ) { mValue = value; }
00175 template<typename T>
00176 void setValue( const T &value ) { mValue = boost::lexical_cast<std::string>( value ); }
00177
00178 private:
00179 XmlTree *mXml;
00180 std::string mName, mValue;
00181 };
00182
00184 class ParseOptions {
00185 public:
00187 ParseOptions() : mParseComments( false ), mCollapseCData( true ), mIgnoreDataChildren( true ) {}
00188
00190 ParseOptions& parseComments( bool parse = true ) { mParseComments = parse; return *this; }
00192 ParseOptions& collapseCData( bool collapse = true ) { mCollapseCData = collapse; return *this; }
00194 ParseOptions& ignoreDataChildren( bool ignore = true ) { setIgnoreDataChildren( ignore ); return *this; }
00195
00197 bool getParseComments() const { return mParseComments; }
00199 void setParseComments( bool parseComments = true ) { mParseComments = parseComments; }
00201 bool getCollapseCData() const { return mCollapseCData; }
00203 void setCollapseCData( bool collapseCData = true ) { mCollapseCData = collapseCData; }
00205 bool getIgnoreDataChildren() const { return mIgnoreDataChildren; }
00207 void setIgnoreDataChildren( bool ignore = true ) { mIgnoreDataChildren = ignore; }
00208
00209 private:
00210 bool mParseComments, mCollapseCData, mIgnoreDataChildren;
00211 };
00212
00214 typedef enum { NODE_UNKNOWN, NODE_DOCUMENT, NODE_ELEMENT, NODE_CDATA, NODE_COMMENT, NODE_DATA } NodeType;
00215
00217 XmlTree() : mParent( 0 ), mNodeType( NODE_ELEMENT ) {}
00218
00220 XmlTree( const XmlTree &rhs );
00221 XmlTree& operator=( const XmlTree &rhs );
00222
00225 explicit XmlTree( DataSourceRef dataSource, ParseOptions parseOptions = ParseOptions() ) {
00226 loadFromDataSource( dataSource, this, parseOptions );
00227 }
00228
00230 explicit XmlTree( const std::string &xmlString, ParseOptions parseOptions = ParseOptions() );
00231
00233 explicit XmlTree( const std::string &tag, const std::string &value, XmlTree *parent = 0, NodeType type = NODE_ELEMENT )
00234 : mTag( tag ), mValue( value ), mParent( parent ), mNodeType( type )
00235 {}
00236
00238 static XmlTree createDoc() { return XmlTree( "", "", 0, NODE_DOCUMENT ); }
00239
00241 NodeType getNodeType() const { return mNodeType; }
00243 void setNodeType( NodeType type ) { mNodeType = type; }
00245 bool isDocument() const { return mNodeType == NODE_DOCUMENT; }
00247 bool isElement() const { return mNodeType == NODE_ELEMENT; }
00249 bool isCData() const { return mNodeType == NODE_CDATA; }
00251 bool isComment() const { return mNodeType == NODE_COMMENT; }
00252
00254 const std::string& getTag() const { return mTag; }
00256 void setTag( const std::string &tag ) { mTag = tag; }
00257
00259 std::string getValue() const { return mValue; }
00261 template<typename T>
00262 T getValue() const { return boost::lexical_cast<T>( mValue ); }
00264 template<typename T>
00265 T getValue( const T &defaultValue ) const { try { return boost::lexical_cast<T>( mValue ); } catch( ... ) { return defaultValue; } }
00266
00268 void setValue( const std::string &value ) { mValue = value; }
00270 template<typename T>
00271 void setValue( const T &value ) { mValue = boost::lexical_cast<std::string>( value ); }
00272
00274 bool hasParent() const { return mParent != NULL; }
00276 XmlTree& getParent() { return *mParent; }
00278 const XmlTree& getParent() const { return *mParent; }
00279
00281 Iter find( const std::string &relativePath, bool caseSensitive = false, char separator = '/' ) { return Iter( *this, relativePath, caseSensitive, separator ); }
00283 ConstIter find( const std::string &relativePath, bool caseSensitive = false, char separator = '/' ) const { return ConstIter( *this, relativePath, caseSensitive, separator ); }
00285 bool hasChild( const std::string &relativePath, bool caseSensitive = false, char separator = '/' ) const;
00286
00288 XmlTree& getChild( const std::string &relativePath, bool caseSensitive = false, char separator = '/' );
00290 const XmlTree& getChild( const std::string &relativePath, bool caseSensitive = false, char separator = '/' ) const;
00292 Container& getChildren() { return mChildren; }
00294 const Container& getChildren() const { return mChildren; }
00295
00297 std::list<Attr>& getAttributes() { return mAttributes; }
00299 const std::list<Attr>& getAttributes() const { return mAttributes; }
00300
00302 const Attr& getAttribute( const std::string &attrName ) const;
00303
00305 const Attr operator[]( const std::string &attrName ) const { if( hasAttribute( attrName ) ) return getAttribute(attrName); else return Attr( const_cast<XmlTree*>( this ), attrName, "" ); }
00307 Attr operator[]( const std::string &attrName ) { if( hasAttribute( attrName ) ) return getAttribute(attrName); else return Attr( this, attrName, "" ); }
00308
00310 const XmlTree& operator/( const std::string &childName ) const { return getChild( childName ); }
00312 XmlTree& operator/( const std::string &childName ) { return getChild( childName ); }
00313
00316 template<typename T>
00317 T getAttributeValue( const std::string &attrName ) const { return getAttribute( attrName ).getValue<T>(); }
00320 template<typename T>
00321 T getAttributeValue( const std::string &attrName, const T &defaultValue ) const {
00322 if( hasAttribute( attrName ) ) {
00323 try {
00324 return getAttribute( attrName ).getValue<T>();
00325 }
00326 catch(...) {
00327 return defaultValue;
00328 }
00329 }
00330 else return defaultValue;
00331 }
00332
00334 XmlTree& setAttribute( const std::string &attrName, const std::string &value );
00336 template<typename T>
00337 XmlTree& setAttribute( const std::string &attrName, const T &value ) { return setAttribute( attrName, boost::lexical_cast<std::string>( value ) ); }
00339 bool hasAttribute( const std::string &attrName ) const;
00341 std::string getPath( char separator = '/' ) const;
00342
00344 Iter begin() { return Iter( &mChildren ); }
00346 Iter begin( const std::string &filterPath, bool caseSensitive = false, char separator = '/' ) { return Iter( *this, filterPath, caseSensitive, separator ); }
00348 ConstIter begin() const { return ConstIter( &mChildren ); }
00350 ConstIter begin( const std::string &filterPath, bool caseSensitive = false, char separator = '/' ) const { return ConstIter( *this, filterPath, caseSensitive, separator ); }
00352 Iter end() { return Iter( &mChildren, mChildren.end() ); }
00354 ConstIter end() const { return ConstIter( &mChildren, mChildren.end() ); }
00356 void push_back( const XmlTree &newChild );
00357
00359 std::string getDocType() const { return mDocType; }
00361 void setDocType( const std::string &docType ) { mDocType = docType; }
00362
00364 friend std::ostream& operator<<( std::ostream &out, const XmlTree &xml );
00366 void write( DataTargetRef target, bool createDocument = true );
00367
00369 class Exception : public cinder::Exception {
00370 };
00371
00373 class ExcChildNotFound : public XmlTree::Exception {
00374 public:
00375 ExcChildNotFound( const XmlTree &node, const std::string &childPath ) throw();
00376
00377 virtual const char* what() const throw() { return mMessage; }
00378
00379 private:
00380 char mMessage[2048];
00381 };
00382
00384 class ExcAttrNotFound : public XmlTree::Exception {
00385 public:
00386 ExcAttrNotFound( const XmlTree &node, const std::string &attrName ) throw();
00387
00388 virtual const char* what() const throw() { return mMessage; }
00389
00390 private:
00391 char mMessage[2048];
00392 };
00393
00395 class ExcUnknownNodeType : public cinder::Exception {
00396 };
00397
00399 std::shared_ptr<rapidxml::xml_document<char> > createRapidXmlDoc( bool createDocument = false ) const;
00400
00401 private:
00402 XmlTree* getNodePtr( const std::string &relativePath, bool caseSensitive, char separator ) const;
00403 void appendRapidXmlNode( rapidxml::xml_document<char> &doc, rapidxml::xml_node<char> *parent ) const;
00404
00405 static Container::const_iterator findNextChildNamed( const Container &sequence, Container::const_iterator firstCandidate, const std::string &searchTag, bool caseSensitive );
00406
00407 NodeType mNodeType;
00408 std::string mTag;
00409 std::string mValue;
00410 std::string mDocType;
00411 XmlTree *mParent;
00412 Container mChildren;
00413 std::list<Attr> mAttributes;
00414
00415 static void loadFromDataSource( DataSourceRef dataSource, XmlTree *result, const ParseOptions &parseOptions );
00416 };
00417
00418 std::ostream& operator<<( std::ostream &out, const XmlTree &xml );
00419
00420 }
00421
00422 namespace std {
00423
00425 template<>
00426 struct iterator_traits<cinder::XmlTree::Iter> {
00427 typedef cinder::XmlTree value_type;
00428 typedef ptrdiff_t difference_type;
00429 typedef forward_iterator_tag iterator_category;
00430 typedef cinder::XmlTree* pointer;
00431 typedef cinder::XmlTree& reference;
00432 };
00433
00434 template<>
00435 struct iterator_traits<cinder::XmlTree::ConstIter> {
00436 typedef cinder::XmlTree value_type;
00437 typedef ptrdiff_t difference_type;
00438 typedef forward_iterator_tag iterator_category;
00439 typedef const cinder::XmlTree* pointer;
00440 typedef const cinder::XmlTree& reference;
00441 };
00443
00444 }