| Tekkotsu Homepage | Demos | Overview | Downloads | Dev. Resources | Reference | Credits |
plistCollections.hGo to the documentation of this file.00001 //-*-c++-*- 00002 #ifndef INCLUDED_plistCollections_h_ 00003 #define INCLUDED_plistCollections_h_ 00004 00005 #include "plistPrimitives.h" 00006 #include <map> 00007 #include <vector> 00008 #include <set> 00009 #include <regex.h> 00010 namespace plist { 00011 ObjectBase* loadXML(xmlNode* node); // defined in plist.h, we need the prototype 00012 } 00013 00014 namespace plist { 00015 00016 //! Provides a common base class for the collection-oriented primitives, Dictionary and Array 00017 /*! 00018 * When a collection, you can call addEntry() or setEntry() you can either: 00019 * - pass a pointer to an ObjectBase or directly pass a primitive value (int, float, char, etc.), 00020 * in which case the Array will assume management of the corresponding ObjectBase 00021 * instance (freeing the memory region when removed) 00022 * - pass a reference to an ObjectBase, in which case you retain control over the object's 00023 * allocation 00024 * 00025 * This class supports callbacks upon modification through the use of the 00026 * CollectionListener interface. Note that we only store a pointer to the 00027 * listener list, which is typically unallocated when no listeners are 00028 * active. This should ensure minimal memory usage per object, as well as 00029 * support safe storage of plist objects in inter-process shared memory 00030 * regions. 00031 * 00032 * If you are using these in a shared memory region, just be sure that only 00033 * the process with listeners does any and all modifications, and that it 00034 * unsubscribes before detaching from the region (or else destroys the region 00035 * itself) 00036 * 00037 * There isn't a callback if entries themselves are modified, only when new 00038 * entries are added, or old ones removed. If you want to know any time any 00039 * aspect of any entry is modified, listen for the add and remove callbacks, 00040 * and then add yourself as a listener to each entry individually. */ 00041 class Collection : public ObjectBase { 00042 public: 00043 //! Specifies that a collection of collections cannot contain any primitive values 00044 template<typename U, typename V> struct conversion_policy { typedef typename U::DeniedValueConversions value_conversion; }; 00045 00046 //! used to define values for LoadSavePolicy values so we can test a bit out of the policy value 00047 enum LoadSaveActionBitMask { 00048 ADDITIONS=1, //!< if this bit is set in #loadPolicy's value, entries not found in the collection will be added, or for #savePolicy, entries will be added to the file 00049 REMOVALS=2 //!< if this bit is set in #loadPolicy's value, entries not found in the file will be removed from the collection, or for #savePolicy, entries will be removed from the file 00050 }; 00051 00052 //! Arguments for setLoadPolicy() and setSavePolicy(), specifies how to handle orphaned entries when loading or saving 00053 /*! An entry is considered "orphaned" if you are loading or saving into a pre-existing file, 00054 * and an entry exists in one location (the collection or the file), but not the other. 00055 * The results look like this... (add/remove refer to the destination i.e. collection if loading, file if saving): 00056 * <table> 00057 * <tr><td>@b Loading</td><td>SYNC</td><td>INTERSECT</td><td>UNION</td><td>FIXED</td></tr> 00058 * <tr><td>not in file, in collection</td><td>remove</td><td>remove</td><td>ignore</td><td>ignore</td></tr> 00059 * <tr><td>in file, not in collection</td><td>add</td><td>ignore</td><td>add</td><td>ignore</td></tr> 00060 * <tr><td>@b Saving</td><td>SYNC</td><td>INTERSECT</td><td>UNION</td><td>FIXED</td></tr> 00061 * <tr><td>not in file, in collection</td><td>add</td><td>ignore</td><td>add</td><td>ignore</td></tr> 00062 * <tr><td>in file, not in collection</td><td>remove</td><td>remove</td><td>ignore</td><td>ignore</td></tr> 00063 * </table> 00064 * 00065 * Commonly, you'll want to use SYNC (the default) to support dynamic storage, and FIXED load/SYNC save 00066 * for configuration settings (or perhaps FIXED load and UNION save to keep 'extra' values in the file...)*/ 00067 enum LoadSavePolicy { 00068 FIXED = 0, //!< destination will have the same entries as before, ignores orphans and only updates entries with matching keys 00069 UNION = ADDITIONS, //!< destination will have its current entries, as well as new ones from the source 00070 INTERSECT = REMOVALS, //!< destination will only hold entries which are in both locations, removes entries not found in source, ignores new entries 00071 SYNC = ADDITIONS|REMOVALS //!< destination will mirror source, new entries are added, missing entries are removed 00072 }; 00073 00074 //! assignment (don't assign listeners); subclass should call fireEntriesChanged after calling this (and updating its own storage) 00075 Collection& operator=(const Collection& d) { if(&d==this) return *this; ObjectBase::operator=(d); return *this; } 00076 00077 //! destructor 00078 ~Collection(); 00079 00080 //! recursively resolves @a path interpreted as a series of collection entry names separated by '.', returns NULL if it doesn't exist 00081 virtual ObjectBase* resolveEntry(const std::string& path) const=0; 00082 //! remove all entries in one fell swoop 00083 virtual void clear()=0; 00084 //! return the size of the collection 00085 virtual size_t size() const=0; 00086 00087 //! get notified of changes; be sure to call removeCollectionListener() before destructing @a l! 00088 virtual void addCollectionListener(CollectionListener* l) const; 00089 //! no longer take notification of changes to this object's value 00090 virtual void removeCollectionListener(CollectionListener* l) const; 00091 //! test if @a l is currently registered as a listener 00092 virtual bool isCollectionListener(CollectionListener* l) const; 00093 00094 void setUnusedWarning(bool b) { warnUnused=b; } //!< set #warnUnused 00095 bool getUnusedWarning() const { return warnUnused; } //!< returns #warnUnused 00096 00097 virtual LoadSavePolicy getLoadPolicy() const { return loadPolicy; } //!< returns #loadPolicy 00098 virtual LoadSavePolicy getSavePolicy() const { return savePolicy; } //!< returns #savePolicy 00099 virtual void setLoadPolicy(LoadSavePolicy lp) { loadPolicy=lp; } //!< assigns #loadPolicy 00100 virtual void setSavePolicy(LoadSavePolicy sp) { savePolicy=sp; } //!< assigns #savePolicy 00101 virtual void setLoadSavePolicy(LoadSavePolicy lp, LoadSavePolicy sp) { setLoadPolicy(lp); setSavePolicy(sp); } //!< assigns #loadPolicy and #savePolicy respectively 00102 00103 //! defines separator between sub-collections 00104 /*! (defined as a function instead of just a constant member so there's no issues with initialization order) */ 00105 static const std::string& subCollectionSep() { 00106 static std::string sep(1,'.'); 00107 return sep; 00108 } 00109 00110 //! returns longest key length which matches the regular expression 00111 virtual unsigned int getLongestKeyLen(const regex_t* reg=NULL, unsigned int depth=-1) const=0; 00112 00113 //! returns true if the Collection subclass allows storage of the argument 00114 virtual bool canContain(const ObjectBase& obj)=0; 00115 00116 virtual long toLong() const; 00117 virtual double toDouble() const; 00118 00119 protected: 00120 //! constructor 00121 Collection() : ObjectBase(), collectionListeners(), warnUnused(true), loadPolicy(SYNC), savePolicy(SYNC) {autoFormat=false;} 00122 //! constructor 00123 Collection(LoadSavePolicy lp, LoadSavePolicy sp) : ObjectBase(), collectionListeners(), warnUnused(true), loadPolicy(lp), savePolicy(sp) {autoFormat=false;} 00124 //! copy constructor (don't assign listeners) 00125 Collection(const Collection& d) : ObjectBase(d), collectionListeners(), warnUnused(d.warnUnused), loadPolicy(d.loadPolicy), savePolicy(d.savePolicy) {autoFormat=false;} 00126 00127 //! run through #collectionListeners, calling CollectionListener::plistCollectionEntryAdded(*this,val) 00128 virtual void fireEntryAdded(ObjectBase& val); 00129 //! run through #collectionListeners, calling CollectionListener::plistCollectionEntryRemoved(*this,val) 00130 virtual void fireEntryRemoved(ObjectBase& val); 00131 //! run through #collectionListeners, calling CollectionListener::plistCollectionEntriesChanged(*this) 00132 virtual void fireEntriesChanged(); 00133 //! stores a list of the current listeners 00134 mutable std::set<CollectionListener*>* collectionListeners; 00135 00136 //! returns index corresponding to @a name, which should encode an integer value less than or equal to the current size 00137 static size_t getIndex(const std::string& name); 00138 00139 //! returns a prefix for items within the collection 00140 static std::string getIndentationPrefix(xmlNode* node); 00141 00142 //! when an empty string is needed for not found items 00143 /*! (defined as a function instead of just a constant member so there's no issues with initialization order) */ 00144 static const std::string& emptyStr() { 00145 static std::string mt; 00146 return mt; 00147 } 00148 //! how much to indent each sub-collection 00149 /*! (defined as a function instead of just a constant member so there's no issues with initialization order) */ 00150 static const std::string& perIndent() { 00151 static std::string pi(1,'\t'); 00152 return pi; 00153 } 00154 //! when saving comments to file, the key name will automatically be prepended to the comment, unless the key is found within this many characters from the beginning of the comment 00155 static const unsigned int KEY_IN_COMMENT_MAX_POS=10; 00156 00157 //! if true (the default) loadXML will give warnings using FIXED policy and there are ignored entries in the source while loading or ignored entries in the destination while saving 00158 bool warnUnused; 00159 //! specifies how to handle "orphaned" entries while loading 00160 LoadSavePolicy loadPolicy; 00161 //! specifies how to handle "orphaned" entries while saving 00162 LoadSavePolicy savePolicy; 00163 }; 00164 00165 00166 00167 00168 //! Maintains a set of (key,value) pairs, see DictionaryOf, and the Dictionary typedef 00169 /*! You can add or set entries by a quite a few variations on addEntry(), setEntry(), or addValue(). 00170 * Basically these boil down to either: 00171 * - pass a pointer to an ObjectBase or directly pass a primitive value (int, float, char, etc.), 00172 * in which case the dictionary will assume management of the corresponding ObjectBase 00173 * instance (freeing the memory region when removed) 00174 * - pass a reference to an ObjectBase, in which case you retain control over the object's 00175 * allocation 00176 */ 00177 class DictionaryBase : virtual public Collection { 00178 friend std::ostream& operator<<(std::ostream& os, const DictionaryBase& d); 00179 public: 00180 //! shorthand for the type of the storage 00181 typedef std::map<std::string,ObjectBase*> storage_t; 00182 //! shorthand for iterators to be returned 00183 typedef storage_t::iterator iterator; 00184 //! shorthand for iterators to be returned 00185 typedef storage_t::const_iterator const_iterator; 00186 00187 //! Indicates that no value conversions are allowed 00188 /*! The actual storage type is still allowed, so technically we could use EntryConstraint<PO> 00189 * instead as the conversion policy, but that doesn't gain anything because you would need 00190 * to know the PO to test for it. At least with this you can test for DeniedValueConversions as a base 00191 * class and then fall back to testing individual PO's if you want. */ 00192 struct DeniedValueConversions { 00193 virtual ~DeniedValueConversions() {} //!< no-op destructor 00194 }; 00195 00196 //! This base conversion policy doesn't actually specify any conversions at all -- this serves as a base class to provide the ability to directly add entries of the specified type; the subclasses will add value conversions 00197 /*! This class is useless to end users: to use it they would have to know the template type being used, 00198 * which if they knew, they could just dynamic_cast to the DictionaryOf type directly. The point of this 00199 * class is to provide the abstract functions to the its subclasses, which use them to implement their 00200 * various conversions. */ 00201 template<typename PO> 00202 struct EntryConstraint { 00203 virtual ~EntryConstraint() {} //!< no-op destructor 00204 //! insert a new entry to the dictionary, with key @a name and value @a val (replaces any previous entry by same name, but can give a warning) 00205 virtual void setEntry(const std::string& name, PO& val, bool warnExists=false)=0; 00206 //! insert a new entry to the dictionary, with key @a name and value @a val (replaces any previous entry by same name with a warning) 00207 virtual void addEntry(const std::string& name, PO& val, const std::string& comment="", bool warnExists=true)=0; 00208 //! insert a new entry to the dictionary, with key @a name and value @a val (replaces any previous entry by same name, but can give a warning); control of (de)allocation will be assumed by the dictionary 00209 virtual void setEntry(const std::string& name, PO* val, bool warnExists=false)=0; 00210 //! insert a new entry to the dictionary, with key @a name and value @a val (replaces any previous entry by same name with a warning); control of (de)allocation will be assumed by the dictionary 00211 virtual void addEntry(const std::string& name, PO* val, const std::string& comment="", bool warnExists=true)=0; 00212 }; 00213 00214 //! Abstract base class to test whether the collection will accept strings (possibly converting to a value type, or storing directly as string depending on concrete type) 00215 /*! The point of this class is to handle the situation where you have a DictionaryBase and user input to append, and 00216 * you don't want to have to test every combination of template parameters to DictionaryOf to find out if you can 00217 * add the data. Instead, test dynamic_cast<plist::DictionaryBase::StringConversion>, and if it is successful, you 00218 * can pass the string without having to know the actual value type of the dictionary. */ 00219 struct StringConversion { 00220 virtual ~StringConversion() {} //!< no-op destructor 00221 //! generic addition of value, control of (de)allocation of corresponding Primitive instance will be assumed by the dictionary 00222 virtual void addValue(const std::string& name, const std::string& val, const std::string& comment="", bool warnExists=true)=0; 00223 }; 00224 00225 //! Abstract base class to test whether the collection will accept integers (possibly converting to another value type, or storing directly as a [unsigned] long depending on concrete type) 00226 /*! The point of this class is to handle the situation where you have a DictionaryBase and user input to append, and 00227 * you don't want to have to test every combination of template parameters to DictionaryOf to find out if you can 00228 * add the data. Instead, test dynamic_cast<plist::DictionaryBase::IntegerConversion>, and if it is successful, you 00229 * can pass the data without having to know the actual value type of the dictionary. */ 00230 struct IntegerConversion { 00231 virtual ~IntegerConversion() {} //!< no-op destructor 00232 //! generic addition of value, control of (de)allocation of corresponding Primitive instance will be assumed by the dictionary 00233 virtual void addValue(const std::string& name, long val, const std::string& comment="", bool warnExists=true)=0; 00234 //! generic addition of value, control of (de)allocation of corresponding Primitive instance will be assumed by the dictionary 00235 virtual void addValue(const std::string& name, unsigned long val, const std::string& comment="", bool warnExists=true)=0; 00236 }; 00237 00238 //! Abstract base class to test whether the collection will accept floating point numbers (possibly converting to another value type, or storing directly as a double depending on concrete type) 00239 /*! The point of this class is to handle the situation where you have a DictionaryBase and user input to append, and 00240 * you don't want to have to test every combination of template parameters to DictionaryOf to find out if you can 00241 * add the data. Instead, test dynamic_cast<plist::DictionaryBase::RealConversion>, and if it is successful, you 00242 * can pass the data without having to know the actual value type of the dictionary. */ 00243 struct RealConversion { 00244 virtual ~RealConversion() {} //!< no-op destructor 00245 //! generic addition of value, control of (de)allocation of corresponding Primitive instance will be assumed by the dictionary 00246 virtual void addValue(const std::string& name, double val, const std::string& comment="", bool warnExists=true)=0; 00247 }; 00248 00249 //! This conversion policy accepts entries of the specified template type, and will try to create new instances of that type constructed from any values which are passed. 00250 /*! Use of this conversion policy requires that the template parameter is not abstract, 00251 * as the policy will be trying to create new instances directly from the specified type. */ 00252 template<typename PO> 00253 struct ConversionTo : public StringConversion, public IntegerConversion, public RealConversion, public EntryConstraint<PO> { 00254 //! insert a new entry to the map, and corresponding comment; expects @a val to be either a primitive type, like int, float, etc., or one of the variable-sized Collection's, like Array, control of (de)allocation of corresponding Primitive instance will be assumed by the dictionary 00255 template<typename T> 00256 void addValue(const std::string& name, const T& val, const std::string& comment="", bool warnExists=true) { addEntry(name,new PO(val),comment,warnExists); } 00257 virtual void addValue(const std::string& name, const std::string& val, const std::string& comment="", bool warnExists=true) { PO * po=new PO; try { po->set(val); } catch(...) { delete po; throw; } addEntry(name,po,comment,warnExists); } 00258 //! "specialization" (actually just another override) for handling character arrays as strings, control of (de)allocation of corresponding Primitive instance will be assumed by the dictionary 00259 virtual void addValue(const std::string& name, char val[], const std::string& comment="", bool warnExists=true) { PO * po=new PO; try { po->set(val); } catch(...) { delete po; throw; } addEntry(name,po,comment,warnExists); } 00260 //! "specialization" (actually just another override) for handling character arrays as strings, control of (de)allocation of corresponding Primitive instance will be assumed by the dictionary 00261 virtual void addValue(const std::string& name, const char val[], const std::string& comment="", bool warnExists=true) { PO * po=new PO; try { po->set(val); } catch(...) { delete po; throw; } addEntry(name,po,comment,warnExists); } 00262 virtual void addValue(const std::string& name, long val, const std::string& comment="", bool warnExists=true) { addEntry(name,new PO(val),comment,warnExists); } 00263 virtual void addValue(const std::string& name, unsigned long val, const std::string& comment="", bool warnExists=true) { addEntry(name,new PO(val),comment,warnExists); } 00264 virtual void addValue(const std::string& name, double val, const std::string& comment="", bool warnExists=true) { addEntry(name,new PO(val),comment,warnExists); } 00265 }; 00266 00267 //! This conversion policy accepts any entries of the specified template type, values will be directly wrapped as Primitives so no conversion at all is actually performed 00268 /*! Use addEntry() to add ObjectBase subclasses -- addValue is for POD types */ 00269 template<typename PO> 00270 struct WrapValueConversion : public StringConversion, public IntegerConversion, public RealConversion, public EntryConstraint<PO> { 00271 //! insert a new entry to the map, and corresponding comment; expects @a val to be either a primitive type, like int, float, etc., control of (de)allocation of corresponding Primitive instance will be assumed by the dictionary 00272 template<typename T> 00273 void addValue(const std::string& name, const T& val, const std::string& comment="", bool warnExists=true) { this->addEntry(name,new Primitive<T>(val),comment,warnExists); } 00274 virtual void addValue(const std::string& name, const std::string& val, const std::string& comment="", bool warnExists=true) { this->addEntry(name,new Primitive<std::string>(val),comment,warnExists); } 00275 //! "specialization" (actually just another override) for handling character arrays as strings, control of (de)allocation of corresponding Primitive instance will be assumed by the dictionary 00276 virtual void addValue(const std::string& name, char val[], const std::string& comment="", bool warnExists=true) { this->addEntry(name,new Primitive<std::string>(val),comment,warnExists); } 00277 //! "specialization" (actually just another override) for handling character arrays as strings, control of (de)allocation of corresponding Primitive instance will be assumed by the dictionary 00278 virtual void addValue(const std::string& name, const char val[], const std::string& comment="", bool warnExists=true) { this->addEntry(name,new Primitive<std::string>(val),comment,warnExists); } 00279 virtual void addValue(const std::string& name, long val, const std::string& comment="", bool warnExists=true) { this->addEntry(name,new Primitive<long>(val),comment,warnExists); } 00280 virtual void addValue(const std::string& name, unsigned long val, const std::string& comment="", bool warnExists=true) { this->addEntry(name,new Primitive<unsigned long>(val),comment,warnExists); } 00281 virtual void addValue(const std::string& name, double val, const std::string& comment="", bool warnExists=true) { this->addEntry(name,new Primitive<double>(val),comment,warnExists); } 00282 }; 00283 00284 //! remove the entry with key @a name, returns true if something was actually removed (if false, wasn't there to begin with) 00285 virtual bool removeEntry(const std::string& name); 00286 00287 //! change the key for an entry from @a oldname to @a newname, returns false if @a oldname didn't exist (thus no change was made) 00288 /*! If the collection owns the reference to the object, you have to use this function instead 00289 * of a pair of calls to removeEntry/addEntry, otherwise you will wind up with an invalid pointer! */ 00290 virtual bool renameEntry(const std::string& oldname, const std::string& newname); 00291 //! exchange the values for a pair of keys -- if either key doesn't exist, forwards call to renameEntry() 00292 /*! returns true if the swap was successful, only returns false if both keys are invalid */ 00293 virtual bool swapEntry(const std::string& a, const std::string& b); 00294 00295 //! returns a reference to the entry with the specified name, creating it if it doesn't exist 00296 virtual ObjectBase& getEntry(const std::string& name)=0; 00297 //! returns a reference to the entry with the specified name, creating it if it doesn't exist 00298 virtual ObjectBase& operator[](const std::string& name)=0; 00299 //! returns a pointer to entry with the specified 'path', which may recurse through sub-collections 00300 virtual ObjectBase* resolveEntry(const std::string& path) const; 00301 00302 //! returns an iterator to an entry in the current dictionary 00303 const_iterator findEntry(const std::string& name) const { return dict.find(name); } 00304 00305 virtual void clear(); 00306 00307 //! return an STL const_iterator to the first entry 00308 const_iterator begin() const { return dict.begin(); } 00309 //! return the one-past-end const_iterator 00310 const_iterator end() const { return dict.end(); } 00311 //! return the size of the dictionary 00312 virtual size_t size() const { return dict.size(); } 00313 00314 //! replaces previous comment for @a name, or adds it if it doesn't already exist (can preceed actual entry!) 00315 void setComment(const std::string& name, const std::string& comment); 00316 //! returns comment retrieved from loaded file, or any subsequent call to setComment 00317 const std::string& getComment(const std::string& name) const; 00318 00319 virtual void loadXML(xmlNode* node); 00320 virtual void saveXML(xmlNode* node) const { std::set<std::string> seen; saveXML(node,!(savePolicy&ADDITIONS),seen); } 00321 //! saves the dictionary into the specified node 00322 /*! @param[in] node the xml node which should be saved into 00323 * @param[in] onlyOverwrite if is true, only saves entries for keys already found in the node (this overrides the current savePolicy value) 00324 * @param[in] seen used to keep track of which nodes have been seen in @a node -- may be of particular interest with @a onlyOverride set 00325 * 00326 * @a seen is not cleared before being used.*/ 00327 virtual void saveXML(xmlNode* node, bool onlyOverwrite, std::set<std::string>& seen) const; 00328 00329 virtual std::string toString() const; 00330 00331 //! returns the length of the longest key, subject to an optional regular expression and max depth 00332 virtual unsigned int getLongestKeyLen(const regex_t* reg=NULL, unsigned int depth=-1) const; 00333 00334 //! returns true if the specified object will be deallocated when removed from the dictionary 00335 bool ownsReference(ObjectBase * val) const { return myRef.find(val)==myRef.end(); } 00336 00337 protected: 00338 //! constructor 00339 explicit DictionaryBase(bool growable) : Collection(growable?UNION:FIXED,SYNC), dict(), myRef(), comments() { setLoadSavePolicy(growable?UNION:FIXED,SYNC); } 00340 //! copy constructor (don't assign listeners) 00341 DictionaryBase(const DictionaryBase& d) : Collection(d), dict(d.dict), myRef(d.myRef), comments(d.comments) { cloneMyRef(); setLoadSavePolicy(d.getLoadPolicy(),d.getSavePolicy()); } 00342 //! assignment (don't assign listeners) 00343 DictionaryBase& operator=(const DictionaryBase& d) { Collection::operator=(d); return *this; } 00344 00345 //! destructor 00346 ~DictionaryBase() { clear(); } 00347 00348 //! indicates that the storage implementation should mark this as an externally supplied heap reference, which needs to be deleted on removal/destruction 00349 virtual void takeObject(const std::string& name, ObjectBase* obj); 00350 00351 virtual void fireEntryRemoved(ObjectBase& val); 00352 00353 //! returns an entry matching just the prefix 00354 /*! @param[in] name the name being looked up 00355 * @param[out] seppos the position of the separator (sub-collections are separated by '.') 00356 * @return iterator of the sub entry*/ 00357 iterator getSubEntry(const std::string& name, std::string::size_type& seppos); 00358 //! returns an entry matching just the prefix 00359 /*! @param[in] name the name being looked up 00360 * @param[out] seppos the position of the separator (sub-collections are separated by '.') 00361 * @return iterator of the sub entry*/ 00362 const_iterator getSubEntry(const std::string& name, std::string::size_type& seppos) const; 00363 00364 //! called after an assignment or copy to clone the objects in #myRef to perform a deep copy 00365 virtual void cloneMyRef(); 00366 00367 //! called with each node being loaded so subclass can handle appropriately 00368 virtual bool loadXMLNode(const std::string& key, xmlNode* val, const std::string& comment)=0; 00369 00370 //! called with each node being saved so subclass can handle appropriately, return true if successful and reset key if changed 00371 virtual bool saveOverXMLNode(xmlNode* k, xmlNode* val, const std::string& key, std::string comment, const std::string& indentStr, std::set<std::string>& seen) const; 00372 00373 //! called with each node being saved so subclass can handle appropriately, return true if successful and set key 00374 virtual void saveXMLNode(xmlNode* node, const std::string& key, const ObjectBase* val, const std::string& indentStr) const; 00375 00376 //! storage of entries -- mapping from keys to value pointers 00377 storage_t dict; 00378 00379 //! objects which have been handed over to the collection for eventual de-allocation 00380 std::set<ObjectBase*> myRef; 00381 00382 //! shorthand for the type of #comments 00383 typedef std::map<std::string,std::string> comments_t; 00384 //! storage of entry comments -- mapping from keys to help text comments for manual editing or user feedback 00385 /*! not every key necessarily has a comment! */ 00386 comments_t comments; 00387 }; 00388 00389 //! A dictionary which requires all elements to be subtypes of the PO template argument 00390 /*! You can add or set entries by a quite a few variations on addEntry(), setEntry(), and addValue (via the Alloc conversion policy) 00391 * Basically these boil down to either: 00392 * - pass a pointer to an ObjectBase or directly pass a primitive value (int, float, char, etc.), 00393 * in which case the dictionary will assume management of the corresponding ObjectBase 00394 * instance (freeing the memory region when removed) 00395 * - pass a reference to an ObjectBase, in which case you retain control over the object's 00396 * allocation 00397 * 00398 * You have probably noticed this is a templated class -- you can provide any of the ObjectBase 00399 * subclasses to restrict the storage to that particular type, which will make life easier when 00400 * retrieving objects since their type will be known. 00401 * 00402 * However, if you @e want an dictionary of mixed types, you can pass ObjectBase itself for the 00403 * template parameter, and you can then insert any combination of the plist types into the 00404 * same dictionary. For convenience, a plist::Dictionary typedef is provided which does exactly this. 00405 * 00406 * So plist::Dictionary can handle any mixture of types, whereas plist::DictionaryOf<PO> will @e only 00407 * accept the plist objects of type PO (or their subclasses). The Alloc template argument 00408 * allows you to define how new string values will be handled from DictionaryBase. 00409 * 00410 * The optional conversion policy template specifies a base class for the dictionary which 00411 * can control how the dictionary will handle conversions from non-PO-based types. 00412 */ 00413 template<typename PO, typename Alloc=typename PO::template conversion_policy<DictionaryBase,PO>::value_conversion > 00414 class DictionaryOf : public DictionaryBase, public Alloc { 00415 /// @cond INTERNAL 00416 typedef typename storage_t::const_iterator::iterator_category const_iterator_category; 00417 typedef typename std::pair<storage_t::key_type,PO*> const_iterator_value; 00418 typedef typename storage_t::const_iterator::difference_type const_iterator_difference; 00419 /// @endcond 00420 public: 00421 //! shorthand for the type of the storage 00422 typedef typename DictionaryBase::storage_t storage_t; 00423 00424 /// @cond INTERNAL 00425 00426 //! iterator implementation which wraps storage_t::const_iterator to transparently dynamic_cast to the PO for client 00427 class const_iterator : std::iterator<const_iterator_category, const_iterator_value, const_iterator_difference> { 00428 public: 00429 const_iterator(const storage_t::const_iterator& sit) : it(sit), tmp() {} 00430 const const_iterator_value& operator*() const { return std::make_pair(it->first,dynamic_cast<PO*>(it->second)); } 00431 const const_iterator_value* operator->() const { tmp=std::make_pair(it->first,dynamic_cast<PO*>(it->second)); return &tmp; } 00432 const_iterator& operator++() { ++it; return *this; } 00433 const_iterator operator++(int) { return const_iterator(it++); } 00434 |