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

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