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 <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; // only used on NodeType::NODE_DOCUMENT
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 } // namespace cinder
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 } // namespace std