Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

plistBase.h

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #ifndef INCLUDED_plistBase_h_
00003 #define INCLUDED_plistBase_h_
00004 
00005 #include "XMLLoadSave.h"
00006 #include "Cloneable.h"
00007 #include <exception>
00008 #include <string>
00009 #include <iostream>
00010 #include <iomanip>
00011 #include <sstream>
00012 #include <set>
00013 #include <cctype>
00014 
00015 /*
00016  From: <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
00017  
00018  <!ENTITY % plistObject "(array | data | date | dict | real | integer | string | true | false )" >
00019  <!ELEMENT plist %plistObject;>
00020  <!ATTLIST plist version CDATA "1.0" >
00021  
00022  <!-- Collections -->
00023  <!ELEMENT array (%plistObject;)*>
00024  <!ELEMENT dict (key, %plistObject;)*>
00025  <!ELEMENT key (#PCDATA)>
00026  
00027  <!--- Primitive types -->
00028  <!ELEMENT string (#PCDATA)>
00029  <!ELEMENT data (#PCDATA)> <!-- Contents interpreted as Base-64 encoded -->
00030  <!ELEMENT date (#PCDATA)> <!-- Contents should conform to a subset of ISO 8601 (in particular, YYYY '-' MM '-' DD 'T' HH ':' MM ':' SS 'Z'.  Smaller units may be omitted with a loss of precision) -->
00031  
00032  <!-- Numerical primitives -->
00033  <!ELEMENT true EMPTY>  <!-- Boolean constant true -->
00034  <!ELEMENT false EMPTY> <!-- Boolean constant false -->
00035  <!ELEMENT real (#PCDATA)> <!-- Contents should represent a floating point number matching ("+" | "-")? d+ ("."d*)? ("E" ("+" | "-") d+)? where d is a digit 0-9.  -->
00036  <!ELEMENT integer (#PCDATA)> <!-- Contents should represent a (possibly signed) integer number in base 10 -->
00037  */
00038 
00039 extern "C" {
00040   struct _xmlNode;
00041   struct _xmlDoc;
00042   struct _xmlAttr;
00043   struct _xmlNs;
00044   typedef _xmlNode xmlNode; //!< forward declaration of xmlNode to avoid including actual header here
00045   typedef _xmlDoc xmlDoc; //!< forward declaration of xmlDoc to avoid including actual header here
00046   typedef _xmlAttr xmlAttr; //!< forward declaration of xmlAttr to avoid including actual header here
00047   typedef _xmlNs xmlNs; //!< forward declaration of xmlNs to avoid including actual header here
00048   typedef unsigned char xmlChar; //!< forward declaration of xmlChar to avoid including actual header here
00049 } 
00050 
00051 //! A collection of classes to implement the Propery List data storage format, a XML standard used by Apple and others
00052 namespace plist {
00053   
00054 #ifdef PLIST_CLONE_ABS
00055 #  error PLIST_CLONE_ABS already defined!
00056 #else
00057 #  if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && (__GNUC_MINOR__ > 3))
00058 //! defines abstract clone() (=0) using polymorphic return type @a TYPE if supported by current version of gcc, Cloneable otherwise
00059 #    define PLIST_CLONE_ABS(TYPE)   virtual TYPE* clone() const __attribute__ ((warn_unused_result)) =0;
00060 #  else
00061 //! defines abstract clone() (=0) using polymorphic return type @a TYPE if supported by current version of gcc, Cloneable otherwise
00062 #    define PLIST_CLONE_ABS(TYPE)   virtual Cloneable* clone() const =0;
00063 #  endif
00064 #endif
00065   
00066 #ifdef PLIST_CLONE_DEF
00067 #  error PLIST_CLONE_DEF already defined!
00068 #else
00069 #  if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && (__GNUC_MINOR__ > 3))
00070 //! declares clone() using polymorphic return type @a TYPE if supported by current version of gcc, Cloneable otherwise; returns @a RETVAL
00071 #    define PLIST_CLONE_DEF(TYPE,RETVAL)    virtual TYPE* clone() const __attribute__ ((warn_unused_result));
00072 //! implements clone() using polymorphic return type @a TYPE if supported by current version of gcc, Cloneable otherwise; returns @a RETVAL
00073 #    define PLIST_CLONE_IMP(TYPE,RETVAL)    TYPE* TYPE::clone() const { return RETVAL; }
00074 //! implements clone() using templated polymorphic return type @a TYPE if supported by current version of gcc, Cloneable otherwise; returns @a RETVAL
00075 #    define PLIST_CLONE_IMPT(TEMPL,TYPE,RETVAL)   template<typename TEMPL> TYPE<TEMPL>* TYPE<TEMPL>::clone() const { return RETVAL; }
00076 //! implements clone() using templated polymorphic return type @a TYPE if supported by current version of gcc, Cloneable otherwise; returns @a RETVAL
00077 #    define PLIST_CLONE_IMPT2(TEMPL1,TEMPL2,TYPE,RETVAL)    template<typename TEMPL1,typename TEMPL2> TYPE<TEMPL1,TEMPL2>* TYPE<TEMPL1,TEMPL2>::clone() const { return RETVAL; }
00078 #  else
00079 //! declares clone() using polymorphic return type @a TYPE if supported by current version of gcc, Cloneable otherwise; returns @a RETVAL
00080 #    define PLIST_CLONE_DEF(TYPE,RETVAL)    virtual Cloneable* clone() const { return RETVAL; }
00081 //! implements clone() using polymorphic return type @a TYPE if supported by current version of gcc, Cloneable otherwise; returns @a RETVAL
00082 #    define PLIST_CLONE_IMP(TYPE,RETVAL)
00083 //! implements clone() using templated polymorphic return type @a TYPE if supported by current version of gcc, Cloneable otherwise; returns @a RETVAL
00084 #    define PLIST_CLONE_IMPT(TEMPL,TYPE,RETVAL)
00085 //! implements clone() using templated polymorphic return type @a TYPE if supported by current version of gcc, Cloneable otherwise; returns @a RETVAL
00086 #    define PLIST_CLONE_IMPT2(TEMPL1,TEMPL2,TYPE,RETVAL)
00087 #  endif
00088 #endif
00089   
00090   //! Base class for the plist listener callbacks
00091   class Listener {
00092   public:
00093     //! destructor
00094     virtual ~Listener() {}
00095   };
00096   
00097   class PrimitiveBase;
00098   //! If you wish to be notified any time a particular plist primitive's value has been changed, inherit from this and implement the callback, then register it with the plist object through Primitive::addPrimitiveListener()
00099   class PrimitiveListener : public Listener {
00100   public:
00101     //! This will be called whenever a plist you have registered with is changed
00102     /*! @a pl is const to help you avoid infinite recursion from an
00103      *  accidental modification of @a pl's value -- use a const cast
00104      *  if you're sure you know what you're doing */
00105     virtual void plistValueChanged(const PrimitiveBase& pl)=0;
00106     
00107     //! This will be called whenever a plist you have registered with is reassigned is current value (usually something you'll want to ignore...)
00108     /*! Argument is const to help you avoid infinite recursion from an
00109      *  accidental modification of its value -- use a const cast
00110      *  if you're sure you know what you're doing */
00111     virtual void plistValueTouched(const PrimitiveBase& /*pl*/) {}
00112   };
00113   
00114   class ObjectBase;
00115   class Collection;
00116   //! If you wish to be notified any time an entry is added, removed, or replaced from a Dictionary, Array, or Vector, inherit from this and implement one or both of the functions, then register it with the collection's addCollectionListener()
00117   class CollectionListener : public Listener {
00118   public:
00119     //! This will be called whenever an entry is added to the collection
00120     virtual void plistCollectionEntryAdded(Collection& /*col*/, ObjectBase& /*primitive*/) {}
00121     //! This will be called whenever an entry is added to the collection
00122     virtual void plistCollectionEntryRemoved(Collection& /*col*/, ObjectBase& /*primitive*/) {}
00123     //! This will be called whenever an entry is replaced, or multiple entries are added/removed at once, such as when an assignment occurs
00124     virtual void plistCollectionEntriesChanged(Collection& /*col*/) {}
00125   };
00126   
00127   //! This base class provides the root functionality for all plist entities -- Dictionary and the various templated subclasses of PrimitiveBase
00128   /*! The subclasses may throw std::bad_format if the document is poorly structured or bad values are found. */
00129   class ObjectBase : public XMLLoadSave, public Cloneable {
00130     friend ObjectBase* loadXML(xmlNode* node);
00131   public:
00132     //! specifies that collections (e.g. plist::Array or plist::Dictionary) of these abstract base classes (ObjectBase, PrimitiveBase) can convert any primitive type to a plist::Primitive wrapper
00133     template<typename U, typename V> struct conversion_policy { typedef typename U::template WrapValueConversion<V> value_conversion; };
00134     
00135     ObjectBase(); //!< constructor
00136     virtual ~ObjectBase()=0; //!< destructor
00137     
00138     //! polymorphic assignment (throws std::bad_cast if the assignment is between invalid types, i.e. a primitive and a collection, or different collection types)
00139     virtual void set(const ObjectBase&)=0;
00140     
00141     //! casting operator: return current value as specified type (throws std::runtime_error exception if bad cast, e.g. dictionary or array to value type)
00142     /*! The implementation for this function is defined by a series of specializations.
00143      *  This allows you to add casts for additional user-defined types, as well as get
00144      *  compile time error if you attempt to cast to an unsupported type.
00145      *  (I really wish we had virtual templated functions...) */
00146     template<typename T> T to() const;
00147     
00148     //! return current value as a string
00149     virtual std::string toString() const=0;
00150     //! return current value as an (long) integer (throws std::runtime_error exception if incompatable, e.g. dictionary or array to value type)
00151     virtual long toLong() const=0;
00152     //! return current value as a double (throws std::runtime_error exception if incompatable, e.g. dictionary or array to value type)
00153     virtual double toDouble() const=0;
00154     
00155     //! subclasses are expected to provide a working implementation
00156     virtual void loadXML(xmlNode* node)=0;
00157     //! subclasses are expected to provide a working implementation
00158     virtual void saveXML(xmlNode* node) const=0;
00159     
00160     //! allows a copy to be made of an event, supporting polymorphism
00161     PLIST_CLONE_ABS(ObjectBase);
00162       
00163   protected:
00164     //! polymorphic assignment operator, see assign()
00165     /*! This is protected for two reasons: one, so you don't accidentally use it via simple '=' statement,
00166      *  and two, to avoid 'operator= was hidden' warnings in every base class (because they keep
00167      *  reintroducing their default operator=(), hiding/shadowing this one (if it were virtual, as it would
00168      *  need to be to take on the role filled by assign(). */
00169     ObjectBase& operator=(const ObjectBase&) { return *this; }
00170     
00171     //!@name Inherited from XMLLoadSave
00172     virtual void setParseTree(xmlDoc * doc) const;
00173     virtual xmlNode* FindRootXMLElement(xmlDoc* doc) const;
00174     //@}
00175     
00176     //!Provides accessor functions to struct fields without having to include libxml.h everywhere
00177     //!@name libxml Forwards
00178     static bool xNodeHasName(xmlNode* node, const char* name); //!< returns true if the name of @a node matches @a name
00179     static const xmlChar* xNodeGetName(xmlNode* node); //!< returns name of @a node (not a libxml function)
00180     static xmlNode* xNodeGetChildren(xmlNode* node); //!< returns children of @a node (not a libxml function)
00181     static xmlNode* xNodeGetLastChild(xmlNode* node); //!< returns last child of @a node (not a libxml function)
00182     static xmlNode* xNodeGetNextNode(xmlNode* node); //!< returns next node (sibling) after @a node (not a libxml function)
00183     static xmlNode* xNodeGetPrevNode(xmlNode* node); //!< returns previous node (sibling) before @a node (not a libxml function)
00184     static xmlNode* xNodeGetParent(xmlNode* node); //!< returns parent node of @a node (not a libxml function)
00185     static xmlDoc* xNodeGetDoc(xmlNode* node); //!< returns document node of @a node (not a libxml function)
00186     static bool xNodeIsText(xmlNode* node); //!< returns true if node is an XML_TEXT_NODE (not a libxml function)
00187     static bool xNodeIsElement(xmlNode* node); //!< returns true if node is an XML_ELEMENT_NODE (not a libxml function)
00188     static bool xNodeIsComment(xmlNode* node); //!< returns true if node is an XML_COMMENT_NODE (not a libxml function)
00189      //@}
00190     
00191     //! returns true if @a str is some form of affirmative (e.g. "true" or "yes")
00192     static bool matchTrue(const std::string& str) { return str=="true" || str=="yes"; }
00193     //! returns true if @a str is some form of negative (e.g. "false" or "no")
00194     static bool matchFalse(const std::string& str) { return str=="false" || str=="no"; }
00195   };
00196   //! output of an ObjectBase to a stream
00197   inline std::ostream& operator<<(std::ostream& os, const ObjectBase& pb) {
00198     return os << pb.toString();
00199   }
00200   
00201   // specializations to funnel cast requests through the appropriate conversion
00202   /// @cond INTERNAL
00203   template<> inline bool ObjectBase::to<bool>() const { return toLong(); }
00204   template<> inline char ObjectBase::to<char>() const { return toLong(); }
00205   template<> inline unsigned char ObjectBase::to<unsigned char>() const { return toLong(); }
00206   template<> inline short ObjectBase::to<short>() const { return toLong(); }
00207   template<> inline unsigned short ObjectBase::to<unsigned short>() const { return toLong(); }
00208   template<> inline int ObjectBase::to<int>() const { return toLong(); }
00209   template<> inline unsigned int ObjectBase::to<unsigned int>() const { return toLong(); }
00210   template<> inline long ObjectBase::to<long>() const { return toLong(); }
00211   template<> inline unsigned long ObjectBase::to<unsigned long>() const { return toLong(); }
00212   template<> inline long long ObjectBase::to<long long>() const { return toLong(); }
00213   template<> inline unsigned long long ObjectBase::to<unsigned long long>() const { return toLong(); }
00214   template<> inline float ObjectBase::to<float>() const { return toDouble(); }
00215   template<> inline double ObjectBase::to<double>() const { return toDouble(); }
00216   template<> inline const char* ObjectBase::to<const char*>() const { return toString().c_str(); }
00217   template<> inline std::string ObjectBase::to<std::string>() const { return toString(); }
00218   /// @endcond
00219   
00220   template<typename T> class Primitive; // forward declaration so we can solve string/Primitive<string> ambiguity in operator= below
00221   
00222   //! Provides common functionality to all primitive value classes (implemented in a templated subclass Primitive)
00223   /*! This class supports callbacks upon modification through the use of the
00224    *  PrimitiveListener interface.  Note that we only store a pointer to the
00225    *  listener list, which is typically unallocated when no listeners are
00226    *  active.  This should ensure minimal memory usage per object, as well as
00227    *  support safe storage of plist objects in inter-process shared memory
00228    *  regions.
00229    *
00230    *  If you are using these in a shared memory region, just be sure that only
00231    *  the process with listeners does any and all modifications, and that it
00232    *  unsubscribes before detaching from the region (or else destroys the region
00233    *  itself) */
00234   class PrimitiveBase : public ObjectBase {
00235   public:
00236     //! constructor
00237     PrimitiveBase() : ObjectBase(), primitiveListeners() {}
00238     //! copy constructor (don't copy listeners)
00239     PrimitiveBase(const PrimitiveBase& pb) : ObjectBase(pb), primitiveListeners() {}
00240     //! assignment (don't assign listeners); doesn't trigger fireValueChanged, subclass should do that from its own operator=() following assignment
00241     virtual PrimitiveBase& operator=(const PrimitiveBase& pb) { ObjectBase::operator=(pb); return *this; }
00242     //! assignment from Primitive<string>, solely to resolve ambiguity with this type between operator=(PrimitiveBase) and operator=(std::string)
00243     PrimitiveBase& operator=(const Primitive<std::string>& v);
00244     //! assignment from std::string, wraps it in a plist::Primitive and passes on to operator=(PrimitiveBase)
00245     PrimitiveBase& operator=(const std::string& v);
00246     //! assignment from long value, wraps it in a plist::Primitive and passes on to operator=(PrimitiveBase)
00247     PrimitiveBase& operator=(long v);
00248     //! assignment from unsigned long value, wraps it in a plist::Primitive and passes on to operator=(PrimitiveBase)
00249     PrimitiveBase& operator=(unsigned long v);
00250     //! assignment from double value, wraps it in a plist::Primitive and passes on to operator=(PrimitiveBase)
00251     PrimitiveBase& operator=(double v);
00252     //! destructor
00253     ~PrimitiveBase();
00254     
00255     //! assign a new value
00256     virtual void set(const std::string& str)=0;
00257     virtual void set(const ObjectBase& ob) { const PrimitiveBase& pb = dynamic_cast<const PrimitiveBase&>(ob); *this=pb; }
00258     //! return current value as a string
00259     virtual std::string get() const=0;
00260     
00261     virtual std::string toString() const { return get(); }
00262     
00263     //! get notified of changes; be sure to call removeValueListener before destructing @a vl!
00264     virtual void addPrimitiveListener(PrimitiveListener* vl);
00265     //! no longer take notification of changes to this object's value
00266     virtual void removePrimitiveListener(PrimitiveListener* vl);
00267     //! test if @a l is currently registered as a listener
00268     virtual bool isPrimitiveListener(PrimitiveListener * vl);
00269 
00270   protected:
00271     //! run through #primitiveListeners, calling PrimitiveListener::plistValueChanged(*this) or PrimitiveListener::plistValueTouched(*this)
00272     virtual void fireValueChanged(bool touch) const;
00273     //! stores a list of the current listeners
00274     std::set<PrimitiveListener*>* primitiveListeners;
00275   };
00276   //! output stringified value (from PrimitiveBase::get()) to stream
00277   inline std::ostream& operator<<(std::ostream& os, const PrimitiveBase& pb) {
00278     return os << pb.get();
00279   }
00280   //! input value from next word in @a is, via PrimitiveBase::set()
00281   inline std::istream& operator>>(std::istream& is, PrimitiveBase& pb) {
00282     std::string s;
00283     is >> s;
00284     pb.set(s);
00285     return is;
00286   }
00287 
00288 } //namespace plist
00289 
00290 
00291 /*! @file
00292  * @brief 
00293  * @author Ethan Tira-Thompson (ejt) (Creator)
00294  *
00295  * $Author: ejt $
00296  * $Name: tekkotsu-4_0 $
00297  * $Revision: 1.18 $
00298  * $State: Exp $
00299  * $Date: 2007/11/15 02:44:06 $
00300  */
00301 
00302 #endif

Tekkotsu v4.0
Generated Thu Nov 22 00:54:54 2007 by Doxygen 1.5.4