Cinder

  • Main Page
  • Related Pages
  • Namespaces
  • Classes
  • Files
  • Examples
  • File List
  • File Members

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 
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