Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

plist.h

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #ifndef INCLUDED_PListSupport_h_
00003 #define INCLUDED_PListSupport_h_
00004 
00005 /*
00006  From: <!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
00007  
00008 <!ENTITY % plistObject "(array | data | date | dict | real | integer | string | true | false )" >
00009 <!ELEMENT plist %plistObject;>
00010 <!ATTLIST plist version CDATA "1.0" >
00011 
00012 <!-- Collections -->
00013 <!ELEMENT array (%plistObject;)*>
00014 <!ELEMENT dict (key, %plistObject;)*>
00015 <!ELEMENT key (#PCDATA)>
00016 
00017 <!--- Primitive types -->
00018 <!ELEMENT string (#PCDATA)>
00019 <!ELEMENT data (#PCDATA)> <!-- Contents interpreted as Base-64 encoded -->
00020 <!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) -->
00021 
00022 <!-- Numerical primitives -->
00023 <!ELEMENT true EMPTY>  <!-- Boolean constant true -->
00024 <!ELEMENT false EMPTY> <!-- Boolean constant false -->
00025 <!ELEMENT real (#PCDATA)> <!-- Contents should represent a floating point number matching ("+" | "-")? d+ ("."d*)? ("E" ("+" | "-") d+)? where d is a digit 0-9.  -->
00026 <!ELEMENT integer (#PCDATA)> <!-- Contents should represent a (possibly signed) integer number in base 10 -->
00027 */
00028 
00029 #include "XMLLoadSave.h"
00030 #include <exception>
00031 #include <string>
00032 #include <iostream>
00033 #include <iomanip>
00034 #include <sstream>
00035 #include <map>
00036 #include <list>
00037 
00038 //! A collection of classes to implement the Propery List data storage format, a XML standard used by Apple and others
00039 namespace plist {
00040   
00041   //! Base class for the plist listener callbacks
00042   class Listener {
00043   public:
00044     //! destructor
00045     virtual ~Listener() {}
00046   };
00047   
00048   class PrimitiveBase;
00049   //! 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()
00050   class PrimitiveListener : public Listener {
00051   public:
00052     //! This will be called whenever a plist you have registered with is changed
00053     /*! @a pl is const to help you avoid infinite recursion from an
00054      *  accidental modification of @a pl's value -- use a const cast
00055      *  if you're sure you know what you're doing */
00056     virtual void plistValueChanged(const PrimitiveBase& pl)=0;
00057   };
00058   
00059   class ObjectBase;
00060   class Dictionary;
00061   //! If you wish to be notified any time an entry is added or removed from a Dictionary, inherit from this and implement one or both of the functions, then register it with the dictionary's Dictionary::addDictionaryListener()
00062   class DictionaryListener : public Listener {
00063   public:
00064     //! This will be called whenever an entry is added to the dictionary
00065     virtual void plistDictionaryEntryAdded(Dictionary& /*dict*/, ObjectBase& /*primitive*/) {}
00066     //! This will be called whenever an entry is added to the dictionary
00067     virtual void plistDictionaryEntryRemoved(Dictionary& /*dict*/, ObjectBase& /*primitive*/) {}
00068   };
00069   
00070   //! This base class provides the root functionality for all plist entities -- Dictionary and the various templated subclasses of PrimitiveBase
00071   /*! The subclasses may throw std::bad_format if the
00072    *  document is poorly structured or bad values are found. */
00073   class ObjectBase : public XMLLoadSave {
00074   public:
00075     ObjectBase(); //!< constructor
00076     virtual ~ObjectBase()=0; //!< destructor
00077     
00078   protected:
00079     //!@name Inherited from XMLLoadSave
00080     virtual void setParseTree(xmlDoc * doc) const;
00081     virtual xmlNode* FindRootXMLElement(xmlDoc* doc) const;
00082     //@}
00083     
00084     //!Provides indirect access to the libxml function of the same name
00085     //!@name libxml Forwards
00086     typedef unsigned char xChar;
00087     static int xStrEqual(const xChar* a, const xChar* b);
00088     static int xStrCaseEqual(const xChar* a, const xChar* b);
00089     static xChar* xNodeGetContent(xmlNode* node);
00090     static void xNodeSetContent(xmlNode* node, const xChar* content);
00091     static const xChar* xNodeGetName(xmlNode* node);
00092     static bool xNodeHasName(xmlNode* node, const char* name);
00093     static void xNodeSetName(xmlNode* node, const xChar* name);
00094     static xmlAttr* xHasProperty(xmlNode* node, const xChar* name);
00095     static xChar* xGetProperty(xmlNode* node, const xChar* name);
00096     static long xGetLineNo(xmlNode* node);
00097     static void xFree(void* ptr);
00098     //@}
00099   };
00100   
00101   //! Maintains a set of (key,value) pairs, where a value can be any subclass of ObjectBase
00102   /*! This class supports callbacks upon modification through the use of the
00103    *  DictionaryListener interface.  Note that we only store a pointer to the
00104    *  listener list, which is typically unallocated when no listeners are
00105    *  active.  This should ensure minimal memory usage per object, as well as
00106    *  support safe storage of plist objects in inter-process shared memory
00107    *  regions.
00108    *
00109    *  If you are using these in a shared memory region, just be sure that only
00110    *  the process with listeners does any and all modifications, and that it
00111    *  unsubscribes before detaching from the region (or else destroys the region
00112    *  itself)
00113    *
00114    *  There isn't a callback if entries themselves are modified, only when new
00115    *  entries are added, or old ones removed.  If you want to know any time any
00116    *  aspect of any entry is modified, listen for the add and remove callbacks,
00117    *  and then add yourself as a listener to each entry individually.  */
00118   class Dictionary : public ObjectBase {
00119     friend std::ostream& operator<<(std::ostream& os, const Dictionary& d);
00120   public:
00121     //! constructor
00122     Dictionary() : ObjectBase(), dictionaryListeners(), warnUnused(true), dict(), comments() {autoFormat=false;}
00123     //! copy constructor (don't assign listeners)
00124     Dictionary(const Dictionary& d) : ObjectBase(d), dictionaryListeners(), warnUnused(d.warnUnused), dict(d.dict), comments(d.comments) {}
00125     //! assignment (don't assign listeners)
00126     Dictionary& operator=(const Dictionary& d) { if(&d==this) return *this; ObjectBase::operator=(d); warnUnused=d.warnUnused; dict=d.dict; comments=d.comments; return *this; }
00127     //! destructor
00128     virtual ~Dictionary();
00129     
00130     //! insert a new entry to the dictionary, with key @a name and value @a val (replaces any previous entry by same name, but gives a warning)
00131     void addEntry(const std::string& name, ObjectBase& val);
00132     //! insert a new entry to the dictionary, with key @a name and value @a val (replaces any previous entry by same name, but gives a warning)
00133     void addEntry(const std::string& name, ObjectBase& val, const std::string& comment);
00134     //! replaces previous entry of the same name, or adds it if it doesn't exist; use addEntry if you want to assert that the name does not already exist
00135     void setEntry(const std::string& name, ObjectBase& val) { dict[name]=&val; }
00136     //! replaces previous comment for @a name, or adds it if it doesn't already exist (can preceed actual entry!)
00137     void setComment(const std::string& name, const std::string& comment) { comments[name]=comment; }
00138     //! returns comment retrieved from loaded file, or any subsequent call to setComment
00139     const std::string& getComment(const std::string& name) const;
00140     //! remove the entry with key @a name
00141     void removeEntry(const std::string& name) { dict.erase(name); }
00142     //! return the value of the key @a name, or NULL if it doesn't exist
00143     ObjectBase* findEntry(const std::string& name) const;
00144     
00145     void LoadXML(xmlNode* node);
00146     void SaveXML(xmlNode* node) const;
00147     
00148     void setUnusedWarning(bool b) { warnUnused=b; } //!< set #warnUnused
00149     bool getUnusedWarning() { return warnUnused; } //!< returns #warnUnused
00150 
00151     //! get notified of changes; be sure to call removeValueListener before destructing @a vl!
00152     virtual void addDictionaryListener(DictionaryListener* vl);
00153     //! no longer take notification of changes to this object's value
00154     virtual void removeDictionaryListener(DictionaryListener* vl);
00155     
00156   protected:
00157     //! run through #dictionaryListeners, calling DictionaryListener::plistDictionaryEntryAdded(*this,val)
00158     virtual void fireEntryAdded(ObjectBase& val);
00159     //! run through #dictionaryListeners, calling DictionaryListener::plistDictionaryEntryRemoved(*this,val)
00160     virtual void fireEntryRemoved(ObjectBase& val);
00161     //! stores a list of the current listeners
00162     std::list<DictionaryListener*>* dictionaryListeners;
00163 
00164     //! return the length of the longest key for formatting purposes
00165     unsigned int getLongestKeyLen() const;
00166     //! if true (the default) LoadXML will give warnings if there are unused entries in the node it is passed
00167     bool warnUnused;
00168 
00169     //! shorthand for the type of #dict
00170     typedef std::map<std::string,ObjectBase*> dict_t;
00171     //! storage of entries -- mapping from keys to value pointers
00172     dict_t dict;
00173 
00174     //! shorthand for the type of #comments
00175     typedef std::map<std::string,std::string> comments_t;
00176     //! storage of entry comments -- mapping from keys to help text comments for manual editing or user feedback
00177     /*! not every key necessarily has a comment! */
00178     comments_t comments;
00179   };
00180   //! provides textual output
00181   std::ostream& operator<<(std::ostream& os, const Dictionary& d);
00182 
00183 
00184   //! Provides common functionality to all primitive value classes (implemented in a templated subclass Primitive)
00185   /*! This class supports callbacks upon modification through the use of the
00186    *  PrimitiveListener interface.  Note that we only store a pointer to the
00187    *  listener list, which is typically unallocated when no listeners are
00188    *  active.  This should ensure minimal memory usage per object, as well as
00189    *  support safe storage of plist objects in inter-process shared memory
00190    *  regions.
00191    *
00192    *  If you are using these in a shared memory region, just be sure that only
00193    *  the process with listeners does any and all modifications, and that it
00194    *  unsubscribes before detaching from the region (or else destroys the region
00195    *  itself) */
00196   class PrimitiveBase : public ObjectBase {
00197   public:
00198     //! constructor
00199     PrimitiveBase() : ObjectBase(), primitiveListeners() {}
00200     //! copy constructor (don't copy listeners)
00201     PrimitiveBase(const PrimitiveBase& pb) : ObjectBase(pb), primitiveListeners() {}
00202     //! assignment (don't assign listeners)
00203     PrimitiveBase& operator=(const PrimitiveBase& pb) { ObjectBase::operator=(pb); return *this; }
00204     //! destructor
00205     ~PrimitiveBase();
00206     
00207     //! assign a new value
00208     virtual void set(const std::string& str)=0;
00209     //! return current value as a string
00210     virtual std::string get() const=0;
00211 
00212     //! get notified of changes; be sure to call removeValueListener before destructing @a vl!
00213     virtual void addPrimitiveListener(PrimitiveListener* vl);
00214     //! no longer take notification of changes to this object's value
00215     virtual void removePrimitiveListener(PrimitiveListener* vl);
00216 
00217   protected:
00218     //! run through #primitiveListeners, calling PrimitiveListener::plistValueChanged(*this)
00219     virtual void fireValueChanged() const;
00220     //! stores a list of the current listeners
00221     std::list<PrimitiveListener*>* primitiveListeners;
00222   };
00223   //! output stringified value (from PrimitiveBase::get()) to stream
00224   inline std::ostream& operator<<(std::ostream& os, const PrimitiveBase& pb) {
00225     return os << pb.get();
00226   }
00227   //! input value from next word in @a is, via PrimitiveBase::set()
00228   inline std::istream& operator>>(std::istream& is, PrimitiveBase& pb) {
00229     std::string s;
00230     is >> s;
00231     pb.set(s);
00232     return is;
00233   }
00234   
00235 
00236   //! Implements type-specific functionality through template specialization, mainly involving value conversion and stringification formatting
00237   /*! Provides smart-pointer style functionality for transparent
00238    *  access to the value storage, as well as automatic casting
00239    *  to and from the value type so you can almost always treat
00240    *  the Primitive as if it was the value itself. */
00241   template<typename T>
00242   class Primitive : public PrimitiveBase {
00243   public:
00244     Primitive() : ObjectBase(), val() {} //!< constructor
00245     Primitive(const T& v) : ObjectBase(), val(v) {} //!< constructor, automatic casting from the value type
00246     Primitive& operator=(const T& v) { val=v; fireValueChanged(); return *this; } //!< assignment
00247     //T& operator*() { return val; } //!< access value -- if #val is modified, this won't know, so PrimitiveListeners are going to be clueless
00248     const T& operator*() const { return val; } //!< smart pointer access to value
00249     //T* operator->() { return &val; } //!< smart pointer access to value -- if #val is modified, this won't know, so PrimitiveListeners are going to be clueless
00250     const T* operator->() const { return &val; } //!< smart pointer access to value
00251     operator T() const { return val; } //!< automatic casting to the value type
00252 /*    void LoadXML(xmlNode* node) {
00253       if(node==NULL)
00254         return;
00255       //decode base64 from xml node
00256       //val->LoadBuffer(buf,bufsize);
00257       fireValueChanged(); 
00258     }
00259     void SaveXML(xmlNode* node) const {
00260       if(node==NULL)
00261         return;
00262       unsigned int bufsize=val->getBinSize();
00263       char * buf = new char[bufsize];
00264       val->SaveBuffer(buf,bufsize);
00265       //base64 encode into xml node
00266       delete [] buf;
00267     }*/
00268   protected:
00269     T val; //!< value storage
00270   };
00271 
00272   //! provides a @c bool specialization of Primitive<T>
00273   /*! A @c bool can be treated as either a string or an integer\n
00274    *  When saving, "true" or "false" will be used, but users could
00275    *  also specify "yes"/"no" or "on"/"off".  If an integer is used,
00276    *  it will be interpreted as usual: 0==false, otherwise true. */
00277   template<>
00278   class Primitive<bool> : public PrimitiveBase {
00279   public:
00280     Primitive() : PrimitiveBase(), val() {} //!< constructor
00281     Primitive(const bool& v) : PrimitiveBase(), val(v) {} //!< casting constructor
00282     Primitive& operator=(const bool& v) { val=v; fireValueChanged(); return *this; } //!< assignment constructor
00283     //bool& operator*() { return val; }
00284     const bool& operator*() const { return val; } //!< dereference will return data storage
00285     //bool* operator->() { return &val; }
00286     const bool* operator->() const { return &val; } //!< can use -> to access members of data storage
00287     operator bool() const { return val; } //!< casting operator
00288     void LoadXML(xmlNode* node) {
00289       if(node==NULL)
00290         return;
00291       if(xNodeHasName(node,"true")) {
00292         val=true;
00293         fireValueChanged(); 
00294       } else if(xNodeHasName(node,"false")) {
00295         val=false;
00296         fireValueChanged(); 
00297       } else if(xNodeHasName(node,"integer") || xNodeHasName(node,"real")) {
00298         xChar * cont=xNodeGetContent(node);
00299         std::stringstream str((char*)cont);
00300         str >> val;
00301         xFree(cont);
00302         fireValueChanged(); 
00303       } else if(xNodeHasName(node,"string")) {
00304         xChar * cont=xNodeGetContent(node);
00305         try {
00306           set((char*)cont);
00307           std::cerr << "Warning: plist boolean expects value of '<true/>', '<false/>', or numeric.  String value of '" << (char*)cont << "' is not recommended." << std::endl;
00308         } catch(const bad_format& err) {
00309           xFree(cont);
00310           throw bad_format(node,err.what());
00311         } catch(...) {
00312           xFree(cont);
00313           throw;
00314         }
00315         xFree(cont);
00316       } else
00317         throw bad_format(node,"Error: plist boolean must be 'true', 'false', or numeric type");
00318     }
00319     void SaveXML(xmlNode* node) const {
00320       if(node==NULL)
00321         return;
00322       xNodeSetName(node,(const xChar*)(val?"true":"false"));
00323       xNodeSetContent(node,NULL);
00324     }
00325     void set(const std::string& str) {
00326       if(str=="true" || str=="yes" || str=="on")
00327         val=true;
00328       else if(str=="false" || str=="no" || str=="off")
00329         val=false;
00330       else {
00331         float t;
00332         if(sscanf(str.c_str(),"%g",&t)==0)
00333           throw bad_format(NULL,"Error: plist boolean must be 'true', 'false', or numeric type");
00334         val=t;
00335       }
00336       fireValueChanged(); 
00337     }
00338     std::string get() const {
00339       return val?"true":"false";
00340     }
00341   protected:
00342     bool val; //!< the actual data storage
00343   };
00344 
00345   //! provides a @c char specialization of Primitive<T>
00346   /*! A @c char can be treated as either a string or an integer, you can use
00347    *  the setNumeric(bool) function to indicate which style to use when saving */
00348   template<>
00349   class Primitive<char> : public PrimitiveBase {
00350   public:
00351     Primitive() : PrimitiveBase(), val(), numeric(false) {} //!< constructor
00352     Primitive(const char& v, bool isNum=false) : PrimitiveBase(), val(v), numeric(isNum) {} //!< casting constructor
00353     Primitive& operator=(const char& v) { val=v; fireValueChanged(); return *this; } //!< assignment
00354     Primitive& operator+=(const char& v) { val+=v; fireValueChanged(); return *this; } //!< add-assign
00355     Primitive& operator-=(const char& v) { val-=v; fireValueChanged(); return *this; } //!< subtract-assign
00356     Primitive& operator*=(const char& v) { val*=v; fireValueChanged(); return *this; } //!< multiply-assign
00357     Primitive& operator/=(const char& v) { val/=v; fireValueChanged(); return *this; } //!< divide-assign
00358     //char& operator*() { return val; }
00359     const char& operator*() const { return val; } //!< dereference will return data storage
00360     //char* operator->() { return &val; }
00361     const char* operator->() const { return &val; } //!< can use -> to access members of data storage
00362     operator char() const { return val; } //!< casting operator
00363     void setNumeric(bool isNum) { numeric=isNum; } //!< sets #numeric
00364     bool getNumeric() const { return numeric; } //!< returns #numeric
00365     void LoadXML(xmlNode* node) {
00366       if(node==NULL)
00367         return;
00368       xChar* cont=xNodeGetContent(node);
00369       try {
00370         if(xNodeHasName(node,"string")) {
00371           set((char*)cont);
00372         } else if(xNodeHasName(node,"integer")) {
00373           val=strtol((const char*)cont,NULL,0);
00374           fireValueChanged(); 
00375         } else if(xNodeHasName(node,"real")) {
00376           val=(char)strtod((const char*)cont,NULL);
00377           fireValueChanged(); 
00378         } else if(xNodeHasName(node,"true")) {
00379           val=true;
00380           fireValueChanged(); 
00381         } else if(xNodeHasName(node,"false")) {
00382           val=false;
00383           fireValueChanged(); 
00384         } else {
00385           throw bad_format(node,"Error: plist char must be either a string or integer");
00386         } 
00387       } catch(const bad_format& err) {
00388         xFree(cont);
00389         throw bad_format(node,err.what());
00390       } catch(...) {
00391         xFree(cont);
00392         throw;
00393       }
00394       xFree(cont);
00395     }
00396     void SaveXML(xmlNode* node) const {
00397       if(node==NULL)
00398         return;
00399       if(numeric) {
00400         xNodeSetName(node,(const xChar*)"integer");
00401         std::stringstream str;
00402         str << (int)val;
00403         xNodeSetContent(node,(const xChar*)str.str().c_str());
00404       } else {
00405         xNodeSetName(node,(const xChar*)"string");
00406         xChar str[2] = {val,'\0'};
00407         xNodeSetContent(node,(const xChar*)str);
00408       }
00409     }
00410     void set(const std::string& str) {
00411       if(str.size()==0)
00412         throw bad_format(NULL,"Error: plist char must have non-empty content");
00413       val=str[0];
00414       if(str.size()!=1)
00415         std::cerr << "Warning: plist expected single char, found multi-char string '" << str << "'" << std::endl;
00416       fireValueChanged(); 
00417     }
00418     std::string get() const {
00419       return std::string(1,val);
00420     }
00421   protected:
00422     char val; //!< data storage
00423     bool numeric; //!< if true, requests that saves store the numeric value instead of corresponding character
00424   };
00425   
00426   //! provides an @c unsigned @c char specialization of Primitive<T>
00427   /*! A @c char can be treated as either a string or an integer, you can use
00428    *  the setNumeric(bool) function to indicate which style to use when saving */
00429   template<>
00430   class Primitive<unsigned char> : public PrimitiveBase {
00431   public:
00432     Primitive() : PrimitiveBase(), val(), numeric(false) {} //!< constructor
00433     Primitive(const unsigned char& v, bool isNum=false) : PrimitiveBase(), val(v), numeric(isNum) {} //!< casting constructor
00434     Primitive& operator=(const unsigned char& v) { val=v; fireValueChanged(); return *this; } //!< assignment
00435     Primitive& operator+=(const unsigned char& v) { val+=v; fireValueChanged(); return *this; } //!< add-assign
00436     Primitive& operator-=(const unsigned char& v) { val-=v; fireValueChanged(); return *this; } //!< subtract-assign
00437     Primitive& operator*=(const unsigned char& v) { val*=v; fireValueChanged(); return *this; } //!< multiple-assign
00438     Primitive& operator/=(const unsigned char& v) { val/=v; fireValueChanged(); return *this; } //!< divide-assign
00439     //unsigned char& operator*() { return val; }
00440     const unsigned char& operator*() const { return val; } //!< dereference will return data storage
00441     //unsigned char* operator->() { return &val; }
00442     const unsigned char* operator->() const { return &val; } //!< can use -> to access members of data storage
00443     operator unsigned char() const { return val; } //!< casting operator
00444     void setNumeric(bool isNum) { numeric=isNum; } //!< sets #numeric
00445     bool getNumeric() const { return numeric; } //!< returns #numeric
00446     void LoadXML(xmlNode* node) {
00447       if(node==NULL)
00448         return;
00449       xChar* cont=xNodeGetContent(node);
00450       try {
00451         if(xNodeHasName(node,"string")) {
00452           set((char*)cont);
00453         } else if(xNodeHasName(node,"integer")) {
00454           val=strtol((const char*)cont,NULL,0);
00455           fireValueChanged(); 
00456         } else if(xNodeHasName(node,"real")) {
00457           val=(char)strtod((const char*)cont,NULL);
00458           fireValueChanged(); 
00459         } else if(xNodeHasName(node,"true")) {
00460           val=true;
00461           fireValueChanged(); 
00462         } else if(xNodeHasName(node,"false")) {
00463           val=false;
00464           fireValueChanged(); 
00465         } else {
00466           throw bad_format(node,"Error: plist unsigned char must be either a string or integer");
00467         } 
00468       } catch(const bad_format& err) {
00469         xFree(cont);
00470         throw bad_format(node,err.what());
00471       } catch(...) {
00472         xFree(cont);
00473         throw;
00474       }
00475       xFree(cont);
00476     }
00477     void SaveXML(xmlNode* node) const {
00478       if(node==NULL)
00479         return;
00480       if(numeric) {
00481         xNodeSetName(node,(const xChar*)"integer");
00482         std::stringstream str;
00483         str << (int)val;
00484         xNodeSetContent(node,(const xChar*)str.str().c_str());
00485       } else {
00486         xNodeSetName(node,(const xChar*)"string");
00487         xChar str[2] = {val,'\0'};
00488         xNodeSetContent(node,(const xChar*)str);
00489       }
00490     }
00491     void set(const std::string& str) {
00492       if(str.size()==0)
00493         throw bad_format(NULL,"Error: plist char must have non-empty content");
00494       val=str[0];
00495       if(str.size()!=1)
00496         std::cerr << "Warning: plist expected single char, found multi-char string '" << str << "'" << std::endl;
00497     }
00498     std::string get() const {
00499       return std::string(1,val);
00500     }
00501   protected:
00502     unsigned char val; //!< data storage
00503     bool numeric; //!< if true, requests that saves store the numeric value instead of corresponding character
00504   };
00505 
00506   
00507 //! a macro to provide template specializations for the numeric primitives, which only need to vary their string name
00508 #define PLIST_OBJECT_SPECIALIZATION(T,PRIM) \
00509   template<> \
00510   class Primitive<T> : public PrimitiveBase { \
00511   public: \
00512     Primitive() : PrimitiveBase(), val() {} \
00513     Primitive(const T& v) : PrimitiveBase(), val(v) {} \
00514     Primitive& operator=(const T& v) { val=v; fireValueChanged(); return *this; } \
00515     Primitive& operator+=(const T& v) { val+=v; fireValueChanged(); return *this; } \
00516     Primitive& operator-=(const T& v) { val-=v; fireValueChanged(); return *this; } \
00517     Primitive& operator*=(const T& v) { val*=v; fireValueChanged(); return *this; } \
00518     Primitive& operator/=(const T& v) { val/=v; fireValueChanged(); return *this; } \
00519     /*T& operator*() { return val; }*/ \
00520     const T& operator*() const { return val; } \
00521     /*T* operator->() { return &val; }*/ \
00522     const T* operator->() const { return &val; } \
00523     operator T() const { return val; } \
00524     void LoadXML(xmlNode* node) { \
00525       if(node==NULL) \
00526         return; \
00527       bool bt=xNodeHasName(node,"true"); \
00528       bool bf=xNodeHasName(node,"false"); \
00529       if(!bt && !bf && !xNodeHasName(node,"integer") && !xNodeHasName(node,"real") && !xNodeHasName(node,"string")) \
00530         throw bad_format(node,"Error: plist "#T" expects "PRIM" type"); \
00531       if(!xNodeHasName(node,PRIM)) \
00532         std::cerr << "Warning: plist expected "PRIM" got " << (const char*)xNodeGetName(node) << ", trying to convert. (line " << xGetLineNo(node) << ")" << std::endl; \
00533       if(bt) \
00534         val = true; \
00535       else if(bf) \
00536         val = false; \
00537       else { \
00538         xChar * cont=xNodeGetContent(node); \
00539         std::stringstream str((const char*)cont); \
00540         str >> val; \
00541         xFree(cont); \
00542       } \
00543       fireValueChanged(); \
00544     } \
00545     void SaveXML(xmlNode* node) const { \
00546       if(node==NULL) \
00547         return; \
00548       xNodeSetName(node,(const xChar*)PRIM); \
00549       std::stringstream str; \
00550       str <<std::setprecision(128)<< val; \
00551       xNodeSetContent(node,(const xChar*)str.str().c_str()); \
00552     } \
00553     void set(const std::string& str) { \
00554       std::stringstream sstr(str); \
00555       sstr >> val; \
00556       fireValueChanged(); \
00557     } \
00558     std::string get() const { \
00559       std::stringstream sstr; \
00560       sstr <<std::setprecision(128)<< val; \
00561       return sstr.str(); \
00562     } \
00563   protected: \
00564     T val; \
00565   }
00566   
00567   PLIST_OBJECT_SPECIALIZATION(short,"integer"); //!< short instance of PrimitiveBase ("integer")
00568   PLIST_OBJECT_SPECIALIZATION(unsigned short,"integer"); //!< unsigned short instance of PrimitiveBase ("integer")
00569   PLIST_OBJECT_SPECIALIZATION(int,"integer"); //!< int instance of PrimitiveBase ("integer")
00570   PLIST_OBJECT_SPECIALIZATION(unsigned int,"integer"); //!< unsigned int instance of PrimitiveBase ("integer")
00571   PLIST_OBJECT_SPECIALIZATION(long,"integer"); //!< long instance of PrimitiveBase ("integer")
00572   PLIST_OBJECT_SPECIALIZATION(unsigned long,"integer"); //!< unsigned long instance of PrimitiveBase ("integer")
00573   PLIST_OBJECT_SPECIALIZATION(long long,"integer"); //!< long long instance of PrimitiveBase ("integer")
00574   PLIST_OBJECT_SPECIALIZATION(unsigned long long,"integer"); //!< unsigned long long instance of PrimitiveBase ("integer")
00575   PLIST_OBJECT_SPECIALIZATION(float,"real"); //!< float instance of PrimitiveBase ("real")
00576   PLIST_OBJECT_SPECIALIZATION(double,"real"); //!< double instance of PrimitiveBase ("real")
00577 
00578   //! Provides a @c std::string specialization of Primitive<T>
00579   /*! Doesn't need to provide a operator cast because we subclass std::string itself! */
00580   template<>
00581   class Primitive<std::string> : public PrimitiveBase, public std::string {
00582   public:
00583     Primitive() : PrimitiveBase(), std::string() {} //!< constructor
00584     Primitive(const std::string& v) : PrimitiveBase(), std::string(v) {} //!< casting constructor
00585     Primitive& operator=(const std::string& v) { std::string::operator=(v); fireValueChanged(); return *this; } //!< assignment
00586     //std::string& operator*() { return *this; }
00587     const std::string& operator*() const { return *this; } //!< dereference will return data storage as a string (for uniformity with the other Primitives, although unnecessary with this instantiation)
00588     //std::string* operator->() { return this; }
00589     const std::string* operator->() const { return this; } //!< returns a pointer to this (for uniformity with the other Primitives, although unnecessary with this instantiation)
00590     //no casting operator because we subclass string
00591     void LoadXML(xmlNode* node) {
00592       if(node==NULL)
00593         return;
00594       if(xNodeHasName(node,"string")) {
00595         xChar * cont=xNodeGetContent(node);
00596         *this=(char*)cont;
00597         xFree(cont);
00598       } else {
00599         if(xNodeHasName(node,"integer") || xNodeHasName(node,"real")) {
00600           xChar * cont=xNodeGetContent(node);
00601           *this=(char*)cont;
00602           xFree(cont);
00603         } else if(xNodeHasName(node,"true"))
00604           *this="true";
00605         else if(xNodeHasName(node,"false"))
00606           *this="false";
00607         else
00608           throw bad_format(node,"Error: plist string must be 'true', 'false', or numeric type");
00609         std::cerr << "Warning: plist string expected, found " << (const char*)xNodeGetName(node) << " on line " << xGetLineNo(node) << std::endl;
00610         fireValueChanged(); 
00611       }
00612     }
00613     void SaveXML(xmlNode* node) const {
00614       if(node==NULL)
00615         return;
00616       xNodeSetName(node,(const xChar*)"string");
00617       xNodeSetContent(node,(const xChar*)c_str());
00618     }
00619     void set(const std::string& str) {
00620       *this=str;
00621       fireValueChanged(); 
00622     }
00623     std::string get() const {
00624       return *this;
00625     }
00626   };
00627   
00628   //! Provides an interface for the use of enumerations in a plist -- you can specify values by either the string name or the corresponding integer value
00629   /*! Where an array of names is required, you must order the array such that
00630    *  the enumeration value can be used as an index into the array.
00631    *  The 'maxval' parameters should be one above the maximum enumeration -- 
00632    *  if your enumeration runs sequentially from 0 to n, #max should be the
00633    *  number of enumerations: n+1 */
00634   template<typename T> 
00635   class NamedEnumeration : public PrimitiveBase {
00636   public:
00637     NamedEnumeration() : PrimitiveBase(), val(), names(NULL), max(0) {} //!< constructor
00638     NamedEnumeration(const NamedEnumeration& ne) : PrimitiveBase(ne), val(ne.val), names(ne.names), max(ne.max) {} //!< copy constructor
00639     NamedEnumeration(const T& v, const char * const* enumnames, unsigned int maxval) : PrimitiveBase(), val(v), names(enumnames), max(maxval) {} //!< constructor, pass initial value, array of strings (the names), and the one-plus-maximum enum value (i.e. the number of enumeration values if they run sequentially from 0)
00640     NamedEnumeration(const T& v) : PrimitiveBase(), val(v), names(NULL), max(0) {} //!< automatic casting from the enumeration
00641     NamedEnumeration& operator=(const T& v) { val=v; fireValueChanged(); return *this; } //!< assignment from enumeration value (numeric)
00642     NamedEnumeration& operator=(const std::string& v) { set(v); return *this; } //!< assignment from string
00643     NamedEnumeration& operator=(const NamedEnumeration& ne) { PrimitiveBase::operator=(ne); val=ne.val; return *this; } //!< assignment
00644     //T& operator*() { return val; }
00645     const T& operator*() const { return val; } //!< value access
00646     operator T() const { return val; } //!< automatic casting to the enumeration value
00647     void setNames(const char *  const* enumnames, unsigned int maxval) { names=enumnames; max=maxval; } //!< (re)set the array of names and one-plus-maximum enum value (i.e. the number of enumeration values if they run sequentially from 0)
00648     const char*  const* getNames() const { return names; } //!< returns the array of names previously provided from constructor or setNames()
00649     const char* getName(unsigned int i) const { return names[i]; } //!< returns the name for a particular index
00650     unsigned int getMax() const { return max; } //!< returns the one-past-maximum of enumeration values previously provided to constructor or setNames()
00651 
00652     void LoadXML(xmlNode* node) {
00653       if(node==NULL)
00654         return;
00655       if(xNodeHasName(node,"true") || xNodeHasName(node,"false")) {
00656         std::string name=(const char*)xNodeGetName(node);
00657         unsigned int i=findName(name.c_str());
00658         if(i==-1U)
00659           throw bad_format(node,("Error: plist NamedEnumeration cannot be '"+name+"'").c_str());
00660         val=static_cast<T>(i);
00661         std::cerr << "Warning: plist NamedEnumeration should use <string>" << name << "</string>, not <" << name << "/>" << std::endl;
00662       } else if(xNodeHasName(node,"integer") || xNodeHasName(node,"real") || xNodeHasName(node,"string")) {
00663         xChar * cont=xNodeGetContent(node);
00664         try {
00665           set((char*)cont);
00666         } catch(const bad_format& err) {
00667           xFree(cont);
00668           throw bad_format(node,err.what());
00669         } catch(...) {
00670           xFree(cont);
00671           throw;
00672         }
00673         xFree(cont);
00674       } else
00675         throw bad_format(node,"Error: plist NamedEnumeration must be numeric or valid string");
00676     }
00677     void SaveXML(xmlNode* node) const {
00678       if(node==NULL)
00679         return;
00680       if(names!=NULL && names[val]!=NULL && val>0 && (unsigned int)val<max) {
00681         xNodeSetName(node,(const xChar*)"string");
00682       } else {
00683         xNodeSetName(node,(const xChar*)"integer");
00684       }
00685       xNodeSetContent(node,(const xChar*)get().c_str());
00686     }
00687     void set(const std::string& str) {
00688       unsigned int i=findName(str.c_str());
00689       if(i==-1U) {
00690         if(sscanf(str.c_str(),"%d",&i)==0)
00691           throw bad_format(NULL,"Error: plist NamedEnumeration must be numeric or valid string");
00692       }
00693       val=static_cast<T>(i);
00694       fireValueChanged(); 
00695     }
00696     std::string get() const {
00697       if(names!=NULL && names[val]!=NULL && val>=0 && (unsigned int)val<max)
00698         return names[val];
00699       std::stringstream str;
00700       str << val;
00701       return str.str();
00702     }
00703   protected:
00704     //! returns the enum corresponding to @a name
00705     unsigned int findName(const char* name) {
00706       if(name==NULL || names==NULL)
00707         return -1U;
00708       for(unsigned int i=0; i<max; i++)
00709         if(names[i] && strcmp(name,names[i])==0)
00710           return i;
00711       return -1U;
00712     }
00713     T val; //!< storage of enum value
00714     const char *  const* names; //!< pointer to array of names -- enum value must be able to serve as index for lookup
00715     unsigned int max; //!< one-plus-maximum enum value, i.e. the number of enum entries if they are ordered sequentially from 0
00716   };
00717   
00718   //! output of an ObjectBase to a stream
00719   inline std::ostream& operator<<(std::ostream& os, const ObjectBase& pb) {
00720     if(const PrimitiveBase * pbp=dynamic_cast<const PrimitiveBase*>(&pb))
00721       os << *pbp;
00722     else if(const Dictionary * dp=dynamic_cast<const Dictionary*>(&pb))
00723       os << *dp;
00724     else
00725       os << "[Unknown ObjectBase subclass, edit " << __FILE__ << ":" << __LINE__ << "]";
00726     return os;
00727   }
00728   
00729 } //namespace plist
00730 
00731 /*! @file
00732  * @brief Provides the plist namespace, a collection of classes to implement the Propery List data storage format, a XML standard used by Apple and others
00733  * @author Ethan Tira-Thompson (ejt) (Creator)
00734  *
00735  * $Author: ejt $
00736  * $Name: tekkotsu-2_4_1 $
00737  * $Revision: 1.9 $
00738  * $State: Exp $
00739  * $Date: 2005/08/07 04:11:04 $
00740  */
00741 
00742 #endif

Tekkotsu v2.4.1
Generated Tue Aug 16 16:32:48 2005 by Doxygen 1.4.4