include/cinder/Xml.h
Go to the documentation of this file.
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