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 //! forward-declares clone() using polymorphic return type @a TYPE if supported by current version of gcc, Cloneable otherwise
00071 #    define PLIST_CLONE_FWD(TYPE)   virtual TYPE* clone() const __attribute__ ((warn_unused_result))=0;
00072 //! declares clone() using polymorphic return type @a TYPE if supported by current version of gcc, Cloneable otherwise; returns @a RETVAL
00073 #    define PLIST_CLONE_DEF(TYPE,RETVAL)    virtual TYPE* clone() const __attribute__ ((warn_unused_result));
00074 //! implements clone() using polymorphic return type @a TYPE if supported by current version of gcc, Cloneable otherwise; returns @a RETVAL
00075 #    define PLIST_CLONE_IMP(TYPE,RETVAL)    TYPE* TYPE::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_IMPT(TEMPL,TYPE,RETVAL)   template<typename TEMPL> TYPE<TEMPL>* TYPE<TEMPL>::clone() const { return RETVAL; }
00078 //! implements clone() using templated polymorphic return type @a TYPE if supported by current version of gcc, Cloneable otherwise; returns @a RETVAL
00079 #    define PLIST_CLONE_IMPT2(TEMPL1,TEMPL2,TYPE,RETVAL)    template<typename TEMPL1,typename TEMPL2> TYPE<TEMPL1,TEMPL2>* TYPE<TEMPL1,TEMPL2>::clone() const { return RETVAL; }
00080 #  else
00081 //! forward-declares clone() using polymorphic return type @a TYPE if supported by current version of gcc, Cloneable otherwise
00082 #    define PLIST_CLONE_FWD(TYPE)   virtual Cloneable* clone() const =0;
00083 //! declares clone() using polymorphic return type @a TYPE if supported by current version of gcc, Cloneable otherwise; returns @a RETVAL
00084 #    define PLIST_CLONE_DEF(TYPE,RETVAL)    virtual Cloneable* clone() const { return RETVAL; }
00085 //! implements clone() using polymorphic return type @a TYPE if supported by current version of gcc, Cloneable otherwise; returns @a RETVAL
00086 #    define PLIST_CLONE_IMP(TYPE,RETVAL)
00087 //! implements clone() using templated polymorphic return type @a TYPE if supported by current version of gcc, Cloneable otherwise; returns @a RETVAL
00088 #    define PLIST_CLONE_IMPT(TEMPL,TYPE,RETVAL)
00089 //! implements clone() using templated polymorphic return type @a TYPE if supported by current version of gcc, Cloneable otherwise; returns @a RETVAL
00090 #    define PLIST_CLONE_IMPT2(TEMPL1,TEMPL2,TYPE,RETVAL)
00091 #  endif
00092 #endif
00093   
00094   //! Base class for the plist listener callbacks
00095   class Listener {
00096   public:
00097     //! destructor
00098     virtual ~Listener() {}
00099   };
00100   
00101   class PrimitiveBase;
00102   //! 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()
00103   class PrimitiveListener : public Listener {
00104   public:
00105     //! This will be called whenever a plist you have registered with is changed
00106     /*! @a pl is const to help you avoid infinite recursion from an
00107      *  accidental modification of @a pl's value -- use a const cast
00108      *  if you're sure you know what you're doing */
00109     virtual void plistValueChanged(const PrimitiveBase& pl)=0;
00110     
00111     //! This will be called whenever a plist you have registered with is reassigned is current value (usually something you'll want to ignore...)
00112     /*! Argument is const to help you avoid infinite recursion from an
00113      *  accidental modification of its value -- use a const cast
00114      *  if you're sure you know what you're doing */
00115     virtual void plistValueTouched(const PrimitiveBase& /*pl*/) {}
00116   };
00117   
00118   class ObjectBase;
00119   class Collection;
00120   //! 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()
00121   class CollectionListener : public Listener {
00122   public:
00123     //! This will be called whenever an entry is added to the collection
00124     virtual void plistCollectionEntryAdded(Collection& /*col*/, ObjectBase& /*primitive*/) {}
00125     //! This will be called whenever an entry is added to the collection
00126     virtual void plistCollectionEntryRemoved(Collection& /*col*/, ObjectBase& /*primitive*/) {}
00127     //! This will be called whenever an entry is replaced, or multiple entries are added/removed at once, such as when an assignment occurs
00128     virtual void plistCollectionEntriesChanged(Collection& /*col*/) {}
00129   };
00130   
00131   //! This base class provides the root functionality for all plist entities -- Dictionary and the various templated subclasses of PrimitiveBase
00132   /*! The subclasses may throw XMLLoadSave::bad_format if the document is poorly structured or bad values are found. */
00133   class ObjectBase : public virtual XMLLoadSave, public virtual Cloneable {
00134     friend ObjectBase* loadXML(xmlNode* node);
00135   public:
00136     //! 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
00137     template<typename U, typename V> struct conversion_policy { typedef typename U::template WrapValueConversion<V> value_conversion; };
00138     
00139     ObjectBase(); //!< constructor
00140     virtual ~ObjectBase()=0; //!< destructor
00141     
00142     //! polymorphic assignment (throws std::bad_cast if the assignment is between invalid types, i.e. a primitive and a collection, or different collection types)
00143     virtual void set(const ObjectBase&)=0;
00144     
00145     //! casting operator: return current value as specified type (throws std::runtime_error exception if bad cast, e.g. dictionary or array to value type)
00146     /*! The implementation for this function is defined by a series of specializations.
00147      *  This allows you to add casts for additional user-defined types, as well as get
00148      *  compile time error if you attempt to cast to an unsupported type.
00149      *  (I really wish we had virtual templated functions...) */
00150     template<typename T> T castTo() const;
00151     
00152     //! return current value as a string
00153     virtual std::string toString() const=0;
00154     //! return current value as a boolean (throws std::runtime_error exception if incompatable, e.g. dictionary or array to value type)
00155     /*! Does something a little smarter than assuming numeric conversion if called on a string... */
00156     virtual bool toBool() const { return toLong(); }
00157     //! return current value as a character (throws std::runtime_error exception if incompatable, e.g. dictionary or array to value type)
00158     /*! Does something a little smarter than assuming numeric conversion if called on a string... */
00159     virtual char toChar() const { return toLong(); }
00160     //! return current value as an (long) integer (throws std::runtime_error exception if incompatable, e.g. dictionary or array to value type)
00161     virtual long toLong() const=0;
00162     //! return current value as a double (throws std::runtime_error exception if incompatable, e.g. dictionary or array to value type)
00163     virtual double toDouble() const=0;
00164     
00165     //! subclasses are expected to provide a working implementation
00166     virtual void loadXML(xmlNode* node)=0;
00167     //! subclasses are expected to provide a working implementation
00168     virtual void saveXML(xmlNode* node) const=0;
00169     
00170     //! allows a copy to be made of an event, supporting polymorphism
00171     PLIST_CLONE_ABS(ObjectBase);
00172       
00173     virtual void setParseTree(xmlDoc * doc) const;
00174     
00175   protected:
00176     //! polymorphic assignment operator, see assign()
00177     /*! This is protected for two reasons: one, so you don't accidentally use it via simple '=' statement,
00178      *  and two, to avoid 'operator= was hidden' warnings in every base class (because they keep
00179      *  reintroducing their default operator=(), hiding/shadowing this one (if it were virtual, as it would
00180      *  need to be to take on the role filled by assign(). */
00181     ObjectBase& operator=(const ObjectBase&) { return *this; }
00182     
00183     virtual xmlNode* FindRootXMLElement(xmlDoc* doc) const;
00184     
00185     //!Provides accessor functions to struct fields without having to include libxml.h everywhere
00186     //!@name libxml Forwards
00187     static bool xNodeHasName(xmlNode* node, const char* name); //!< returns true if the name of @a node matches @a name
00188     static const xmlChar* xNodeGetName(xmlNode* node); //!< returns name of @a node (not a libxml function)
00189     static xmlChar* xGetNodePath(xmlNode* node); //!< returns path from document root to @a node (forwards to xmlGetNodePath, returns new allocation, so call xmlFree)
00190     static const xmlChar* xNodeGetURL(xmlNode* node); //!< returns URL/file of @a node (not a libxml function)
00191     static xmlNode* xNodeGetChildren(xmlNode* node); //!< returns children of @a node (not a libxml function)
00192     static xmlNode* xNodeGetLastChild(xmlNode* node); //!< returns last child of @a node (not a libxml function)
00193     static xmlNode* xNodeGetNextNode(xmlNode* node); //!< returns next node (sibling) after @a node (not a libxml function)
00194     static xmlNode* xNodeGetPrevNode(xmlNode* node); //!< returns previous node (sibling) before @a node (not a libxml function)
00195     static xmlNode* xNodeGetParent(xmlNode* node); //!< returns parent node of @a node (not a libxml function)
00196     static xmlDoc* xNodeGetDoc(xmlNode* node); //!< returns document node of @a node (not a libxml function)
00197     static bool xNodeIsText(xmlNode* node); //!< returns true if node is an XML_TEXT_NODE (not a libxml function)
00198     static bool xNodeIsElement(xmlNode* node); //!< returns true if node is an XML_ELEMENT_NODE (not a libxml function)
00199     static bool xNodeIsComment(xmlNode* node); //!< returns true if node is an XML_COMMENT_NODE (not a libxml function)
00200      //@}
00201     
00202     //! returns true if @a str is some form of affirmative (e.g. "true" or "yes")
00203     static bool matchTrue(const std::string& str) { return str=="true" || str=="yes"; }
00204     //! returns true if @a str is some form of negative (e.g. "false" or "no")
00205     static bool matchFalse(const std::string& str) { return str=="false" || str=="no"; }
00206   };
00207   //! output of an ObjectBase to a stream
00208   inline std::ostream& operator<<(std::ostream& os, const ObjectBase& pb) {
00209     return os << pb.toString();
00210   }
00211   
00212   // specializations to funnel cast requests through the appropriate conversion
00213   /// @cond INTERNAL
00214   template<> inline bool ObjectBase::castTo<bool>() const { return toBool(); }
00215   template<> inline char ObjectBase::castTo<char>() const { return toLong(); }
00216   template<> inline unsigned char ObjectBase::castTo<unsigned char>() const { return toLong(); }
00217   template<> inline short ObjectBase::castTo<short>() const { return toLong(); }
00218   template<> inline unsigned short ObjectBase::castTo<unsigned short>() const { return toLong(); }
00219   template<> inline int ObjectBase::castTo<int>() const { return toLong(); }
00220   template<> inline unsigned int ObjectBase::castTo<unsigned int>() const { return toLong(); }
00221   template<> inline long ObjectBase::castTo<long>() const { return toLong(); }
00222   template<> inline unsigned long ObjectBase::castTo<unsigned long>() const { return toLong(); }
00223   template<> inline long long ObjectBase::castTo<long long>() const { return toLong(); }
00224   template<> inline unsigned long long ObjectBase::castTo<unsigned long long>() const { return toLong(); }
00225   template<> inline float ObjectBase::castTo<float>() const { return static_cast<float>(toDouble()); }
00226   template<> inline double ObjectBase::castTo<double>() const { return toDouble(); }
00227   template<> inline const char* ObjectBase::castTo<const char*>() const { return toString().c_str(); }
00228   template<> inline std::string ObjectBase::castTo<std::string>() const { return toString(); }
00229   /// @endcond
00230   
00231   template<typename T> class Primitive; // forward declaration so we can solve string/Primitive<string> ambiguity in operator= below
00232   
00233   //! Provides common functionality to all primitive value classes (implemented in a templated subclass Primitive)
00234   /*! This class supports callbacks upon modification through the use of the
00235    *  PrimitiveListener interface.  Note that we only store a pointer to the
00236    *  listener list, which is typically unallocated when no listeners are
00237    *  active.  This should ensure minimal memory usage per object, as well as
00238    *  support safe storage of plist objects in inter-process shared memory
00239    *  regions.
00240    *
00241    *  If you are using these in a shared memory region, just be sure that only
00242    *  the process with listeners does any and all modifications, and that it
00243    *  unsubscribes before detaching from the region (or else destroys the region
00244    *  itself) */
00245   class PrimitiveBase : public ObjectBase {
00246   public:
00247     //! constructor
00248     PrimitiveBase() : ObjectBase(), primitiveListeners() {}
00249     //! copy constructor (don't copy listeners)
00250     PrimitiveBase(const PrimitiveBase& pb) : ObjectBase(pb), primitiveListeners() {}
00251     //! assignment (don't assign listeners); doesn't trigger fireValueChanged, subclass should do that from its own operator=() following assignment
00252     virtual PrimitiveBase& operator=(const PrimitiveBase& pb) { ObjectBase::operator=(pb); return *this; }
00253     //! assignment from Primitive<string>, solely to resolve ambiguity with this type between operator=(PrimitiveBase) and operator=(std::string)
00254     PrimitiveBase& operator=(const Primitive<std::string>& v);
00255     //! assignment from std::string, wraps it in a plist::Primitive and passes on to operator=(PrimitiveBase)
00256     PrimitiveBase& operator=(const std::string& v);
00257     //! assignment from long value, wraps it in a plist::Primitive and passes on to operator=(PrimitiveBase)
00258     PrimitiveBase& operator=(long v);
00259     //! assignment from unsigned long value, wraps it in a plist::Primitive and passes on to operator=(PrimitiveBase)
00260     PrimitiveBase& operator=(unsigned long v);
00261     //! assignment from double value, wraps it in a plist::Primitive and passes on to operator=(PrimitiveBase)
00262     PrimitiveBase& operator=(double v);
00263     //! destructor
00264     ~PrimitiveBase();
00265     
00266     //! assign a new value
00267     virtual void set(const std::string& str)=0;
00268     virtual void set(const ObjectBase& ob) { const PrimitiveBase& pb = dynamic_cast<const PrimitiveBase&>(ob); *this=pb; }
00269     //! return current value as a string
00270     virtual std::string get() const=0;
00271     
00272     virtual bool operator==(const PrimitiveBase& other)=0;
00273     
00274     virtual std::string toString() const { return get(); }
00275     
00276     //! get notified of changes; be sure to call removeValueListener before destructing @a vl!
00277     virtual void addPrimitiveListener(PrimitiveListener* vl) const;
00278     //! no longer take notification of changes to this object's value
00279     virtual void removePrimitiveListener(PrimitiveListener* vl) const;
00280     //! test if @a l is currently registered as a listener
00281     virtual bool isPrimitiveListener(PrimitiveListener * vl) const;
00282 
00283   protected:
00284     //! run through #primitiveListeners, calling PrimitiveListener::plistValueChanged(*this) or PrimitiveListener::plistValueTouched(*this)
00285     virtual void fireValueChanged(bool touch) const;
00286     //! stores a list of the current listeners
00287     mutable std::set<PrimitiveListener*>* primitiveListeners;
00288   };
00289   //! output stringified value (from PrimitiveBase::get()) to stream
00290   inline std::ostream& operator<<(std::ostream& os, const PrimitiveBase& pb) {
00291     return os << pb.get();
00292   }
00293   //! input value from next word in @a is, via PrimitiveBase::set()
00294   inline std::istream& operator>>(std::istream& is, PrimitiveBase& pb) {
00295     std::string s;
00296     is >> s;
00297     pb.set(s);
00298     return is;
00299   }
00300 
00301 } //namespace plist
00302 
00303 
00304 /*! @file
00305  * @brief 
00306  * @author Ethan Tira-Thompson (ejt) (Creator)
00307  */
00308 
00309 #endif

Tekkotsu v5.1CVS
Generated Mon May 9 04:58:47 2016 by Doxygen 1.6.3