Tekkotsu Homepage | Demos | Overview | Downloads | Dev. Resources | Reference | Credits |
plistBase.hGo 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 |