Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

plistPrimitives.h

Go to the documentation of this file.
00001 //-*-c++-*-
00002 #ifndef INCLUDED_plistPrimitives_h_
00003 #define INCLUDED_plistPrimitives_h_
00004 
00005 #include "plistBase.h"
00006 #include "string_util.h"
00007 #include <limits>
00008 #include <typeinfo>
00009 #include <algorithm>
00010 #include <map>
00011 #include <libxml/xmlmemory.h>
00012 
00013 extern "C" {
00014   //!@name libxml2 forward declarations
00015   //!forward declaration of the libxml2 struct of the same name
00016   xmlNode* xmlAddPrevSibling(xmlNode* node, xmlNode* sibling);
00017   xmlNode* xmlNewText(const xmlChar* s);
00018   xmlNode* xmlNewComment(const xmlChar* s);
00019   xmlNode* xmlAddChild(xmlNode * parent, xmlNode* child);
00020   xmlNode* xmlNewChild(xmlNode* parent, xmlNs* ns, const xmlChar * name, const xmlChar * content);
00021   int xmlStrEqual(const xmlChar* a, const xmlChar* b);
00022   xmlChar* xmlNodeGetContent(xmlNode* node);
00023   void xmlNodeSetContent(xmlNode* node, const xmlChar* content);
00024   xmlAttr* xmlHasProperty(xmlNode* node, const xmlChar* name);
00025   xmlChar* xmlGetProperty(xmlNode* node, const xmlChar* name);
00026   long xmlGetLineNo(xmlNode* node);
00027   xmlChar* xmlGetNodePath(xmlNode* node);
00028   void xmlNodeSetName(xmlNode* node, const xmlChar* name);
00029   void xmlFreeNode(xmlNode* node);
00030   void xmlUnlinkNode(xmlNode* node);
00031   //@}
00032 }
00033 
00034 namespace plist {
00035   //! returns a string indicating the plist entry type to use for the specified type
00036   /*! some primitives (bool, char) aren't handled because they require a specialization
00037    *  of Primitive and won't use this function.  If you want to use a plist Primitive of
00038    *  some custom type, you might be able to just define a new specialization of 
00039    *  this function and provide iostream <</>> operators for your type... */
00040   template<typename T> const char* getTypeName();
00041   /// @cond INTERNAL
00042   template<> inline const char* getTypeName<short>() { return "integer"; }
00043   template<> inline const char* getTypeName<unsigned short>() { return "integer"; }
00044   template<> inline const char* getTypeName<int>() { return "integer"; }
00045   template<> inline const char* getTypeName<unsigned int>() { return "integer"; }
00046   template<> inline const char* getTypeName<long>() { return "integer"; }
00047   template<> inline const char* getTypeName<unsigned long>() { return "integer"; }
00048   template<> inline const char* getTypeName<long long>() { return "integer"; }
00049   template<> inline const char* getTypeName<unsigned long long>() { return "integer"; }
00050   template<> inline const char* getTypeName<float>() { return "real"; }
00051   template<> inline const char* getTypeName<double>() { return "real"; }
00052   /// @endcond
00053   
00054   //! Implements type-specific functionality through template specialization, mainly involving value conversion and stringification formatting
00055   /*! Provides smart-pointer style functionality for transparent
00056    *  access to the value storage, as well as automatic casting
00057    *  to and from the value type so you can almost always treat
00058    *  the Primitive as if it was the value itself. */
00059   template<typename T>
00060   class Primitive : public PrimitiveBase {
00061   public:
00062     template<typename U, typename V> struct conversion_policy { typedef typename U::template ConversionTo<V> value_conversion; };
00063     
00064     //! constructor
00065     Primitive() : PrimitiveBase(), val(), prevVal() {}
00066     //! copy constructor, automatic conversion from value type
00067     Primitive(const T& v) : PrimitiveBase(), val(v), prevVal() {} 
00068     //! assignment from value type (template specializations add in-place modiciation (e.g. +=, *=))
00069     Primitive& operator=(const T& v) { if(&v==&prevVal) std::swap(val,prevVal); else { prevVal=val; val=v; } fireValueChanged(prevVal==val); return *this; }
00070     virtual Primitive& operator=(const PrimitiveBase& pb) { if(dynamic_cast<const Primitive*>(&pb)!=this) operator=(pb.castTo<T>()); return *this; }
00071     //! assignment from primitive of the same type (just assign value)
00072     Primitive& operator=(const Primitive& p) { operator=(p.val); return *this; }
00073 
00074     Primitive& operator+=(const T& v) { prevVal=val; val+=v; fireValueChanged(prevVal==val); return *this; } //!< add in-place
00075     Primitive& operator-=(const T& v) { prevVal=val; val-=v; fireValueChanged(prevVal==val); return *this; } //!< subtract in-place
00076     Primitive& operator*=(const T& v) { prevVal=val; val*=v; fireValueChanged(prevVal==val); return *this; } //!< multiply in-place
00077     Primitive& operator/=(const T& v) { prevVal=val; val/=v; fireValueChanged(prevVal==val); return *this; } //!< divide in-place
00078     
00079     //! smart pointer, dereference to access primitive storage
00080     const T& operator*() const { return val; }
00081     //! smart pointer, dereference to access primitive storage
00082     const T* operator->() const { return &val; }
00083     
00084     //! cast operator to automatically convert to value type
00085     operator T() const { return val; }
00086 
00087     //! interprets \a node as holding the specialization type
00088     void loadXML(xmlNode* node);
00089     //! saves #val into \a node
00090     void saveXML(xmlNode* node) const;
00091     void set(const std::string& str);
00092     using PrimitiveBase::set;
00093     std::string get() const {
00094       std::stringstream sstr;
00095       sstr <<std::setprecision(std::numeric_limits<T>::digits10)<< val;
00096       return sstr.str();
00097     }
00098     
00099     //bool operator==(const T& v) { return v==val; }
00100     //bool operator==(const Primitive& p) { return p.val==val; }
00101     virtual bool operator==(const PrimitiveBase& other) { 
00102       if(const Primitive* p = dynamic_cast<const Primitive*>(&other))
00103         return p->val == val;
00104       else
00105         return p->get() == get();
00106     }
00107     
00108     virtual long toLong() const { return static_cast<long>(val); }
00109     virtual double toDouble() const { return static_cast<double>(val); }
00110     
00111     //! clone definition for Primitive<T>
00112     PLIST_CLONE_DEF(Primitive<T>,new Primitive<T>(val));
00113     
00114     const T& getPreviousValue() const { return prevVal; } //!< returns the previously assigned value
00115 
00116   protected:
00117     T val; //!< storage of primitive value
00118     T prevVal; //!< following each assignment, this is the "old" value -- very handy for PrimitiveListeners
00119   };
00120   
00121   template<typename T>
00122   void Primitive<T>::loadXML(xmlNode* node) {
00123     if(node==NULL)
00124       return;
00125     bool bt=xNodeHasName(node,"true");
00126     bool bf=xNodeHasName(node,"false");
00127     if(!bt && !bf && !xNodeHasName(node,"integer") && !xNodeHasName(node,"real") && !xNodeHasName(node,"string")) {
00128       std::stringstream errstr;
00129       errstr << "Error: plist::Primitive<" << typeid(T).name() << "> expects " << getTypeName<T>() << ", got unknown type " << (const char*)xNodeGetName(node);
00130       throw bad_format(node,errstr.str());
00131     }
00132     if(!xNodeHasName(node,getTypeName<T>()))
00133       std::cerr << "Warning: plist expected " << getTypeName<T>() << " got " << (const char*)xNodeGetName(node) << ", trying to convert. (line " << xmlGetLineNo(node) << ")" << std::endl;
00134     if(bt) {
00135       prevVal=val;
00136       val = true;
00137       fireValueChanged(prevVal==val);
00138     } else if(bf) {
00139       prevVal=val;
00140       val = false;
00141       fireValueChanged(prevVal==val);
00142     } else {
00143       xmlChar * cont=xmlNodeGetContent(node);
00144       const std::string str((const char*)cont);
00145       xmlFree(cont);
00146       set(str);
00147     }
00148   }
00149   template<typename T>
00150   void Primitive<T>::saveXML(xmlNode* node) const {
00151     if(node==NULL)
00152       return;
00153     xmlNodeSetName(node,(const xmlChar*)getTypeName<T>());
00154     std::stringstream str;
00155     str <<std::setprecision(std::numeric_limits<T>::digits10)<< val;
00156     xmlNodeSetContent(node,(const xmlChar*)str.str().c_str());
00157   }
00158   template<typename T>
00159   void Primitive<T>::set(const std::string& str) {
00160     std::string trimmed = string_util::trim(str);
00161     if(trimmed.size()==0) {
00162       std::cerr << "Warning: plist expected " << getTypeName<T>() << ", got empty string" << std::endl;
00163       return;
00164     }
00165     bool neg=false;
00166     if(trimmed[0]=='-') {
00167       neg=true;
00168       trimmed=trimmed.substr(1);
00169     }
00170     prevVal=val;
00171     if((trimmed=="∞" || string_util::makeLower(trimmed)=="inf") && std::numeric_limits<T>::has_infinity) {
00172       val = neg ? -std::numeric_limits<T>::infinity() : std::numeric_limits<T>::infinity();
00173     } else {
00174       std::stringstream sstr(str);
00175       if(!(sstr >> val)) {
00176         std::string err="Expected "; err+=getTypeName<T>(); err+=" value, got '"+str+"'";
00177         throw bad_format(NULL,err);
00178       }
00179       while(sstr.good() && isspace(sstr.peek()))
00180         sstr.get();
00181       if(!sstr.eof()) {
00182         std::string err="Expected "; err+=getTypeName<T>(); err+=" value, got '"+sstr.str()+"'";
00183         throw bad_format(NULL,err);
00184       }
00185     }
00186     fireValueChanged(prevVal==val);
00187   }
00188   
00189   //! implements the clone function for Primitive<T>
00190   PLIST_CLONE_IMPT(T,Primitive,new Primitive<T>(val));
00191 
00192   
00193   //! Auto subscribe/unsubscribe from a Primitive
00194   struct AutoPrimitiveListener : public plist::PrimitiveListener {
00195     //! constructor, specify value
00196     AutoPrimitiveListener(const plist::PrimitiveBase& source) : plist::PrimitiveListener(), src(source) { src.addPrimitiveListener(this); }
00197     //! assignment, no-op
00198     AutoPrimitiveListener& operator=(const AutoPrimitiveListener&) { return *this; }
00199     //! destructor, automatically unsubscribes
00200     ~AutoPrimitiveListener() { src.removePrimitiveListener(this); }
00201     const plist::PrimitiveBase& src; //!< the value being monitored
00202   };
00203   //! Produces a callback on a member function when the value changes
00204   template<typename O>
00205   struct PrimitiveCallbackMember : public plist::AutoPrimitiveListener {
00206     //! constructor
00207     PrimitiveCallbackMember(const plist::PrimitiveBase& source, O& component, void(O::*func)(), bool callNow=true)
00208     : plist::AutoPrimitiveListener(source), comp(component), callback(func) { if(callNow) (comp.*callback)(); }
00209     //! assignment, no-op
00210     PrimitiveCallbackMember& operator=(const PrimitiveCallbackMember&) { return *this; }
00211     virtual void plistValueChanged(const plist::PrimitiveBase& /*pl*/) { (comp.*callback)(); }
00212     O& comp; //!< instance to call on
00213     void(O::*callback)(); //!< function to call
00214   };
00215   
00216   
00217   
00218 /// @cond SHOW_PLIST_OBJECT_SPECIALIZATION
00219   
00220   //! provides a @c bool specialization of Primitive<T>
00221   /*! A @c bool can be treated as either a string or an integer\n
00222    *  When saving, "true" or "false" will be used, but users could
00223    *  also specify "yes"/"no" or "on"/"off".  If an integer is used,
00224    *  it will be interpreted as usual: 0==false, otherwise true. */
00225   template<>
00226   class Primitive<bool> : public PrimitiveBase {
00227   public:
00228     template<typename U, typename V> struct conversion_policy { typedef typename U::template ConversionTo<V> value_conversion; };
00229     Primitive() : PrimitiveBase(), val(), prevVal() {} //!< constructor
00230     Primitive(const bool& v) : PrimitiveBase(), val(v), prevVal() {} //!< casting constructor
00231     Primitive& operator=(const bool& v) { if(&v==&prevVal) std::swap(val,prevVal); else { prevVal=val; val=v; } fireValueChanged(prevVal==val); return *this; } //!< assignment constructor
00232     virtual Primitive& operator=(const PrimitiveBase& pb) { if(dynamic_cast<const Primitive*>(&pb)!=this) operator=(pb.castTo<bool>()); return *this; }
00233     Primitive& operator=(const Primitive& p) { operator=(p.val); return *this; }
00234     //bool& operator*() { return val; }
00235     const bool& operator*() const { return val; } //!< dereference will return data storage
00236     //bool* operator->() { return &val; }
00237     const bool* operator->() const { return &val; } //!< can use -> to access members of data storage
00238     operator bool() const { return val; } //!< casting operator
00239 
00240     void loadXML(xmlNode* node); //!< interprets @a node as a bool
00241     void saveXML(xmlNode* node) const;  //!< saves #val into @a node
00242     void set(const std::string& str);
00243     using PrimitiveBase::set;
00244     std::string get() const {
00245       return val?"true":"false";
00246     }
00247     
00248     //bool operator==(const bool& v) { return v==val; }
00249     //bool operator==(const Primitive& p) { return p.val==val; }
00250     virtual bool operator==(const PrimitiveBase& other) { 
00251       if(const Primitive* p = dynamic_cast<const Primitive*>(&other))
00252         return p->val == val;
00253       else
00254         return p->get() == get();
00255     }
00256         
00257     virtual long toLong() const { return static_cast<long>(val); }
00258     virtual double toDouble() const { return static_cast<double>(val); }
00259     
00260     //! clone definition for Primitive<bool>
00261     PLIST_CLONE_DEF(Primitive<bool>,new Primitive<bool>(val));
00262     
00263     const bool& getPreviousValue() const { return prevVal; } //!< returns the previously assigned value
00264     
00265   protected:
00266     bool val; //!< the actual data storage
00267     bool prevVal; //!< following each assignment, this is the "old" value -- very handy for PrimitiveListeners
00268   };
00269   
00270   //! provides a @c char specialization of plist::Primitive<T>, adds a unique #numeric property to the usual template implementation
00271   /*! A @c char can be treated as either a string or an integer, you can use
00272    *  the setNumeric(bool) function to indicate which style to use when saving */
00273   template<>
00274   class Primitive<char> : public PrimitiveBase {
00275   public:
00276     template<typename U, typename V> struct conversion_policy { typedef typename U::template ConversionTo<V> value_conversion; };
00277     Primitive() : PrimitiveBase(), val(), prevVal(), numeric(false) {} //!< constructor
00278     Primitive(const char& v, bool isNum=false) : PrimitiveBase(), val(v), prevVal(), numeric(isNum) {} //!< casting constructor
00279     Primitive& operator=(char v) { if(&v==&prevVal) std::swap(val,prevVal); else { prevVal=val; val=v; } fireValueChanged(prevVal==val); return *this; } //!< assignment
00280     virtual Primitive& operator=(const PrimitiveBase& pb) { if(dynamic_cast<const Primitive*>(&pb)!=this) operator=(pb.castTo<char>()); return *this; }
00281     //! assignment from primitive of the same type (just assign value)
00282     Primitive& operator=(const Primitive& p) { operator=(p.val); return *this; }
00283     Primitive& operator+=(char v) { prevVal=val; val+=v; fireValueChanged(prevVal==val); return *this; } //!< add-assign
00284     Primitive& operator-=(char v) { prevVal=val; val-=v; fireValueChanged(prevVal==val); return *this; } //!< subtract-assign
00285     Primitive& operator*=(char v) { prevVal=val; val*=v; fireValueChanged(prevVal==val); return *this; } //!< multiply-assign
00286     Primitive& operator/=(char v) { prevVal=val; val/=v; fireValueChanged(prevVal==val); return *this; } //!< divide-assign
00287     //char& operator*() { return val; }
00288     const char& operator*() const { return val; } //!< dereference will return data storage
00289     //char* operator->() { return &val; }
00290     const char* operator->() const { return &val; } //!< can use -> to access members of data storage
00291     operator char() const { return val; } //!< casting operator
00292     
00293     void setNumeric(bool isNum) { numeric=isNum; } //!< sets #numeric
00294     bool getNumeric() const { return numeric; } //!< returns #numeric
00295     
00296     void loadXML(xmlNode* node); //!< interprets @a node as a char
00297     void saveXML(xmlNode* node) const; //! saves #val into @a node
00298     void set(const std::string& str);
00299     using PrimitiveBase::set;
00300     std::string get() const {
00301       if(numeric) {
00302         std::stringstream sstr;
00303         sstr << (int)val;
00304         return sstr.str();
00305       } else
00306         return std::string(1,val);
00307     }
00308 
00309     //bool operator==(const char& v) { return v==val; }
00310     //bool operator==(const Primitive& p) { return p.val==val; }
00311     virtual bool operator==(const PrimitiveBase& other) { 
00312       if(const Primitive* p = dynamic_cast<const Primitive*>(&other))
00313         return p->val == val;
00314       else
00315         return p->get() == get();
00316     }
00317     
00318     virtual long toLong() const { return static_cast<long>(val); }
00319     virtual double toDouble() const { return static_cast<double>(val); }
00320     
00321     //! clone definition for Primitive<char>
00322     PLIST_CLONE_DEF(Primitive<char>,new Primitive<char>(val));
00323     
00324     const char& getPreviousValue() const { return prevVal; } //!< returns the previously assigned value
00325     
00326   protected:
00327     char val; //!< data storage
00328     char prevVal; //!< following each assignment, this is the "old" value -- very handy for PrimitiveListeners
00329     bool numeric; //!< if true, requests that saves store the numeric value instead of corresponding character
00330   };
00331   
00332   //! provides an @c unsigned @c char specialization of plist::Primitive<T>, adds a unique #numeric property to the usual template implementation
00333   /*! A @c char can be treated as either a string or an integer, you can use
00334    *  the setNumeric(bool) function to indicate which style to use when saving */
00335   template<>
00336   class Primitive<unsigned char> : public PrimitiveBase {
00337   public:
00338     template<typename U, typename V> struct conversion_policy { typedef typename U::template ConversionTo<V> value_conversion; };
00339     Primitive() : PrimitiveBase(), val(), prevVal(), numeric(false) {} //!< constructor
00340     Primitive(const unsigned char& v, bool isNum=false) : PrimitiveBase(), val(v), prevVal(), numeric(isNum) {} //!< casting constructor
00341     Primitive& operator=(unsigned char v) { prevVal=val; val=v; fireValueChanged(prevVal==val); return *this; } //!< assignment
00342     virtual Primitive& operator=(const PrimitiveBase& pb) { if(dynamic_cast<const Primitive*>(&pb)!=this) operator=(pb.castTo<unsigned char>()); return *this; }
00343     //! assignment from primitive of the same type (just assign value)
00344     Primitive& operator=(const Primitive& p) { operator=(p.val); return *this; }
00345     Primitive& operator+=(unsigned char v) { prevVal=val; val+=v; fireValueChanged(prevVal==val); return *this; } //!< add-assign
00346     Primitive& operator-=(unsigned char v) { prevVal=val; val-=v; fireValueChanged(prevVal==val); return *this; } //!< subtract-assign
00347     Primitive& operator*=(unsigned char v) { prevVal=val; val*=v; fireValueChanged(prevVal==val); return *this; } //!< multiple-assign
00348     Primitive& operator/=(unsigned char v) { prevVal=val; val/=v; fireValueChanged(prevVal==val); return *this; } //!< divide-assign
00349     //unsigned char& operator*() { return val; }
00350     const unsigned char& operator*() const { return val; } //!< dereference will return data storage
00351     //unsigned char* operator->() { return &val; }
00352     const unsigned char* operator->() const { return &val; } //!< can use -> to access members of data storage
00353     operator unsigned char() const { return val; } //!< casting operator
00354     
00355     void setNumeric(bool isNum) { numeric=isNum; } //!< sets #numeric
00356     bool getNumeric() const { return numeric; } //!< returns #numeric
00357     
00358     void loadXML(xmlNode* node); //!< interprets @a node as a unsigned char
00359     void saveXML(xmlNode* node) const; //! saves #val into @a node
00360     void set(const std::string& str);
00361     using PrimitiveBase::set;
00362     std::string get() const {
00363       if(numeric) {
00364         std::stringstream sstr;
00365         sstr << (int)val;
00366         return sstr.str();
00367       } else
00368         return std::string(1,val);
00369     }
00370     
00371     //bool operator==(const unsigned char& v) { return v==val; }
00372     //bool operator==(const Primitive& p) { return p.val==val; }
00373     virtual bool operator==(const PrimitiveBase& other) { 
00374       if(const Primitive* p = dynamic_cast<const Primitive*>(&other))
00375         return p->val == val;
00376       else
00377         return p->get() == get();
00378     }
00379     
00380     virtual long toLong() const { return static_cast<long>(val); }
00381     virtual double toDouble() const { return static_cast<double>(val); }
00382     
00383     //! clone definition for Primitive<unsigned char>
00384     PLIST_CLONE_DEF(Primitive<unsigned char>,new Primitive<unsigned char>(val));
00385     
00386     const unsigned char& getPreviousValue() const { return prevVal; } //!< returns the previously assigned value
00387     
00388   protected:
00389     unsigned char val; //!< data storage
00390     unsigned char prevVal; //!< following each assignment, this is the "old" value -- very handy for PrimitiveListeners
00391     bool numeric; //!< if true, requests that saves store the numeric value instead of corresponding character
00392   };
00393   
00394   
00395   //! Provides a @c std::string specialization of Primitive<T>
00396   /*! Doesn't need to provide a operator cast because we subclass std::string itself! */
00397   template<>
00398   class Primitive<std::string> : public PrimitiveBase, public std::string {
00399   public:
00400     template<typename U, typename V> struct conversion_policy { typedef typename U::template ConversionTo<V> value_conversion; };
00401     Primitive() : PrimitiveBase(), std::string(), prevVal() {} //!< constructor
00402     Primitive(const std::string& v) : PrimitiveBase(), std::string(v), prevVal() {} //!< casting constructor
00403     Primitive(const std::string& v, size_type off, size_type count=npos) : PrimitiveBase(), std::string(v,off,count), prevVal() {} //!< casting constructor
00404     Primitive(const char* v, size_type count) : PrimitiveBase(), std::string(v,count), prevVal() {} //!< casting constructor
00405     Primitive(const char* v) : PrimitiveBase(), std::string(v), prevVal() {} //!< casting constructor
00406     Primitive(size_type count, char v) : PrimitiveBase(), std::string(count,v), prevVal() {} //!< casting constructor
00407     Primitive& operator=(const std::string& v) { if(&v==&prevVal) std::string::swap(prevVal); else { prevVal=*this; std::string::operator=(v); } fireValueChanged(prevVal==*this); return *this; } //!< assignment
00408     Primitive& operator=(const char* v) { prevVal=*this; std::string::operator=(v); fireValueChanged(prevVal==*this); return *this; } //!< assignment
00409     Primitive& operator=(char v) { prevVal=*this; std::string::operator=(v); fireValueChanged(prevVal==*this); return *this; } //!< assignment
00410     virtual Primitive& operator=(const PrimitiveBase& pb) { if(dynamic_cast<const Primitive*>(&pb)!=this) operator=(pb.toString()); return *this; }
00411     //! assignment from primitive of the same type (just assign value)
00412     Primitive& operator=(const Primitive& p) { operator=(static_cast<const std::string&>(p)); return *this; }
00413     //std::string& operator*() { return *this; }
00414     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)
00415     //std::string* operator->() { return this; }
00416     const std::string* operator->() const { return this; } //!< returns a pointer to this (for uniformity with the other Primitives, although unnecessary with this instantiation)
00417     //no casting operator because we subclass string
00418     
00419     void loadXML(xmlNode* node); //!< interprets @a node as a string
00420     void saveXML(xmlNode* node) const; //!< saves the content of the string into @a node
00421     void set(const std::string& str) { operator=(str); } // operator= will fireValueChanged
00422     using PrimitiveBase::set;
00423     std::string get() const { return *this; }
00424     
00425     //bool operator==(const std::string& v) { return v==*this; }
00426     //bool operator==(const Primitive& p) { return static_cast<std::string&>(*this)==static_cast<const std::string&>(p); }
00427     virtual bool operator==(const PrimitiveBase& other) { 
00428       return other.get() == *this;
00429     }
00430     
00431     virtual bool toBool() const;
00432     virtual char toChar() const;
00433     virtual long toLong() const;
00434     virtual double toDouble() const;
00435     
00436     void clear() { prevVal=*this; std::string::clear(); fireValueChanged(prevVal==*this); }
00437     void resize(size_type n, char c) { prevVal=*this; std::string::resize(n,c); fireValueChanged(prevVal==*this); }
00438     // todo: should add forwarding calls for other std::string members...
00439     
00440     //! clone definition for Primitive<std::string>
00441     PLIST_CLONE_DEF(Primitive<std::string>,new Primitive<std::string>(get()));
00442     
00443     const std::string& getPreviousValue() const { return prevVal; } //!< returns the previously assigned value
00444     
00445   protected:
00446     std::string prevVal; //!< stores the previously assigned value for reference/reset by a value listener
00447   };
00448   
00449   extern template class Primitive<short>;
00450   extern template class Primitive<unsigned short>;
00451   extern template class Primitive<int>;
00452   extern template class Primitive<unsigned int>;
00453   extern template class Primitive<long>;
00454   extern template class Primitive<unsigned long>;
00455   extern template class Primitive<float>;
00456   extern template class Primitive<double>;
00457   
00458   /// @endcond
00459   
00460   
00461   //! provides some accessors common across NamedEnumeration types
00462   class NamedEnumerationBase : public PrimitiveBase {
00463   public:
00464     NamedEnumerationBase() : PrimitiveBase(), strictValue(true) {} //!< constructor
00465     NamedEnumerationBase(const NamedEnumerationBase& ne) : PrimitiveBase(ne), strictValue(ne.strictValue) {} //!< copy constructor
00466     NamedEnumerationBase& operator=(const std::string& v) { set(v); return *this; } //!< assignment from string
00467     NamedEnumerationBase& operator=(const NamedEnumerationBase& ne) { PrimitiveBase::operator=(ne); return *this; } //!< assignment (doesn't copy #strictValue)
00468     virtual NamedEnumerationBase& operator=(const PrimitiveBase& pb)=0;
00469     
00470     virtual void set(long x)=0; //!< converts from a numeric value, may throw bad_format if #strictValue is set
00471     using PrimitiveBase::set;
00472     
00473     //! fills @a names with the names and values for the enum -- needed for generic access to the names (e.g. UI generation)
00474     virtual void getPreferredNames(std::map<int,std::string>& names) const=0;
00475     //! fills @a names with the names and values for the enum -- needed for generic access to the names (e.g. UI generation)
00476     virtual void getAllNames(std::map<std::string,int>& names) const=0;
00477     
00478     std::string getDescription(bool preferredOnly=true); //!< returns a string listing the available symbolic names
00479     void setStrict(bool strict) { strictValue=strict; } //!< sets #strictValue (whether or not to allow assignment from numeric values which don't have a symbolic name)
00480     bool getStrict() const { return strictValue; } //!< returns #strictValue (whether or not to allow assignment from numeric values which don't have a symbolic name)
00481     
00482   protected:
00483     bool strictValue; //!< if true, don't allow conversion from numeric string which doesn't correspond to a named value
00484   };
00485   
00486   //! 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
00487   /*! Where an array of names is specified, you must order the array such that
00488    *  the enumeration value can be used as an index into the array.  The array must be
00489    *  terminated with NULL so NamedEnumeration can tell how many
00490    *  elements there are.
00491    *
00492    *  <b>Binary size and symbol definition</b>: this class contains two static STL maps
00493    *  for storing the string names of the enumeration values.  The problem is that
00494    *  due to the way static members of templates are handled, you will wind up
00495    *  with extensive symbol declarations in each translation unit which references
00496    *  this header, which can lead to significant binary bloat (particularly with
00497    *  debugging symbols).  The solution is to limit the instantiation of these statics.
00498    *  
00499    *  - Easy way out: define USE_GLOBAL_PLIST_STATICS, which will then declare the statics
00500    *    here in the header, and allow the duplication to occur (which is fine for small
00501    *    projects or if you don't reference this header widely)
00502    *    
00503    *  - Otherwise, you can then declare:
00504    *    @code
00505    *    template<typename T> std::map<std::string,T> plist::NamedEnumeration<T>::namesToVals;
00506    *    template<typename T> std::map<T,std::string> plist::NamedEnumeration<T>::valsToNames;
00507    *    @endcode
00508    *    in the translation units where you introduce new types to the template parameter.
00509    *    This will greatly limit symbol replication, although there will still be some minor
00510    *    duplication if more than just the "new" types are found in the current unit.
00511    *    You may prefer to call the macro INSTANTIATE_ALL_NAMEDENUMERATION_STATICS() to ensure
00512    *    future compatability in the unlikely event more statics are added in the future.
00513    *    
00514    *  - For the ultimate minimal binary size, explicitly declare a template
00515    *    specialization for each type you use: (note 'YOUR_T' is meant to be replaced!)
00516    *    @code
00517    *    // replace YOUR_T with the type you are using:
00518    *    template<> std::map<std::string,YOUR_T> plist::NamedEnumeration<YOUR_T>::namesToVals = std::map<std::string,YOUR_T>();
00519    *    template<> std::map<YOUR_T,std::string> plist::NamedEnumeration<YOUR_T>::valsToNames = std::map<YOUR_T,std::string>();
00520    *    @endcode
00521    *    You can do this only once, in a single translation unit, yielding zero replication of symbols.
00522    *    For convenience, we provide a macro INSTANTIATE_NAMEDENUMERATION_STATICS(T) which will do this for you. */
00523   template<typename T> 
00524   class NamedEnumeration : public NamedEnumerationBase {
00525   public:
00526     template<typename U, typename V> struct conversion_policy { typedef typename U::template ConversionTo<V> value_conversion; };
00527     NamedEnumeration() : NamedEnumerationBase(), val(), prevVal() {} //!< constructor
00528     NamedEnumeration(const NamedEnumeration& ne) : NamedEnumerationBase(ne), val(ne.val), prevVal() {} //!< copy constructor
00529     NamedEnumeration(const T& v, const char * const* enumnames) : NamedEnumerationBase(), val(v), prevVal() { setNames(enumnames); } //!< constructor, pass initial value, array of strings (the names); assumes enumeration is sequential starting at 0, and runs until the names entry is NULL (i.e. @a names must be terminated with a NULL entry)
00530     NamedEnumeration(const T& v) : NamedEnumerationBase(), val(v), prevVal() {} //!< automatic casting from the enumeration
00531     NamedEnumeration& operator=(const T& v) { if(&v==&prevVal) std::swap(val,prevVal); else { prevVal=val; val=v; } fireValueChanged(prevVal==val); return *this; } //!< assignment from enumeration value (numeric)
00532     NamedEnumeration& operator=(const std::string& v) { set(v); return *this; } //!< assignment from string
00533     NamedEnumeration& operator=(const NamedEnumeration& ne) { operator=(ne.val); return *this; } //!< assignment
00534     //T& operator*() { return val; }
00535     const T& operator*() const { return val; } //!< value access
00536     operator T() const { return val; } //!< automatic casting to the enumeration value
00537     static void setNames(const char *  const* enumnames); //!< calls clearNames() and then resets the array of names, @a enumnames must be terminated with NULL, and the enumeration must be sequential starting at 0; these names become the "preferred" name for each value
00538     static const std::map<T,std::string>& getPreferredNames() { return valsToNames; } //!< returns mapping from numeric value to "preferred" name (one-to-one)
00539     static const std::map<std::string,T>& getAllNames() { return namesToVals; } //!< returns mapping from names to values (many-to-one allowed)
00540     static void clearNames(); //!< removes all names, thus causing only numeric values to be accepted
00541     static void addNameForVal(const std::string& enumname, const T& v); //!< adds an alternative name mapping to the specified numeric value; if the value doesn't already have a name, this is also the "preferred" name
00542     static void setPreferredNameForVal(const std::string& enumname, const T& v); //!< replaces any previous "preferred" name for a specific value
00543 
00544     //! polymorphic assignment, throws bad_format if #strictValue is requested and the value is invalid integer
00545     virtual NamedEnumeration& operator=(const PrimitiveBase& pb) {
00546       if(dynamic_cast<const NamedEnumeration*>(&pb)==this)
00547         return *this;
00548       if(const std::string* str = dynamic_cast<const std::string*>(&pb))
00549         set(*str);
00550       else {
00551         T tv=static_cast<T>(pb.toLong());
00552         if(strictValue && valsToNames.find(tv)==valsToNames.end())
00553           throw bad_format(NULL, "NamedEnumeration unable to assign arbitrary integer value because strict checking is requested");
00554         val=tv;
00555       }
00556       return *this;
00557     }
00558     
00559     //! interprets @a node as either a string holding the name, or a number corresponding to its index (name is preferred)
00560     void loadXML(xmlNode* node) {
00561       if(node==NULL)
00562         return;
00563       if(xNodeHasName(node,"true") || xNodeHasName(node,"false")) {
00564         std::string name=(const char*)xNodeGetName(node);
00565         std::cerr << "Warning: plist NamedEnumeration should use <string>" << name << "</string>, not <" << name << "/>" << std::endl;
00566         try {
00567           set(name);
00568         } catch(const bad_format& err) {
00569           throw bad_format(node,err.what()); //add current node to exception and rethrow
00570         }
00571       } else if(xNodeHasName(node,"integer") || xNodeHasName(node,"real") || xNodeHasName(node,"string")) {
00572         xmlChar * cont=xmlNodeGetContent(node);
00573         try {
00574           set((const char*)cont);
00575         } catch(const bad_format& err) {
00576           xmlFree(cont);
00577           throw bad_format(node,err.what()); //add current node to exception and rethrow
00578         } catch(...) {
00579           xmlFree(cont);
00580           throw;
00581         }
00582         xmlFree(cont);
00583       } else
00584         throw bad_format(node,"Error: plist NamedEnumeration must be numeric or valid string");
00585     }
00586     //! saves name of current value (if available, index used otherwise) into @a node
00587     void saveXML(xmlNode* node) const {
00588       if(node==NULL)
00589         return;
00590       std::string name;
00591       if(getNameForVal(val,name)) {
00592         xmlNodeSetName(node,(const xmlChar*)"string");
00593       } else {
00594         xmlNodeSetName(node,(const xmlChar*)"integer");
00595       }
00596       xmlNodeSetContent(node,(const xmlChar*)name.c_str());
00597     }
00598     void set(const std::string& str) {
00599       prevVal=val;
00600       if(!getValForName(str,val))
00601         throw bad_format(NULL,"Error: plist::NamedEnumeration must be numeric or valid string (cannot be '"+str+"')");
00602       fireValueChanged(prevVal==val); 
00603     }
00604     void set(long x) {
00605       T tv;
00606       if(!convert(x,tv)) {
00607         std::stringstream ss;
00608         ss << "Error: plist::NamedEnumeration must correspond to valid string (cannot be '" << x << "')";
00609         throw bad_format(NULL,ss.str());
00610       }
00611       prevVal=val;
00612       val=tv;
00613       fireValueChanged(prevVal==val); 
00614     }
00615     using NamedEnumerationBase::set;
00616     std::string get() const {
00617       std::string name;
00618       getNameForVal(val,name);
00619       return name;
00620     }
00621     
00622     //bool operator==(const T& v) { return v==val; }
00623     //bool operator==(const NamedEnumeration& p) { return p.val==val; }
00624     virtual bool operator==(const PrimitiveBase& other) { 
00625       if(const NamedEnumeration* p = dynamic_cast<const NamedEnumeration*>(&other))
00626         return p->val == val;
00627       else
00628         return p->get() == get();
00629     }
00630     
00631     virtual long toLong() const { return static_cast<long>(val); }
00632     virtual double toDouble() const { return static_cast<double>(val); }
00633     
00634     //! implements the clone function for NamedEnumeration<T>
00635     PLIST_CLONE_DEF(NamedEnumeration<T>,new NamedEnumeration<T>(*this));
00636 
00637     const T& getPreviousValue() const { return prevVal; } //!< returns the previously assigned value
00638     
00639   protected:
00640     //! provides the generic access to values and names from NamedEnumerationBase; protected because if you know the full type, better to use the static version of the function
00641     virtual void getPreferredNames(std::map<int,std::string>& names) const;
00642     //! provides the generic access to values and names from NamedEnumerationBase; protected because if you know the full type, better to use the static version of the function
00643     virtual void getAllNames(std::map<std::string,int>& names) const;
00644     
00645     //! attempts to convert x to T and store in v, but failing and returning false if strictValue is set and x does not correspond to a name
00646     bool convert(long x, T& v) const {
00647       T tv=static_cast<T>(x);
00648       if(strictValue && valsToNames.find(tv)==valsToNames.end())
00649         return false;
00650       v=tv;
00651       return true;
00652     }
00653     //! sets @a v to the enumeration value named @a name; returns false if no such name is found
00654     bool getValForName(std::string name, T& v) const {
00655       std::transform(name.begin(), name.end(), name.begin(), (int(*)(int)) std::toupper);
00656       typename std::map<std::string,T>::const_iterator vit=namesToVals.find(name);
00657       if(vit!=namesToVals.end())
00658         v=vit->second;
00659       else {
00660         long iv;
00661         if(sscanf(name.c_str(),"%ld",&iv)==0)
00662           return false;
00663         if(!convert(iv,v))
00664            return false;
00665       }
00666       return true;
00667     }
00668     //! retrieves the "preferred" name for the enumeration value @a v; returns false if no name is registered
00669     bool getNameForVal(const T& v, std::string& name) const {
00670       typename std::map<T,std::string>::const_iterator nit=valsToNames.find(v);
00671       if(nit!=valsToNames.end()) {
00672         name=nit->second;
00673         return true;
00674       }
00675       std::stringstream str;
00676       str << v;
00677       name=str.str();
00678       return false;
00679     }
00680     T val; //!< storage of enum value
00681     T prevVal; //!< storage of enum value
00682     
00683     //! look up table of string names to enum values (can have multiple string names mapping to same enum -- deprecated values for example)
00684     /*! See class notes regarding instantiation options for static values like this */
00685     static std::map<std::string,T> namesToVals;
00686     //! look up table of enum values to preferred display name (by default, first name given)
00687     /*! See class notes regarding instantiation options for static values like this */
00688     static std::map<T,std::string> valsToNames;
00689   };
00690   //! implements the clone function for NamedEnumeration<T>
00691   PLIST_CLONE_IMPT(T,NamedEnumeration,new NamedEnumeration<T>(*this));
00692 
00693 #ifdef USE_GLOBAL_PLIST_STATICS
00694   template<typename T> std::map<std::string,T> plist::NamedEnumeration<T>::namesToVals;
00695   template<typename T> std::map<T,std::string> plist::NamedEnumeration<T>::valsToNames;
00696 #endif
00697   
00698   //! Unless you enable GLOBAL_PLIST_STATICS, call this macro in each translation unit which introduces new template types
00699   /*! @see NamedEnumeration for further discussion */
00700 #define INSTANTIATE_ALL_NAMEDENUMERATION_STATICS() \
00701   namespace plist { \
00702     template<typename T> std::map<std::string,T> NamedEnumeration<T>::namesToVals; \
00703     template<typename T> std::map<T,std::string> NamedEnumeration<T>::valsToNames; \
00704   }
00705   
00706   //! Unless you enable GLOBAL_PLIST_STATICS, call this macro in one of your source files to provide a definition of the statics for a specific type
00707   /*! @see NamedEnumeration for further discussion */
00708 #define INSTANTIATE_NAMEDENUMERATION_STATICS(T) \
00709   namespace plist { \
00710     template<> std::map<std::string,T> NamedEnumeration<T>::namesToVals = std::map<std::string,T>(); \
00711     template<> std::map<T,std::string> NamedEnumeration<T>::valsToNames = std::map<T,std::string>(); \
00712   }
00713   
00714   template<typename T> void NamedEnumeration<T>::setNames(const char *  const* enumnames) {
00715     // first test if names are equivalent, this improves thread safety (but still is not thread safe during first initialization!)
00716     size_t incnt=0;
00717     while(enumnames[incnt]!=NULL)
00718       ++incnt;
00719     bool differ=false;
00720     if(incnt!=valsToNames.size() || incnt!=namesToVals.size())
00721       differ=true;
00722     else {
00723       for(size_t i=0; enumnames[i]!=NULL; ++i) {
00724         std::string name=enumnames[i];
00725         typename std::map<T,std::string>::const_iterator v2nit=valsToNames.find(static_cast<T>(i));
00726         if(v2nit==valsToNames.end() || v2nit->second!=name) {
00727           differ=true;
00728           break;
00729         }
00730         std::transform(name.begin(), name.end(), name.begin(), (int(*)(int)) std::toupper);
00731         typename std::map<std::string,T>::const_iterator n2vit=namesToVals.find(name);
00732         if(n2vit==namesToVals.end() || n2vit->second!=static_cast<T>(i)) {
00733           differ=true;
00734           break;
00735         }
00736       }
00737     }
00738     if(differ) { // we need to reset name/value maps
00739       // to further improve thread safety, load into a set of local maps, and then do a 'swap' which should be fast and leave less time for invalid state.
00740       std::map<std::string,T> newNamesToVals;
00741       std::map<T,std::string> newValsToNames;
00742       for(size_t i=0; enumnames[i]!=NULL; ++i) {
00743         std::string name=enumnames[i];
00744         newValsToNames[static_cast<T>(i)]=name;
00745         std::transform(name.begin(), name.end(), name.begin(), (int(*)(int)) std::toupper);
00746         newNamesToVals[name]=static_cast<T>(i);
00747       }
00748       namesToVals.swap(newNamesToVals);
00749       valsToNames.swap(newValsToNames);
00750     }
00751   }
00752   template<typename T> void NamedEnumeration<T>::clearNames() {
00753     namesToVals.clear();
00754     valsToNames.clear();
00755   }
00756   template<typename T> void NamedEnumeration<T>::addNameForVal(const std::string& enumname, const T& v) {
00757     if(valsToNames.find(v)==valsToNames.end())
00758       valsToNames[v]=enumname;
00759     std::string name=enumname;
00760     std::transform(name.begin(), name.end(), name.begin(), (int(*)(int)) std::toupper);
00761     namesToVals[name]=v;
00762   }
00763   template<typename T> void NamedEnumeration<T>::setPreferredNameForVal(const std::string& enumname, const T& v) {
00764     valsToNames[v]=enumname;
00765     addNameForVal(enumname,v);
00766   }
00767     
00768   template<typename T> void NamedEnumeration<T>::getPreferredNames(std::map<int,std::string>& names) const {
00769     names.clear();
00770     for(typename std::map<T,std::string>::const_iterator it=valsToNames.begin(); it!=valsToNames.end(); ++it)
00771       names.insert(std::pair<int,std::string>(it->first,it->second));
00772   }
00773   template<typename T> void NamedEnumeration<T>::getAllNames(std::map<std::string,int>& names) const {
00774     names.clear();
00775     for(typename std::map<std::string,T>::const_iterator it=namesToVals.begin(); it!=namesToVals.end(); ++it)
00776       names.insert(std::pair<std::string,int>(it->first,it->second));
00777   }
00778   
00779 } //namespace plist
00780 
00781 
00782 /*! @file
00783  * @brief 
00784  * @author Ethan Tira-Thompson (ejt) (Creator)
00785  */
00786 
00787 #endif

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