Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

plistCollections.h

Go 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 
00011 namespace plist {
00012   ObjectBase* loadXML(xmlNode* node); // defined in plist.h, we need the prototype
00013   
00014   //! allocates a new T instance, unless T is a known abstract type (ObjectBase, PrimitiveBase, Collection), in which case will throw a bad_cast via template specialization
00015   template<class T> T* allocate() { return new T; }
00016   //! assigns one T to another using operator=, unless T is ObjectBase, in which case set() is used via template specialization
00017   template<class T> void assign(T& a, const T& b) { a=b; }
00018   
00019   //! @cond INTERNAL
00020   template<> inline ObjectBase* allocate() { throw std::runtime_error("cannot plist::allocate generic instances (ObjectBase)"); }
00021   template<> inline PrimitiveBase* allocate() { throw std::runtime_error("cannot plist::allocate generic instances (PrimitiveBase)"); }
00022   template<> inline Collection* allocate() { throw std::runtime_error("cannot plist::allocate generic instances (Collection)"); }
00023   template<> inline void assign(ObjectBase& a, const ObjectBase& b) { a.set(b); }
00024   //! @endcond
00025   
00026   //! attempts to load a new T instance from the specified xmlNode
00027   template<class T> T* loadXML(xmlNode* node) {
00028     T* cobj = plist::allocate<T>();
00029     try {
00030       cobj->loadXML(node);
00031     } catch(...) {
00032       delete cobj;
00033       throw;
00034     }
00035     return cobj;
00036   }
00037 
00038   //! @cond INTERNAL
00039   template<> ObjectBase* loadXML(xmlNode* node);
00040   template<> PrimitiveBase* loadXML(xmlNode* node);
00041   template<> Collection* loadXML(xmlNode* node);
00042   //! @endcond
00043   
00044   //! Provides a common base class for the collection-oriented primitives, Dictionary and Array
00045   /*! 
00046    *  When a collection, you can call addEntry() or setEntry() you can either:
00047    *    - pass a pointer to an ObjectBase or directly pass a primitive value (int, float, char, etc.),
00048    *      in which case the Array will assume management of the corresponding ObjectBase
00049    *      instance (freeing the memory region when removed)
00050    *    - pass a reference to an ObjectBase, in which case you retain control over the object's
00051    *      allocation
00052    *
00053    *  This class supports callbacks upon modification through the use of the
00054    *  CollectionListener interface.  Note that we only store a pointer to the
00055    *  listener list, which is typically unallocated when no listeners are
00056    *  active.  This should ensure minimal memory usage per object, as well as
00057    *  support safe storage of plist objects in inter-process shared memory
00058    *  regions.
00059    *
00060    *  If you are using these in a shared memory region, just be sure that only
00061    *  the process with listeners does any and all modifications, and that it
00062    *  unsubscribes before detaching from the region (or else destroys the region
00063    *  itself)
00064    *
00065    *  There isn't a callback if entries themselves are modified, only when new
00066    *  entries are added, or old ones removed.  If you want to know any time any
00067    *  aspect of any entry is modified, listen for the add and remove callbacks,
00068    *  and then add yourself as a listener to each entry individually.  */
00069   class Collection : public ObjectBase {
00070   public:
00071     //! Specifies that a collection of collections cannot contain any primitive values
00072     template<typename U, typename V> struct conversion_policy { typedef typename U::DeniedValueConversions value_conversion; };
00073     
00074     //! used to define values for LoadSavePolicy values so we can test a bit out of the policy value
00075     enum LoadSaveActionBitMask {
00076       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
00077       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
00078     };
00079     
00080     //! Arguments for setLoadPolicy() and setSavePolicy(), specifies how to handle orphaned entries when loading or saving
00081     /*! <table>
00082      *  <tr><th>@b Loading</th><th>SYNC</th><th>INTERSECT</th><th>UNION</th><th>FIXED</th></tr>
00083      *  <tr><td>entry not in file <br> (missing from source)</td><td>remove from<br>collection</td><td>remove from<br>collection</td><td>ignore</td><td>ignore</td></tr>
00084      *  <tr><td>entry not in collection <br> (missing from destination)</td><td>add to<br>collection</td><td>ignore</td><td>add to<br>collection</td><td>ignore</td></tr>
00085      *  </table> <br>
00086      *  
00087      *  <table>
00088      *  <tr><th>@b Saving</th><th>SYNC</th><th>INTERSECT</th><th>UNION</th><th>FIXED</th></tr>
00089      *  <tr><td>entry not in file <br> (missing from destination)</td><td>add to<br>file</td><td>ignore</td><td>add to<br>file</td><td>ignore</td></tr>
00090      *  <tr><td>entry not in collection <br> (missing from source)</td><td>remove<br>from file</td><td>remove<br>from file</td><td>ignore</td><td>ignore</td></tr>
00091      *  </table>
00092      *  
00093      *  Commonly, you'll want to use SYNC (the default) to support dynamically-resized storage, and FIXED load/SYNC save
00094      *  for configuration settings (or perhaps FIXED load and UNION save to keep 'extra' values in the file...)*/
00095     enum LoadSavePolicy {
00096       FIXED = 0, //!< destination will have the same entries as before, ignores orphans and only updates entries with matching keys
00097       UNION = ADDITIONS, //!< destination will have its current entries, as well as new ones from the source
00098       INTERSECT = REMOVALS, //!< destination will only hold entries which are in both locations, removes entries not found in source, ignores new entries
00099       SYNC = ADDITIONS|REMOVALS //!< destination will mirror source, new entries are added, missing entries are removed
00100     };
00101 
00102     //! assignment (don't assign listeners); subclass should call fireEntriesChanged after calling this (and updating its own storage)
00103     Collection& operator=(const Collection& d) { if(&d==this) return *this; ObjectBase::operator=(d); return *this; }
00104     
00105     //! destructor
00106     ~Collection();
00107     
00108     //! recursively resolves @a path interpreted as a series of collection entry names separated by '.', returns NULL if it doesn't exist
00109     virtual ObjectBase* resolveEntry(const std::string& path) const=0;
00110     //! Processes a command of the form 'collection.key = value' or 'collection.array += entry'
00111     /*! Returns whether the assignment was successful, but does not provide error message (see other version if you want a string to give user) */
00112     bool resolveAssignment(const std::string& arg);
00113     //! Processes a command of the form 'collection.key = value' or 'collection.array += entry'
00114     /*! Returns whether the assignment was successful, and if an error occurrs, places the error message in errstream */
00115     bool resolveAssignment(const std::string& arg, std::ostream& errstream);
00116     //! remove all entries in one fell swoop
00117     virtual void clear()=0;
00118     //! return the size of the collection
00119     virtual size_t size() const=0;
00120     
00121     //! get notified of changes; be sure to call removeCollectionListener() before destructing @a l!
00122     virtual void addCollectionListener(CollectionListener* l) const;
00123     //! no longer take notification of changes to this object's value
00124     virtual void removeCollectionListener(CollectionListener* l) const;
00125     //! test if @a l is currently registered as a listener
00126     virtual bool isCollectionListener(CollectionListener* l) const;
00127     
00128     void setSaveCondensed(bool b) { saveCondensed=b; } //!< set #saveCondensed (not automatically recursive)
00129     bool getSaveCondensed() const { return saveCondensed; } //!< returns #saveCondensed
00130     
00131     void setUnusedWarning(bool b) { warnUnused=b; } //!< set #warnUnused
00132     bool getUnusedWarning() const { return warnUnused; } //!< returns #warnUnused
00133 
00134     virtual LoadSavePolicy getLoadPolicy() const { return loadPolicy; } //!< returns #loadPolicy
00135     virtual LoadSavePolicy getSavePolicy() const { return savePolicy; } //!< returns #savePolicy
00136     virtual void setLoadPolicy(LoadSavePolicy lp) { loadPolicy=lp; } //!< assigns #loadPolicy
00137     virtual void setSavePolicy(LoadSavePolicy sp) { savePolicy=sp; } //!< assigns #savePolicy
00138     virtual void setLoadSavePolicy(LoadSavePolicy lp, LoadSavePolicy sp) { setLoadPolicy(lp); setSavePolicy(sp); } //!< assigns #loadPolicy and #savePolicy respectively
00139     
00140     //! defines separator between sub-collections
00141     /*!  (defined as a function instead of just a constant member so there's no issues with initialization order) */
00142     static const std::string& subCollectionSep() {
00143       static std::string sep(1,'.');
00144       return sep;
00145     }
00146     
00147     //! returns longest key length which matches the regular expression
00148     virtual unsigned int getLongestKeyLen(const regex_t* reg=NULL, unsigned int depth=-1) const=0;
00149     
00150     //! returns true if the Collection subclass allows storage of the argument
00151     virtual bool canContain(const ObjectBase& obj)=0;
00152     
00153     virtual long toLong() const;
00154     virtual double toDouble() const;
00155     
00156   protected:
00157     //! constructor
00158     Collection() : ObjectBase(), collectionListeners(), saveCondensed(false), warnUnused(true), loadPolicy(SYNC), savePolicy(SYNC) {autoFormat=false;}
00159     //! constructor
00160     Collection(LoadSavePolicy lp, LoadSavePolicy sp) : ObjectBase(), collectionListeners(), saveCondensed(false), warnUnused(true), loadPolicy(lp), savePolicy(sp) {autoFormat=false;}
00161     //! copy constructor (don't assign listeners)
00162     Collection(const Collection& d) : ObjectBase(d), collectionListeners(), saveCondensed(d.saveCondensed), warnUnused(d.warnUnused), loadPolicy(d.loadPolicy), savePolicy(d.savePolicy) {autoFormat=false;}
00163     
00164     //! run through #collectionListeners, calling CollectionListener::plistCollectionEntryAdded(*this,val)
00165     virtual void fireEntryAdded(ObjectBase& val);
00166     //! run through #collectionListeners, calling CollectionListener::plistCollectionEntryRemoved(*this,val)
00167     virtual void fireEntryRemoved(ObjectBase& val);
00168     //! run through #collectionListeners, calling CollectionListener::plistCollectionEntriesChanged(*this)
00169     virtual void fireEntriesChanged();
00170     //! stores a list of the current listeners
00171     mutable std::set<CollectionListener*>* collectionListeners;
00172     
00173     //! returns index corresponding to @a name, which should encode an integer value less than or equal to the current size
00174     static size_t getIndex(const std::string& name);
00175     
00176     //! returns a prefix for items within the collection
00177     static std::string getIndentationPrefix(xmlNode* node);
00178     
00179     //! when an empty string is needed for not found items
00180     /*!  (defined as a function instead of just a constant member so there's no issues with initialization order) */
00181     static const std::string& emptyStr() {
00182       static std::string mt;
00183       return mt;
00184     }
00185     //! how much to indent each sub-collection
00186     /*!  (defined as a function instead of just a constant member so there's no issues with initialization order) */
00187     static const std::string& perIndent() {
00188       static std::string pi(1,'\t');
00189       return pi;
00190     }
00191     //! 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
00192     static const unsigned int KEY_IN_COMMENT_MAX_POS=10;
00193     
00194     //! if true, will skip putting "headline" comments before sub-collections
00195     bool saveCondensed;
00196     //! 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
00197     bool warnUnused;    
00198     //! specifies how to handle "orphaned" entries while loading
00199     LoadSavePolicy loadPolicy;
00200     //! specifies how to handle "orphaned" entries while saving
00201     LoadSavePolicy savePolicy;
00202   };
00203   
00204   
00205   //! Monitors a collection to keep tabs on all its entries, so PrimitiveListener::plistValueChanged will be called if any entry is changed
00206   struct AutoCollectionListener : public plist::PrimitiveListener, public plist::CollectionListener {
00207     //! constructor, specify collection to monitor and whether you want plistValueChanged to be called when new entries are added
00208     AutoCollectionListener(const plist::Collection& source, bool updateOnNewEntry)
00209     : plist::PrimitiveListener(), src(source), updateOnNew(updateOnNewEntry)
00210     {
00211       activate();
00212     }
00213     //! destructor, unsubscribe from everything
00214     ~AutoCollectionListener() {
00215       deactivate();
00216     }
00217     //! assignment (no-op)
00218     AutoCollectionListener& operator=(const AutoCollectionListener&) { return *this; }
00219     
00220     virtual void plistCollectionEntryAdded(plist::Collection& col, ObjectBase& primitive);
00221     virtual void plistCollectionEntryRemoved(plist::Collection& col, ObjectBase& primitive);
00222     virtual void plistCollectionEntriesChanged(plist::Collection& col);
00223     virtual void plistSubCollectionAdded(plist::Collection& /*col*/) {} //!< called if any sub collections are added for potential recursive handling
00224     virtual void plistSubCollectionRemoved(plist::Collection& /*col*/) {} //!< called if any sub collections are removed for potential recursive handling
00225     
00226     //! add listeners
00227     virtual void activate(bool callNow=true) {
00228       src.addCollectionListener(this);
00229       if(callNow)
00230         plistCollectionEntriesChanged(const_cast<plist::Collection&>(src));
00231     }
00232     //! remove listeners
00233     virtual void deactivate();
00234     
00235     const plist::Collection& src; //!< the collection being monitored
00236     bool updateOnNew; //!< whether to call plistValueChanged whenever new primitives are added to the collection
00237   private:
00238     //! copy is a bad idea: as a member of a collection, don't want to listen to original collection's values
00239     AutoCollectionListener(const AutoCollectionListener&);
00240   };
00241   //! Produces a callback on a member function when a value in the collection changes
00242   template<typename O>
00243   struct CollectionCallbackMember : public plist::AutoCollectionListener {
00244     //! constructor
00245     CollectionCallbackMember(const plist::Collection& source, O& component, void(O::*func)(), bool callOnAdd=true)
00246     : plist::AutoCollectionListener(source,callOnAdd), comp(component), callback(func) { if(updateOnNew) (comp.*callback)(); }
00247     virtual void plistValueChanged(const plist::PrimitiveBase& /*pl*/) { (comp.*callback)(); }
00248     virtual void activate(bool callNow=true) { if(updateOnNew && callNow) (comp.*callback)(); }
00249     O& comp; //!< instance to call on
00250     void(O::*callback)(); //!< function to call
00251     
00252     CollectionCallbackMember& operator=(const CollectionCallbackMember&) { return *this; } //!< assignment is a no-op
00253   private:
00254     CollectionCallbackMember(CollectionCallbackMember&);
00255   };
00256   
00257   
00258   
00259   
00260   //! Maintains a set of (key,value) pairs, see DictionaryOf, and the Dictionary typedef
00261   /*! You can add or set entries by a quite a few variations on addEntry(), setEntry(), or addValue().
00262    *  Basically these boil down to either:
00263    *    - pass a pointer to an ObjectBase or directly pass a primitive value (int, float, char, etc.),
00264    *      in which case the dictionary will assume management of the corresponding ObjectBase
00265    *      instance (freeing the memory region when removed)
00266    *    - pass a reference to an ObjectBase, in which case you retain control over the object's
00267    *      allocation
00268    */
00269   class DictionaryBase : virtual public Collection {
00270     friend std::ostream& operator<<(std::ostream& os, const DictionaryBase& d);
00271   public:
00272     //! shorthand for the type of the storage
00273     typedef std::map<std::string,ObjectBase*> storage_t;
00274     //! shorthand for iterators to be returned
00275     typedef storage_t::iterator iterator;
00276     //! shorthand for iterators to be returned
00277     typedef storage_t::const_iterator const_iterator;
00278     
00279     //! Indicates that no value conversions are allowed
00280     /*! The actual storage type is still allowed, so technically we could use EntryConstraint<PO>
00281      *  instead as the conversion policy, but that doesn't gain anything because you would need
00282      *  to know the PO to test for it.  At least with this you can test for DeniedValueConversions as a base
00283      *  class and then fall back to testing individual PO's if you want. */
00284     struct DeniedValueConversions {
00285       virtual ~DeniedValueConversions() {} //!< no-op destructor
00286     };
00287     
00288     //! 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
00289     /*! This class is useless to end users: to use it they would have to know the template type being used,
00290      *  which if they knew, they could just dynamic_cast to the DictionaryOf type directly.  The point of this
00291      *  class is to provide the abstract functions to the its subclasses, which use them to implement their
00292      *  various conversions. */
00293     template<typename PO>
00294     struct EntryConstraint {
00295       virtual ~EntryConstraint() {} //!< no-op destructor
00296       //! 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)
00297       virtual void setEntry(const std::string& name, PO& val, bool warnExists=false)=0;
00298       //! 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)
00299       virtual void addEntry(const std::string& name, PO& val, const std::string& comment="", bool warnExists=true)=0;
00300       //! 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
00301       virtual void setEntry(const std::string& name, PO* val, bool warnExists=false)=0;
00302       //! 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
00303       virtual void addEntry(const std::string& name, PO* val, const std::string& comment="", bool warnExists=true)=0;
00304     };
00305     
00306     //! 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)
00307     /*! The point of this class is to handle the situation where you have a DictionaryBase and user input to append, and
00308      *  you don't want to have to test every combination of template parameters to DictionaryOf to find out if you can
00309      *  add the data.  Instead, test dynamic_cast<plist::DictionaryBase::StringConversion>, and if it is successful, you
00310      *  can pass the string without having to know the actual value type of the dictionary. */
00311     struct StringConversion {
00312       virtual ~StringConversion() {} //!< no-op destructor
00313       //! generic addition of value, control of (de)allocation of corresponding Primitive instance will be assumed by the dictionary
00314       virtual void addValue(const std::string& name, const std::string& val, const std::string& comment="", bool warnExists=true)=0;
00315     };
00316     
00317     //! 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)
00318     /*! The point of this class is to handle the situation where you have a DictionaryBase and user input to append, and
00319      *  you don't want to have to test every combination of template parameters to DictionaryOf to find out if you can
00320      *  add the data.  Instead, test dynamic_cast<plist::DictionaryBase::IntegerConversion>, and if it is successful, you
00321      *  can pass the data without having to know the actual value type of the dictionary. */
00322     struct IntegerConversion {
00323       virtual ~IntegerConversion() {} //!< no-op destructor
00324       //! generic addition of value, control of (de)allocation of corresponding Primitive instance will be assumed by the dictionary
00325       virtual void addValue(const std::string& name, long val, const std::string& comment="", bool warnExists=true)=0;
00326       //! generic addition of value, control of (de)allocation of corresponding Primitive instance will be assumed by the dictionary
00327       virtual void addValue(const std::string& name, unsigned long val, const std::string& comment="", bool warnExists=true)=0;
00328     };
00329     
00330     //! 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)
00331     /*! The point of this class is to handle the situation where you have a DictionaryBase and user input to append, and
00332      *  you don't want to have to test every combination of template parameters to DictionaryOf to find out if you can
00333      *  add the data.  Instead, test dynamic_cast<plist::DictionaryBase::RealConversion>, and if it is successful, you
00334      *  can pass the data without having to know the actual value type of the dictionary. */
00335     struct RealConversion {
00336       virtual ~RealConversion() {} //!< no-op destructor
00337       //! generic addition of value, control of (de)allocation of corresponding Primitive instance will be assumed by the dictionary
00338       virtual void addValue(const std::string& name, double val, const std::string& comment="", bool warnExists=true)=0;
00339     };
00340     
00341     //! 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.
00342     /*! Use of this conversion policy requires that the template parameter is not abstract,
00343      *  as the policy will be trying to create new instances directly from the specified type. */
00344     template<typename PO>
00345     struct ConversionTo : public StringConversion, public IntegerConversion, public EntryConstraint<PO> {
00346       //! 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
00347       template<typename T>
00348       void addValue(const std::string& name, const T& val, const std::string& comment="", bool warnExists=true) { addEntry(name,new PO(val),comment,warnExists); }
00349       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; } this->addEntry(name,po,comment,warnExists); }
00350       //! "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
00351       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; } this->addEntry(name,po,comment,warnExists); }
00352       //! "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
00353       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; } this->addEntry(name,po,comment,warnExists); }
00354       virtual void addValue(const std::string& name, long val, const std::string& comment="", bool warnExists=true) { this->addEntry(name,new PO(val),comment,warnExists); }
00355       virtual void addValue(const std::string& name, unsigned long val, const std::string& comment="", bool warnExists=true) { this->addEntry(name,new PO(val),comment,warnExists); }
00356     };
00357     
00358     //! 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
00359     /*! Use addEntry() to add ObjectBase subclasses -- addValue is for POD types */
00360     template<typename PO>
00361     struct WrapValueConversion : public StringConversion, public IntegerConversion, public RealConversion, public EntryConstraint<PO> {
00362       //! 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
00363       template<typename T>
00364       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); }
00365       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); }
00366       //! "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
00367       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); }
00368       //! "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
00369       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); }
00370       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); }
00371       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); }
00372       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); }
00373     };
00374     
00375     //! remove the entry with key @a name, returns true if something was actually removed (if false, wasn't there to begin with)
00376     virtual bool removeEntry(const std::string& name);
00377     
00378     //! 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)
00379     /*! If the collection owns the reference to the object, you have to use this function instead
00380      *  of a pair of calls to removeEntry/addEntry, otherwise you will wind up with an invalid pointer! */
00381     virtual bool renameEntry(const std::string& oldname, const std::string& newname);
00382     //! exchange the values for a pair of keys -- if either key doesn't exist, forwards call to renameEntry()
00383     /*! returns true if the swap was successful, only returns false if both keys are invalid */
00384     virtual bool swapEntry(const std::string& a, const std::string& b);
00385     
00386     //! returns a reference to the entry with the specified name, creating it if it doesn't exist
00387     virtual ObjectBase& getEntry(const std::string& name)=0;
00388     //! returns a reference to the entry with the specified name, creating it if it doesn't exist
00389     virtual ObjectBase& operator[](const std::string& name)=0;
00390     //! returns a pointer to entry with the specified 'path', which may recurse through sub-collections
00391     virtual ObjectBase* resolveEntry(const std::string& path) const;
00392         
00393     //! returns an iterator to an entry in the current dictionary
00394     const_iterator findEntry(const std::string& name) const { return dict.find(name); }
00395     
00396     virtual void clear();
00397     
00398     //! return an STL const_iterator to the first entry
00399     const_iterator begin() const { return dict.begin(); }
00400     //! return the one-past-end const_iterator
00401     const_iterator end() const { return dict.end(); }
00402     //! return the size of the dictionary
00403     virtual size_t size() const { return dict.size(); }
00404     
00405     //! replaces previous comment for @a name, or adds it if it doesn't already exist (can preceed actual entry!)
00406     void setComment(const std::string& name, const std::string& comment);
00407     //! returns comment retrieved from loaded file, or any subsequent call to setComment
00408     const std::string& getComment(const std::string& name) const;
00409     
00410     virtual void loadXML(xmlNode* node);
00411     virtual void saveXML(xmlNode* node) const { std::set<std::string> seen; saveXML(node,!(savePolicy&ADDITIONS),seen); }
00412     //! saves the dictionary into the specified node
00413     /*! @param[in] node the xml node which should be saved into
00414      *  @param[in] onlyOverwrite if is true, only saves entries for keys already found in the node (this overrides the current savePolicy value)
00415      *  @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
00416      *
00417      *  @a seen is not cleared before being used.*/
00418     virtual void saveXML(xmlNode* node, bool onlyOverwrite, std::set<std::string>& seen) const;
00419     
00420     virtual std::string toString() const;
00421     
00422     //! returns the length of the longest key, subject to an optional regular expression and max depth
00423     virtual unsigned int getLongestKeyLen(const regex_t* reg=NULL, unsigned int depth=-1) const;
00424     
00425     //! returns true if the specified object will be deallocated when removed from the dictionary
00426     bool ownsReference(ObjectBase * val) const { return myRef.find(val)==myRef.end(); }
00427     
00428   protected:
00429     //! constructor
00430     explicit DictionaryBase(bool growable) : Collection(growable?UNION:FIXED,SYNC), dict(), myRef(), comments() { setLoadSavePolicy(growable?UNION:FIXED,SYNC); }
00431     //! copy constructor (don't assign listeners)
00432     DictionaryBase(const DictionaryBase& d) : Collection(d), dict(d.dict), myRef(d.myRef), comments(d.comments) { cloneMyRef(); setLoadSavePolicy(d.getLoadPolicy(),d.getSavePolicy()); }
00433     //! assignment (don't assign listeners)
00434     DictionaryBase& operator=(const DictionaryBase& d) { Collection::operator=(d); return *this; }
00435     
00436     //! destructor
00437     ~DictionaryBase() { clear(); }
00438     
00439     //! indicates that the storage implementation should mark this as an externally supplied heap reference, which needs to be deleted on removal/destruction
00440     virtual void takeObject(const std::string& name, ObjectBase* obj);
00441     
00442     virtual void fireEntryRemoved(ObjectBase& val);
00443     
00444     //! returns an entry matching just the prefix
00445     /*! @param[in] name the name being looked up
00446      *  @param[out] seppos the position of the separator (sub-collections are separated by '.')
00447      *  @return iterator of the sub entry*/
00448     iterator getSubEntry(const std::string& name, std::string::size_type& seppos);
00449     //! returns an entry matching just the prefix
00450     /*! @param[in] name the name being looked up
00451      *  @param[out] seppos the position of the separator (sub-collections are separated by '.')
00452      *  @return iterator of the sub entry*/
00453     const_iterator getSubEntry(const std::string& name, std::string::size_type& seppos) const;
00454     
00455     //! called after an assignment or copy to clone the objects in #myRef to perform a deep copy
00456     virtual void cloneMyRef();
00457     
00458     //! called with each node being loaded so subclass can handle appropriately
00459     virtual bool loadXMLNode(const std::string& key, xmlNode* val, const std::string& comment)=0;
00460     
00461     //! called with each node being saved so subclass can handle appropriately, return true if successful and reset key if changed
00462     virtual bool saveOverXMLNode(xmlNode* k, xmlNode* val, const std::string& key, std::string comment, const std::string& indentStr, std::set<std::string>& seen) const;
00463     
00464     //! called with each node being saved so subclass can handle appropriately, return true if successful and set key
00465     virtual void saveXMLNode(xmlNode* node, const std::string& key, const ObjectBase* val, const std::string& indentStr, size_t longestKeyLen) const;
00466     
00467     //! storage of entries -- mapping from keys to value pointers
00468     storage_t dict;
00469     
00470     //! objects which have been handed over to the collection for eventual de-allocation
00471     std::set<ObjectBase*> myRef;
00472 
00473     //! shorthand for the type of #comments
00474     typedef std::map<std::string,std::string> comments_t;
00475     //! storage of entry comments -- mapping from keys to help text comments for manual editing or user feedback
00476     /*! not every key necessarily has a comment! */
00477     comments_t comments;
00478   };
00479 
00480   //! This is a specialization for float conversions to add RealConversions
00481   template<>
00482   struct DictionaryBase::ConversionTo<plist::Primitive<float> > : public DictionaryBase::StringConversion, public IntegerConversion, public DictionaryBase::RealConversion, public DictionaryBase::EntryConstraint<plist::Primitive<float> > {
00483     //! 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
00484     template<typename T>
00485     void addValue(const std::string& name, const T& val, const std::string& comment="", bool warnExists=true) { addEntry(name,new plist::Primitive<float>(val),comment,warnExists); }
00486     virtual void addValue(const std::string& name, const std::string& val, const std::string& comment="", bool warnExists=true) { plist::Primitive<float>* po=new plist::Primitive<float>; try { po->set(val); } catch(...) { delete po; throw; } addEntry(name,po,comment,warnExists); }
00487     //! "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
00488     virtual void addValue(const std::string& name, char val[], const std::string& comment="", bool warnExists=true) { plist::Primitive<float> * po=new plist::Primitive<float>; try { po->set(val); } catch(...) { delete po; throw; } addEntry(name,po,comment,warnExists); }
00489     //! "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
00490     virtual void addValue(const std::string& name, const char val[], const std::string& comment="", bool warnExists=true) { plist::Primitive<float> * po=new plist::Primitive<float>; try { po->set(val); } catch(...) { delete po; throw; } addEntry(name,po,comment,warnExists); }
00491     virtual void addValue(const std::string& name, long val, const std::string& comment="", bool warnExists=true) { addEntry(name,new plist::Primitive<float>(val),comment,warnExists); }
00492     virtual void addValue(const std::string& name, unsigned long val, const std::string& comment="", bool warnExists=true) { addEntry(name,new plist::Primitive<float>(val),comment,warnExists); }
00493     virtual void addValue(const std::string& name, double val, const std::string& comment="", bool warnExists=true) { addEntry(name,new plist::Primitive<float>((float)val),comment,warnExists); }
00494   };
00495 
00496   //! This is a specialization for double conversions to add RealConversions
00497   template<>
00498   struct DictionaryBase::ConversionTo<plist::Primitive<double> > : public DictionaryBase::StringConversion, public IntegerConversion, public DictionaryBase::RealConversion, public DictionaryBase::EntryConstraint<plist::Primitive<double> > {
00499     //! 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
00500     template<typename T>
00501     void addValue(const std::string& name, const T& val, const std::string& comment="", bool warnExists=true) { addEntry(name,new plist::Primitive<double>(val),comment,warnExists); }
00502     virtual void addValue(const std::string& name, const std::string& val, const std::string& comment="", bool warnExists=true) { plist::Primitive<double>* po=new plist::Primitive<double>; try { po->set(val); } catch(...) { delete po; throw; } addEntry(name,po,comment,warnExists); }
00503     //! "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
00504     virtual void addValue(const std::string& name, char val[], const std::string& comment="", bool warnExists=true) { plist::Primitive<double> * po=new plist::Primitive<double>; try { po->set(val); } catch(...) { delete po; throw; } addEntry(name,po,comment,warnExists); }
00505     //! "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
00506     virtual void addValue(const std::string& name, const char val[], const std::string& comment="", bool warnExists=true) { plist::Primitive<double> * po=new plist::Primitive<double>; try { po->set(val); } catch(...) { delete po; throw; } addEntry(name,po,comment,warnExists); }
00507     virtual void addValue(const std::string& name, long val, const std::string& comment="", bool warnExists=true) { addEntry(name,new plist::Primitive<double>(val),comment,warnExists); }
00508     virtual void addValue(const std::string& name, unsigned long val, const std::string& comment="", bool warnExists=true) { addEntry(name,new plist::Primitive<double>(val),comment,warnExists); }
00509     virtual void addValue(const std::string& name, double val, const std::string& comment="", bool warnExists=true) { addEntry(name,new plist::Primitive<double>(val),comment,warnExists); }
00510   };
00511 
00512   
00513   //! A dictionary which requires all elements to be subtypes of the PO template argument
00514   /*! You can add or set entries by a quite a few variations on addEntry(), setEntry(), and addValue (via the Alloc conversion policy)
00515    *  Basically these boil down to either:
00516    *    - pass a pointer to an ObjectBase or directly pass a primitive value (int, float, char, etc.),
00517    *      in which case the dictionary will assume management of the corresponding ObjectBase
00518    *      instance (freeing the memory region when removed)
00519    *    - pass a reference to an ObjectBase, in which case you retain control over the object's
00520    *      allocation
00521    *  
00522    *  You have probably noticed this is a templated class -- you can provide any of the ObjectBase
00523    *  subclasses to restrict the storage to that particular type, which will make life easier when
00524    *  retrieving objects since their type will be known.
00525    *
00526    *  However, if you @e want an dictionary of mixed types, you can pass ObjectBase itself for the
00527    *  template parameter, and you can then insert any combination of the plist types into the
00528    *  same dictionary.  For convenience, a plist::Dictionary typedef is provided which does exactly this.
00529    *  
00530    *  So plist::Dictionary can handle any mixture of types, whereas plist::DictionaryOf<PO> will @e only
00531    *  accept the plist objects of type PO (or their subclasses).  The Alloc template argument
00532    *  allows you to define how new string values will be handled from DictionaryBase.
00533    *
00534    *  The optional conversion policy template specifies a base class for the dictionary which
00535    *  can control how the dictionary will handle conversions from non-PO-based types.
00536    */
00537   template<typename PO, typename Alloc=typename PO::template conversion_policy<DictionaryBase,PO>::value_conversion >
00538   class DictionaryOf : public DictionaryBase, public Alloc {
00539     /// @cond INTERNAL
00540     typedef typename storage_t::const_iterator::iterator_category const_iterator_category;
00541     typedef typename std::pair<storage_t::key_type,PO*> const_iterator_value;
00542     typedef typename storage_t::const_iterator::difference_type const_iterator_difference;
00543     /// @endcond
00544   public:
00545     //! shorthand for the type of the storage
00546     typedef typename DictionaryBase::storage_t storage_t;
00547     
00548     /// @cond INTERNAL
00549     
00550     //! iterator implementation which wraps storage_t::const_iterator to transparently dynamic_cast to the PO for client
00551     class const_iterator : public std::iterator<const_iterator_category, const_iterator_value, const_iterator_difference> {
00552     public:
00553       const_iterator(const storage_t::const_iterator& sit) : it(sit), tmp() {}
00554       const const_iterator_value& operator*() const { tmp.first=it->first; tmp.second=dynamic_cast<PO*>(it->second); return tmp; }
00555       const const_iterator_value* operator->() const { tmp.first=it->first; tmp.second=dynamic_cast<PO*>(it->second); return &tmp; }
00556       const_iterator& operator++() { ++it; return *this; }
00557       const_iterator operator++(int) { return const_iterator(it++); }
00558       const_iterator& operator--() { --it; return *this; }
00559       const_iterator operator--(int) { return const_iterator(it--); }
00560       bool operator==(const const_iterator& rhs) const { return it==rhs.it; }
00561       bool operator!=(const const_iterator& rhs) const { return it!=rhs.it; }
00562       
00563     protected:
00564       storage_t::const_iterator it;
00565       mutable const_iterator_value tmp;
00566     };
00567     /// @endcond
00568     
00569     //! constructor
00570     DictionaryOf() : DictionaryBase(true), Alloc() {}
00571     //! constructor
00572     explicit DictionaryOf(bool growable) : DictionaryBase(growable), Alloc() {}
00573     //! copy constructor (don't assign listeners)
00574     DictionaryOf(const DictionaryOf& d) : DictionaryBase(d), Alloc(d) {}
00575     //! assignment (don't assign listeners); subclass should call fireEntriesChanged after calling this (and updating its own storage)
00576     DictionaryOf& operator=(const DictionaryOf& a);
00577 
00578     virtual void set(const ObjectBase& ob) { const DictionaryBase& d=dynamic_cast<const DictionaryBase&>(ob); set(d); }
00579     //! handles actual setting of one dictionary to another, similar to operator=(DictionaryOf), but allows for polymorphic conversion to the template type
00580     virtual void set(const DictionaryBase& a);
00581     
00582     //! destructor
00583     ~DictionaryOf() { }
00584     
00585     //! Replaces the entry with the specified key, optionally warns as it does so.  If you simply want to set the *value* of the specified entry, try getEntry() and assignment...
00586     /*! By passing a reference to the entry, you indicate you will retain responsibility for deallocation */
00587     virtual void setEntry(const std::string& name, PO& val, bool warnExists=false);
00588     //! Convenience method for adding new entries, with optional comment and warning if replacing a previous entry
00589     /*! By passing a reference to the entry, you indicate you will retain responsibility for deallocation */
00590     virtual void addEntry(const std::string& name, PO& val, const std::string& comment="", bool warnExists=true);
00591     //! Replaces the entry with the specified key, optionally warns as it does so.  If you simply want to set the *value* of the specified entry, try getEntry() and assignment...
00592     /*! By passing a pointer to the entry, you indicate you wish the dictionary to assume responsibility for deallocation */
00593     virtual void setEntry(const std::string& name, PO* val, bool warnExists=false);
00594     //! Convenience method for adding new entries, with optional comment and warning if replacing a previous entry
00595     /*! By passing a pointer to the entry, you indicate you wish the dictionary to assume responsibility for deallocation */
00596     virtual void addEntry(const std::string& name, PO* val, const std::string& comment="", bool warnExists=true);
00597     
00598     // stupid older compilers don't support covariant returns
00599 #if !defined(__GNUC__) || __GNUC__ > 3 || (__GNUC__ == 3 && (__GNUC_MINOR__ > 3))
00600     typedef PO entryRet_t; //!< older compilers don't support covariant returns, so this is the templated storage type on modern compilers, generic ObjectBase otherwise, can check defined(PLIST_NO_COVRET)
00601 #else
00602     typedef ObjectBase entryRet_t; //!< older compilers don't support covariant returns, so this is the templated storage type on modern compilers, generic ObjectBase otherwise, can check defined(PLIST_NO_COVRET)
00603 #  define PLIST_NO_COVRET
00604 #endif
00605     
00606     //! returns a reference to the entry with the specified name, creating it if it doesn't exist
00607     entryRet_t& getEntry(const std::string& name) {
00608       DictionaryBase::const_iterator it=dict.find(name);
00609       if(it!=dict.end())
00610         return dynamic_cast<PO&>(*it->second);
00611       else {
00612         PO* p = plist::allocate<PO>(); // do before dictionary access (next line) in case of exception (don't leak NULL dictionary entries!)
00613         dict[name]=p;
00614         fireEntryAdded(*p);
00615         return *p;
00616       }
00617     }
00618     //! returns a reference to the entry with the specified name, creating it if it doesn't exist
00619     entryRet_t& operator[](const std::string& name) { return getEntry(name); }
00620     //! returns an iterator the <key, value> pair with the specified key (returns end() if not found)
00621     const_iterator findEntry(const std::string& name) const { return dict.find(name); }
00622         
00623     //! return an STL const_iterator to the first entry (note implicit conversion to specialized const_iterator)
00624     const_iterator begin() const { return dict.begin(); }
00625     //! return the one-past-end const_iterator (note implicit conversion to specialized const_iterator)
00626     const_iterator end() const { return dict.end(); }
00627     
00628     virtual bool canContain(const ObjectBase& obj) { return (dynamic_cast<const PO*>(&obj)!=NULL); }
00629     
00630     //! clone implementation for Dictionary
00631     PLIST_CLONE_DEF(DictionaryOf,(new DictionaryOf<PO,Alloc>(*this)));
00632       
00633   protected:
00634     //! called with each node being loaded so subclass can handle appropriately
00635     virtual bool loadXMLNode(const std::string& name, xmlNode* val, const std::string& comment);
00636   };
00637   
00638   /*! plist::Dictionary can handle any mixture of types, whereas plist::DictionaryOf<PO> will @e only
00639    *  accept the plist objects of type PO (or their subclasses).  This typedef is simply for
00640    *  convenience and passes ObjectBase for the template parameter. */
00641   typedef DictionaryOf<ObjectBase> Dictionary;
00642   extern template class DictionaryOf<ObjectBase>;
00643   
00644   
00645   template<typename PO, typename Alloc>
00646   void DictionaryOf<PO,Alloc>::setEntry(const std::string& name, PO& val, bool warnExists/*=false*/) {
00647     DictionaryBase::iterator it=dict.find(name);
00648     if(it!=dict.end()) {
00649       //found exact name match
00650       if(&val==it->second) {
00651         if(warnExists)
00652           std::cerr << "Warning: entry ("<<name<<","<<val<<") was already added, ignoring duplication..." << std::endl;
00653         myRef.erase(&val); // reset reference
00654         return; // same val reference already registered
00655       }
00656       if(warnExists) {
00657         std::cerr << "Warning: new entry ("<<name<<","<<val<<") conflicted with previous entry ("<<name<<","<<(*it->second)<<")" << std::endl;
00658         std::cerr << "         (use setEntry(...,false) if you expect you might need to overwrite)" << std::endl;
00659       }
00660       removeEntry(name);
00661       //fall through to add new val
00662     }
00663     dict[name]=&val;
00664     fireEntryAdded(val);
00665   }
00666   template<typename PO, typename Alloc>
00667   void DictionaryOf<PO,Alloc>::addEntry(const std::string& name, PO& val, const std::string& comment, bool warnExists) {
00668     DictionaryBase::storage_t::iterator it=dict.find(name);
00669     if(it!=dict.end()) {
00670       //found exact name match
00671       if(&val==it->second) {
00672         if(warnExists)
00673           std::cerr << "Warning: entry ("<<name<<","<<val<<") was already added, ignoring duplication..." << std::endl;
00674         myRef.erase(&val);
00675         return; // same val reference already registered
00676       }
00677       if(warnExists) {
00678         std::cerr << "Warning: new entry ("<<name<<","<<val<<") conflicted with previous entry ("<<name<<","<<(*it->second)<<")" << std::endl;
00679         std::cerr << "         (use setEntry() if you expect you might need to overwrite)" << std::endl;
00680       }
00681       removeEntry(name);
00682       //fall through to add new val
00683     }
00684     if(comment.size()>0)
00685       comments[name]=comment;
00686     dict[name]=&val;
00687     fireEntryAdded(val);
00688   }
00689   template<typename PO, typename Alloc>
00690   void DictionaryOf<PO,Alloc>::setEntry(const std::string& name, PO* val, bool warnExists/*=false*/) {
00691     DictionaryBase::iterator it=dict.find(name);
00692     if(it!=dict.end()) {
00693       //found exact name match
00694       if(val==it->second) {
00695         if(warnExists)
00696           std::cerr << "Warning: entry ("<<name<<","<<(*val)<<") was already added, ignoring duplication..." << std::endl;
00697         myRef.insert(val);
00698         return; // same val reference already registered
00699       }
00700       if(warnExists) {
00701         std::cerr << "Warning: new entry ("<<name<<","<<(*val)<<") conflicted with previous entry ("<<name<<","<<(*it->second)<<")" << std::endl;
00702         std::cerr << "         (use setEntry(...,false) if you expect you might need to overwrite)" << std::endl;
00703       }
00704       removeEntry(name);
00705       //fall through to add new val
00706     }
00707     dict[name]=val;
00708     takeObject(name,val);
00709     fireEntryAdded(*val);
00710   }
00711   template<typename PO, typename Alloc>
00712   void DictionaryOf<PO,Alloc>::addEntry(const std::string& name, PO* val, const std::string& comment, bool warnExists) {
00713     DictionaryBase::iterator it=dict.find(name);
00714     if(it!=dict.end()) {
00715       //found exact name match
00716       if(val==it->second) {
00717         if(warnExists)
00718           std::cerr << "Warning: entry ("<<name<<","<<(*val)<<") was already added, ignoring duplication..." << std::endl;
00719         myRef.insert(val);
00720         return; // same val reference already registered
00721       }
00722       if(warnExists) {
00723         std::cerr << "Warning: new entry ("<<name<<","<<(*val)<<") conflicted with previous entry ("<<name<<","<<(*it->second)<<")" << std::endl;
00724         std::cerr << "         (use setEntry() if you expect you might need to overwrite)" << std::endl;
00725       }
00726       removeEntry(name);
00727       //fall through to add new val
00728     }
00729     dict[name]=val;
00730     if(comment.size()>0)
00731       comments[name]=comment;
00732     takeObject(name,val);
00733     fireEntryAdded(*val);
00734   }
00735   
00736   //! implements the clone function for dictionary
00737   PLIST_CLONE_IMPT2(PO,Alloc,DictionaryOf,(new DictionaryOf<PO,Alloc>(*this)));
00738   
00739   template<typename PO, typename Alloc>
00740   bool DictionaryOf<PO,Alloc>::loadXMLNode(const std::string& key, xmlNode* val, const std::string& comment) {
00741     DictionaryBase::const_iterator it=dict.find(key);
00742     if(it!=dict.end()) {
00743       //found pre-existing entry
00744       try {
00745         //it's reasonable to assume that in common usage, the same type will be used each time
00746         //so let's try to load into the current entry as is
00747         it->second->loadXML(val);
00748         if(dynamic_cast<Collection*>(it->second)!=NULL) {
00749           const std::string headline=("======== "+key+" ========");
00750           if(comment.compare(0,headline.size(),headline)!=0)
00751             setComment(key,comment);
00752           else if(comment.size()>headline.size())
00753             setComment(key,comment.substr(headline.size()));
00754         } else if(comment.size()>0)
00755           setComment(key,comment);
00756         return true;
00757       } catch(...) {
00758         // apparently that didn't work, we'll need to make a new reference or clone
00759         if(loadPolicy!=SYNC) // can we do that?
00760           throw; // nope -- give up
00761         // still here? let's try a fresh load using the polymorphic loader (fall through below)
00762         removeEntry(key);
00763       }
00764     } else if(!(loadPolicy&ADDITIONS)) {
00765       if(warnUnused)
00766         std::cerr << "Warning: reading plist dictionary, key '" << key << "' does not match a registered variable.  Ignoring..." << std::endl;
00767       return false;
00768     }
00769     PO * cobj = plist::loadXML<PO>(val);
00770     if(dynamic_cast<Collection*>(cobj)!=NULL) {
00771       const std::string headline=("======== "+key+" ========");
00772       if(comment.compare(0,headline.size(),headline)!=0)
00773         addEntry(key,cobj,comment);
00774       else
00775         addEntry(key,cobj,comment.substr(headline.size()));
00776     } else 
00777       addEntry(key,cobj,comment);
00778     return true;
00779   }
00780   
00781   template<typename PO, typename Alloc>
00782   void DictionaryOf<PO,Alloc>::set(const DictionaryBase& d) {
00783     if(&d == this)
00784       return;
00785     // if we're doing a large dictionary, might be worth checking if we're actually the same type
00786     if(const DictionaryOf* od = dynamic_cast<const DictionaryOf*>(&d)) {
00787       operator=(*od); // same template type, use faster version!
00788       return;
00789     }
00790     DictionaryBase::operator=(d);
00791     
00792     std::set<std::string> seen;
00793     for(DictionaryBase::const_iterator dit=d.begin(); dit!=d.end(); ++dit) {
00794       const std::string key=dit->first;
00795       ObjectBase* val=dit->second;
00796       const std::string comment=d.getComment(key);
00797       seen.insert(key);
00798       DictionaryBase::storage_t::const_iterator it=dict.find(key);
00799       if(it!=dict.end()) {
00800         //found pre-existing entry
00801         try {
00802           //it's reasonable to assume that in common usage, the same type will be used each time
00803           //so let's try to load into the current entry as is
00804           it->second->set(*val);
00805           if(comment.size()>0)
00806             setComment(key,comment);
00807           continue;
00808         } catch(...) {
00809           // apparently that didn't work, we'll need to make a new reference or clone
00810           if(loadPolicy!=SYNC) // can we do that?
00811             throw; // nope -- give up
00812           // still here? let's try a fresh load using the polymorphic loader (fall through below)
00813           removeEntry(key);
00814         }
00815       } else if(!(loadPolicy&ADDITIONS)) {
00816         if(warnUnused)
00817           std::cerr << "Warning: reading plist dictionary, key '" << key << "' does not match a registered variable.  Ignoring..." << std::endl;
00818         continue;
00819       }
00820       PO* obj=dynamic_cast<PO*>(val);
00821       if(obj==NULL) {
00822         obj = plist::allocate<PO>();
00823         try {
00824           obj->set(*val);
00825         } catch(...) {
00826           delete obj;
00827           throw;
00828         }
00829         myRef.insert(obj);
00830       } else {
00831         obj = dynamic_cast<PO*>(obj->clone());
00832         if(obj==NULL) {
00833           // this shouldn't be possible: obj is a PO, so obj->clone should be as well
00834           throw std::runtime_error("plist::Dictionary could not assign clone of object due to impossible type conflict (bad clone?)");
00835         }
00836         myRef.insert(obj);
00837       }
00838       if(dynamic_cast<Collection*>(obj)!=NULL) {
00839         const std::string headline=("======== "+key+" ========");
00840         if(comment.compare(0,headline.size(),headline)!=0)
00841           addEntry(key,*obj,comment);
00842         else
00843           addEntry(key,*obj,comment.substr(headline.size()));
00844       } else 
00845         addEntry(key,*obj,comment);
00846     }
00847     if((loadPolicy&REMOVALS) && seen.size()!=size()) {
00848       std::set<std::string> rem;
00849       for(const_iterator it=begin(); it!=end(); ++it) {
00850         if(seen.find(it->first)==seen.end())
00851           rem.insert(it->first);
00852       }
00853       for(std::set<std::string>::const_iterator it=rem.begin(); it!=rem.end(); ++it)
00854         removeEntry(*it);
00855     }
00856   }
00857   
00858   template<typename PO, typename Alloc>
00859   DictionaryOf<PO,Alloc>& DictionaryOf<PO,Alloc>::operator=(const DictionaryOf& d) {
00860     if(&d==this)
00861       return *this;
00862     DictionaryBase::operator=(d);
00863     
00864     std::set<std::string> seen;
00865     for(const_iterator dit=d.begin(); dit!=d.end(); ++dit) {
00866       const std::string key=dit->first;
00867       PO* val=dit->second;
00868       const std::string comment=d.getComment(key);
00869       seen.insert(key);
00870       DictionaryBase::const_iterator it=dict.find(key);
00871       if(it!=dict.end()) {
00872         //found pre-existing entry
00873         try {
00874           //it's reasonable to assume that in common usage, the same type will be used each time
00875           //so let's try to load into the current entry as is
00876           plist::assign(*dynamic_cast<PO*>(it->second),*val);
00877           if(comment.size()>0)
00878             setComment(key,comment);
00879           continue;
00880         } catch(...) {
00881           // apparently that didn't work, we'll need to make a new reference or clone
00882           if(loadPolicy!=SYNC) // can we do that?
00883             throw; // nope -- give up
00884           // still here? let's try a fresh load using the polymorphic loader (fall through below)
00885           removeEntry(key);
00886         }
00887       } else if(!(loadPolicy&ADDITIONS)) {
00888         if(warnUnused)
00889           std::cerr << "Warning: reading plist dictionary, key '" << key << "' does not match a registered variable.  Ignoring..." << std::endl;
00890         continue;
00891       }
00892       PO* obj = dynamic_cast<PO*>(val->clone());
00893       if(obj==NULL) {
00894         // this shouldn't be possible: val is a PO, so obj->clone should be as well
00895         throw std::runtime_error("plist::Dictionary could not assign clone of object due to impossible type conflict (bad clone?)");
00896       }
00897       myRef.insert(obj);
00898       if(dynamic_cast<Collection*>(obj)!=NULL) {
00899         const std::string headline=("======== "+key+" ========");
00900         if(comment.compare(0,headline.size(),headline)!=0)
00901           addEntry(key,*obj,comment);
00902         else
00903           addEntry(key,*obj,comment.substr(headline.size()));
00904       } else 
00905         addEntry(key,*obj,comment);
00906     }
00907     if((loadPolicy&REMOVALS) && seen.size()!=size()) {
00908       std::set<std::string> rem;
00909       for(const_iterator it=begin(); it!=end(); ++it) {
00910         if(seen.find(it->first)==seen.end())
00911           rem.insert(it->first);
00912       }
00913       for(std::set<std::string>::const_iterator it=rem.begin(); it!=rem.end(); ++it)
00914         removeEntry(*it);
00915     }
00916     return *this;
00917   }
00918   
00919   
00920   //! Maintains an array of value, see ArrayOf, and the Array typedef
00921   /*! You can add or set entries by a quite a few variations on addEntry(), setEntry(), or addValue().
00922    *  Basically these boil down to either:
00923    *    - pass a pointer to an ObjectBase or directly pass a primitive value (int, float, char, etc.),
00924    *      in which case the Array will assume management of the corresponding ObjectBase
00925    *      instance (freeing the memory region when removed)
00926    *    - pass a reference to an ObjectBase, in which case you retain control over the object's
00927    *      allocation
00928    */
00929   class ArrayBase : virtual public Collection {
00930     friend std::ostream& operator<<(std::ostream& os, const ArrayBase& d);
00931   public:
00932     //! shorthand for the type of the storage
00933     typedef std::vector<ObjectBase*> storage_t;
00934     //! shorthand for iterators to be returned
00935     typedef storage_t::iterator iterator;
00936     //! shorthand for iterators to be returned
00937     typedef storage_t::const_iterator const_iterator;
00938     
00939     //! Indicates that no value conversions are allowed
00940     /*! The actual storage type is still allowed, so technically we could use EntryConstraint<PO>
00941      *  instead as the conversion policy, but that doesn't gain anything because you would need
00942      *  to know the PO to test for it.  At least with this you can test for DeniedValueConversions as a base
00943      *  class and then fall back to testing individual PO's if you want. */
00944     struct DeniedValueConversions {
00945       virtual ~DeniedValueConversions() {} //!< no-op destructor
00946     };
00947     
00948     template<typename PO>
00949     struct EntryConstraint {
00950       virtual ~EntryConstraint() {} //!< no-op destructor
00951       //! replaces previous entry at the specified @a index, which must represent an integer value less than or equal to the current array size
00952       virtual void addEntry(PO& val, const std::string& comment="")=0;
00953       //! replaces previous entry at the specified @a index, which must represent an integer value less than or equal to the current array size
00954       virtual void addEntry(PO* val, const std::string& comment="")=0;
00955       
00956       //! replaces previous entry at the specified @a index, which must be less than or equal to the current array size
00957       virtual void setEntry(size_t index, PO& val, bool warnExists=false)=0;
00958       //! replaces previous entry at the specified @a index, which must be less than or equal to the current array size
00959       virtual void addEntry(size_t index, PO& val, const std::string& comment="")=0;
00960       //! replaces previous entry at the specified @a index, which must be less than or equal to the current array size; control of (de)allocation will be assumed by the Array
00961       virtual void setEntry(size_t index, PO* val, bool warnExists=false)=0;
00962       //! replaces previous entry at the specified @a index, which must be less than or equal to the current array size; control of (de)allocation will be assumed by the Array
00963       virtual void addEntry(size_t index, PO* val, const std::string& comment="")=0;
00964     };
00965     
00966     //! 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)
00967     /*! The point of this class is to handle the situation where you have a DictionaryBase and user input to append, and
00968      *  you don't want to have to test every combination of template parameters to DictionaryOf to find out if you can
00969      *  add the data.  Instead, test dynamic_cast<plist::DictionaryBase::StringConversion>, and if it is successful, you
00970      *  can pass the string without having to know the actual value type of the dictionary. */
00971     struct StringConversion {
00972       virtual ~StringConversion() {} //!< no-op destructor
00973       //! generic addition of value at end of collection, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
00974       virtual void addValue(const std::string& val, const std::string& comment="")=0;
00975       //! generic addition of value at a specified position
00976       virtual void addValue(size_t index, const std::string&, const std::string& comment="")=0;
00977     };
00978     
00979     //! 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)
00980     /*! The point of this class is to handle the situation where you have a DictionaryBase and user input to append, and
00981      *  you don't want to have to test every combination of template parameters to DictionaryOf to find out if you can
00982      *  add the data.  Instead, test dynamic_cast<plist::DictionaryBase::IntegerConversion>, and if it is successful, you
00983      *  can pass the data without having to know the actual value type of the dictionary. */
00984     struct IntegerConversion {
00985       virtual ~IntegerConversion() {} //!< no-op destructor
00986       //! generic addition of value at end of collection, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
00987       virtual void addValue(long val, const std::string& comment="", bool warnExists=true)=0;
00988       //! generic addition of value at end of collection, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
00989       virtual void addValue(unsigned long val, const std::string& comment="", bool warnExists=true)=0;
00990       //! generic addition of value at specified position, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
00991       virtual void addValue(size_t index, long val, const std::string& comment="", bool warnExists=true)=0;
00992       //! generic addition of value at specified position, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
00993       virtual void addValue(size_t index, unsigned long val, const std::string& comment="", bool warnExists=true)=0;
00994     };
00995     
00996     //! 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)
00997     /*! The point of this class is to handle the situation where you have a DictionaryBase and user input to append, and
00998      *  you don't want to have to test every combination of template parameters to DictionaryOf to find out if you can
00999      *  add the data.  Instead, test dynamic_cast<plist::DictionaryBase::RealConversion>, and if it is successful, you
01000      *  can pass the data without having to know the actual value type of the dictionary. */
01001     struct RealConversion {
01002       virtual ~RealConversion() {} //!< no-op destructor
01003       //! generic addition of value at end of collection, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
01004       virtual void addValue(double val, const std::string& comment="", bool warnExists=true)=0;
01005       //! generic addition of value at specified position, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
01006       virtual void addValue(size_t index, double val, const std::string& comment="", bool warnExists=true)=0;
01007     };
01008     
01009     
01010     //! 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.
01011     /*! Use of this conversion policy requires that the template parameter is not abstract,
01012      *  as the policy will be trying to create new instances directly from the specified type. */
01013     template<typename PO>
01014     struct ConversionTo : public StringConversion, public EntryConstraint<PO> {
01015       //! insert a new entry to the end of the vector, 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 will be assumed by the Array
01016       template<typename T>
01017       void addValue(const T& val, const std::string& comment="") { addEntry(new PO(val),comment); }
01018       virtual void addValue(const std::string& val, const std::string& comment="") { PO * po=new PO; try { po->set(val); } catch(...) { delete po; throw; } this->addEntry(po,comment); }
01019       //! "specialization" (actually just another override) for handling character arrays as strings
01020       virtual void addValue(char val[], const std::string& comment="") { PO * po=new PO; try { po->set(val); } catch(...) { delete po; throw; } this->addEntry(po,comment); }
01021       //! "specialization" (actually just another override) for handling character arrays as strings
01022       virtual void addValue(const char val[], const std::string& comment="") { PO * po=new PO; try { po->set(val); } catch(...) { delete po; throw; } this->addEntry(po,comment); }
01023       //! generic addition of value at end of collection, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
01024       void addValue(long val, const std::string& comment="") { this->addEntry(new PO(val),comment); }
01025       //! generic addition of value at end of collection, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
01026       void addValue(unsigned long val, const std::string& comment="") { this->addEntry(new PO(val),comment); }
01027       //! generic addition of value at end of collection, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
01028       void addValue(double val, const std::string& comment="") { this->addEntry(new PO(val),comment); }
01029       
01030       //! inserts new entry at the specified @a index, which must be less than or equal to the current array size, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
01031       template<typename T>
01032       void addValue(size_t index, const T& val, const std::string& comment="") { addEntry(index,new PO(val),comment); }
01033       virtual void addValue(size_t index, const std::string& val, const std::string& comment="") { PO * po=new PO; try { po->set(val); } catch(...) { delete po; throw; } this->addEntry(index,po,comment); }
01034       //! "specialization" (actually just another override) for handling character arrays as strings, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
01035       virtual void addValue(size_t index, char val[], const std::string& comment="") { PO * po=new PO; try { po->set(val); } catch(...) { delete po; throw; } this->addEntry(index,po,comment); }
01036       //! "specialization" (actually just another override) for handling character arrays as strings, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
01037       virtual void addValue(size_t index, const char val[], const std::string& comment="") { PO * po=new PO; try { po->set(val); } catch(...) { delete po; throw; } this->addEntry(index,po,comment); }
01038       //! generic addition of value at specified position, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
01039       void addValue(size_t index, long val, const std::string& comment="") { this->addEntry(index,new PO(val),comment); }
01040       //! generic addition of value at specified position, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
01041       void addValue(size_t index, unsigned long val, const std::string& comment="") { this->addEntry(index,new PO(val),comment); }
01042       //! generic addition of value at specified position, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
01043       void addValue(size_t index, double val, const std::string& comment="") { this->addEntry(index,new PO(val),comment); }
01044     };
01045     
01046     //! 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
01047     /*! Use addEntry() to add ObjectBase subclasses -- addValue is for POD types */
01048     template<typename PO>
01049     struct WrapValueConversion : public StringConversion, public EntryConstraint<PO> {
01050       //! insert a new entry to the end of the vector, and corresponding comment; expects @a val to be either a primitive type, like int, float, etc., control of (de)allocation will be assumed by the Array
01051       template<typename T>
01052       void addValue(const T& val, const std::string& comment="") { this->addEntry(new Primitive<T>(val),comment); }
01053       virtual void addValue(const std::string& val, const std::string& comment="") { this->addEntry(new Primitive<std::string>(val),comment); }
01054       //! "specialization" (actually just another override) for handling character arrays as strings
01055       virtual void addValue(char val[], const std::string& comment="") { this->addEntry(new Primitive<std::string>(val),comment); }
01056       //! "specialization" (actually just another override) for handling character arrays as strings
01057       virtual void addValue(const char val[], const std::string& comment="") { this->addEntry(new Primitive<std::string>(val),comment); }
01058       //! generic addition of value at end of collection, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
01059       void addValue(long val, const std::string& comment="") { this->addEntry(new Primitive<long>(val),comment); }
01060       //! generic addition of value at end of collection, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
01061       void addValue(unsigned long val, const std::string& comment="") { this->addEntry(new Primitive<unsigned long>(val),comment); }
01062       //! generic addition of value at end of collection, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
01063       void addValue(double val, const std::string& comment="") { this->addEntry(new Primitive<double>(val),comment); }
01064       
01065       //! inserts new entry at the specified @a index, which must be less than or equal to the current array size, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
01066       template<typename T>
01067       void addValue(size_t index, const T& val, const std::string& comment="") { this->addEntry(index,new Primitive<T>(val),comment); }
01068       virtual void addValue(size_t index, const std::string& val, const std::string& comment="") { this->addEntry(index,new Primitive<std::string>(val),comment); }
01069       //! "specialization" (actually just another override) for handling character arrays as strings, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
01070       virtual void addValue(size_t index, char val[], const std::string& comment="") { this->addEntry(index,new Primitive<std::string>(val),comment); }
01071       //! "specialization" (actually just another override) for handling character arrays as strings, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
01072       virtual void addValue(size_t index, const char val[], const std::string& comment="") { this->addEntry(index,new Primitive<std::string>(val),comment); }
01073       //! generic addition of value at specified position, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
01074       void addValue(size_t index, long val, const std::string& comment="") { this->addEntry(index,new Primitive<long>(val),comment); }
01075       //! generic addition of value at specified position, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
01076       void addValue(size_t index, unsigned long val, const std::string& comment="") { this->addEntry(index,new Primitive<unsigned long>(val),comment); }
01077       //! generic addition of value at specified position, control of (de)allocation of corresponding Primitive instance will be assumed by the Array
01078       void addValue(size_t index, double val, const std::string& comment="") { this->addEntry(index,new Primitive<double>(val),comment); }
01079     };
01080     
01081     //! adds a new entry to the end of the array, (de)allocation retained by caller
01082     virtual void forceEntry(ObjectBase& val, const std::string& comment="")=0;
01083     //! adds a new entry to the end of the array, (de)allocation responsibility of array
01084     virtual void forceEntry(ObjectBase* val, const std::string& comment="")=0;
01085     //! displaces previous entry at the specified @a index, which must be less than or equal to the current array size
01086     virtual void forceEntry(size_t index, ObjectBase& val, const std::string& comment="")=0;
01087     //! displaces previous entry at the specified @a index, which must be less than or equal to the current array size; control of (de)allocation will be assumed by the Array
01088     virtual void forceEntry(size_t index, ObjectBase* val, const std::string& comment="")=0;
01089     
01090     //! remove the entry at position @a index, returns true if something was actually removed (if false, wasn't there to begin with)
01091     virtual bool removeEntry(size_t index);
01092     //! return the value at position @a index, which must exist (no range checking)
01093     ObjectBase& getEntry(size_t index) const { return *arr[index]; }
01094     //! return the value at position @a index, which must exist (no range checking, equivalent to getEntry(index))
01095     ObjectBase& operator[](size_t index) const { return *arr[index]; }
01096     virtual ObjectBase* resolveEntry(const std::string& path) const;
01097     
01098     virtual void clear();
01099     
01100     //! return an STL const_iterator to the first entry
01101     const_iterator begin() const { return arr.begin(); }
01102     //! return the one-past-end const_iterator
01103     const_iterator end() const { return arr.end(); }
01104     //! return the size of the array
01105     virtual size_t size() const { return arr.size(); }
01106     //! return first element
01107     ObjectBase& front() const { return *arr.front(); }
01108     //! return last element
01109     ObjectBase& back() const { return *arr.back(); }
01110     
01111     //! replaces previous comment for @a name, or adds it if it doesn't already exist (can preceed actual entry!)
01112     virtual void setComment(size_t index, const std::string& comment);
01113     //! returns comment retrieved from loaded file, or any subsequent call to setComment
01114     virtual const std::string& getComment(size_t index) const;
01115     
01116     virtual void loadXML(xmlNode* node);
01117     virtual void saveXML(xmlNode* node) const;
01118     
01119     virtual std::string toString() const;
01120     
01121     virtual unsigned int getLongestKeyLen(const regex_t* reg=NULL, unsigned int depth=-1) const;
01122     
01123     //! returns true if the specified object will be deallocated when removed from the array
01124     bool ownsReference(ObjectBase * val) const { return myRef.find(val)==myRef.end(); }
01125     
01126     virtual void setSaveInlineStyle(bool b) { saveInlineStyle=b; } //!< sets #saveInlineStyle
01127     virtual bool getSaveInlineStyle() const { return saveInlineStyle; } //!< returns #saveInlineStyle
01128     
01129 protected:
01130     //! constructor
01131     ArrayBase(bool growable) : Collection(growable?SYNC:FIXED,SYNC), arr(), myRef(), comments(), saveInlineStyle(false) { setLoadSavePolicy(growable?SYNC:FIXED,SYNC); }
01132     //! copy constructor
01133     ArrayBase(const ArrayBase& d) : Collection(d), arr(d.arr), myRef(d.myRef), comments(d.comments), saveInlineStyle(false) { cloneMyRef(); setLoadSavePolicy(d.getLoadPolicy(),d.getSavePolicy()); }
01134     //! assignment
01135     ArrayBase& operator=(const ArrayBase& d) { Collection::operator=(d); return *this; }
01136     
01137     //! destructor
01138     ~ArrayBase() { clear(); }
01139     
01140     //! indicates that the storage implementation should mark this as an externally supplied heap reference, which needs to be deleted on removal/destruction
01141     virtual void takeObject(size_t index, ObjectBase* obj);
01142     
01143     virtual void fireEntryRemoved(ObjectBase& val);
01144     
01145     //! returns an entry matching just the prefix
01146     /*! @param[in] name the name being looked up
01147      *  @param[out] seppos the position of the separator (sub-collections are separated by '.')
01148      *  @return iterator of the sub entry*/
01149     iterator getSubEntry(const std::string& name, std::string::size_type& seppos);
01150     //! returns an entry matching just the prefix
01151     /*! @param[in] name the name being looked up
01152      *  @param[out] seppos the position of the separator (sub-collections are separated by '.')
01153      *  @return iterator of the sub entry*/
01154     const_iterator getSubEntry(const std::string& name, std::string::size_type& seppos) const;
01155     
01156     //! called after an assignment or copy to clone the objects in #myRef to perform a deep copy
01157     virtual void cloneMyRef();
01158     
01159     //! called by loadXML(), loads a single xmlNode into a specified position, replacing previous entry if it can't accept the new value (subject to the load/save policy...)
01160     virtual bool loadXMLNode(size_t index, xmlNode* val, const std::string& comment)=0;
01161     
01162     //! storage of entries
01163     storage_t arr;
01164     
01165     //! objects which have been handed over to the collection for eventual de-allocation
01166     std::set<ObjectBase*> myRef;
01167     
01168     //! shorthand for the type of #comments
01169     typedef std::map<size_t,std::string> comments_t;
01170     //! storage of entry comments -- mapping from keys to help text comments for manual editing or user feedback
01171     /*! not every key necessarily has a comment! */
01172     comments_t comments;
01173     
01174     //! If true, will avoid line breaks between entries in serialized output, also skips comment headers
01175     /*! This might make short lists more readable. */
01176     bool saveInlineStyle;
01177   };
01178   
01179   
01180   //! A collection of plist objects, similar to a Dictionary, but no keys -- order matters!, see plist::Array
01181   /*! You can add or set entries by a quite a few variations on addEntry(), setEntry(), and addValue (via the Alloc conversion policy)
01182    *  Basically these boil down to either:
01183    *    - pass a pointer to an ObjectBase or directly pass a primitive value (int, float, char, etc.),
01184    *      in which case the Array will assume management of the corresponding ObjectBase
01185    *      instance (freeing the memory region when removed)
01186    *    - pass a reference to an ObjectBase, in which case you retain control over the object's
01187    *      allocation
01188    *  
01189    *  You have probably noticed this is a templated class -- you can provide any of the ObjectBase
01190    *  subclasses to restrict the storage to that particular type, which will make life easier when
01191    *  retrieving objects since their type will be known.
01192    *
01193    *  However, if you @e want an Array of mixed types, you can pass ObjectBase itself for the
01194    *  template parameter, and you can then insert any combination of the plist types into the
01195    *  same array.  For convenience, a plist::Array typedef is provided which does exactly this.
01196    *  
01197    *  So plist::Array can handle any mixture of types, whereas plist::ArrayOf<PO> will @e only
01198    *  accept the plist objects of type PO (or their subclasses).  The Alloc template argument
01199    *  allows you to define how new string values will be handled from ArrayBase.
01200    *
01201    *  The optional conversion policy template specifies a base class for the dictionary which
01202    *  can control how the dictionary will handle conversions from non-PO-based types.
01203    */
01204   template<typename PO, typename Alloc=typename PO::template conversion_policy<ArrayBase,PO>::value_conversion >
01205   class ArrayOf : public ArrayBase, public Alloc {
01206     /// @cond INTERNAL
01207     typedef typename storage_t::const_iterator::iterator_category const_iterator_category;
01208     typedef typename storage_t::const_iterator::difference_type const_iterator_difference;
01209     /// @endcond
01210   public:
01211     //! shorthand for the type of the storage
01212     typedef typename ArrayBase::storage_t storage_t;
01213     
01214     /// @cond INTERNAL
01215     
01216     //! iterator implementation which wraps storage_t::const_iterator to transparently dynamic_cast to the PO for client
01217     class const_iterator : public std::iterator<const_iterator_category, const PO*, const_iterator_difference> {
01218     public:
01219       typedef PO* value_type;
01220       typedef const_iterator_difference difference_type;
01221       
01222       const_iterator(const storage_t::const_iterator& sit) : it(sit), tmp(NULL) {}
01223       const_iterator(const const_iterator& oit) : it(oit.it), tmp(NULL) {}
01224       const_iterator& operator=(const const_iterator& oit) { it=oit.it; return *this; }
01225       
01226       value_type operator*() const { return dynamic_cast<PO*>(*it); }
01227       value_type const* operator->() const { tmp=dynamic_cast<PO*>(*it); return &tmp; }
01228       const_iterator& operator++() { ++it; return *this; }
01229       const_iterator operator++(int) { return const_iterator(it++); }
01230       const_iterator& operator--() { --it; return *this; }
01231       const_iterator operator--(int) { return const_iterator(it--); }
01232 
01233       bool operator==(const const_iterator& rhs) const { return it==rhs.it; }
01234       bool operator!=(const const_iterator& rhs) const { return it!=rhs.it; }
01235       
01236       // Random access iterator requirements
01237       value_type operator[](const difference_type& __n) const { return dynamic_cast<PO*>(it[__n]); }
01238       const_iterator& operator+=(const difference_type& __n) { it += __n; return *this; }
01239       const_iterator operator+(const difference_type& __n) const { return const_iterator(it + __n); }
01240       const_iterator& operator-=(const difference_type& __n) { it -= __n; return *this; }
01241       const_iterator operator-(const difference_type& __n) const { return const_iterator(it - __n); }
01242       bool operator<(const const_iterator& __rhs) const { return it < __rhs.it; }
01243       bool operator>(const const_iterator& __rhs) const { return it > __rhs.it; }
01244       bool operator<=(const const_iterator& __rhs) const { return it <= __rhs.it; }
01245       bool operator>=(const const_iterator& __rhs) const { return it >= __rhs.it; }
01246       difference_type operator-(const const_iterator& __rhs) const { return it - __rhs.it; }
01247       
01248     protected:
01249       storage_t::const_iterator it;
01250       mutable PO* tmp; //!< used by operator->() so it doesn't return pointer-to-temporary
01251     };
01252     /// @endcond
01253     
01254     //! constructor
01255     ArrayOf() : ArrayBase(true), Alloc() {}
01256     //! constructor, create @a n entries using default constructor, defaults to non-growable (load does not change size)
01257     explicit ArrayOf(typename storage_t::size_type n) : ArrayBase(false), Alloc() {
01258       arr.resize(n);
01259       for(ArrayBase::iterator it=arr.begin(); it!=arr.end(); ++it)
01260         *it=new PO;
01261       myRef.insert(arr.begin(),arr.end());
01262     }
01263     //! constructor, create @a n copies of @a t, defaults to non-growable (load does not change size)
01264     ArrayOf(typename storage_t::size_type n, const PO& t, bool growable=false) : ArrayBase(growable), Alloc() {
01265       arr.resize(n);
01266 #ifdef PLATFORM_APERIOS
01267       for(ArrayBase::iterator it=arr.begin(); it!=arr.end(); ++it) {
01268         *it=dynamic_cast<PO*>(t.clone()); // older compiler on Aperios, doesn't support polymorphic return, just assume
01269 #  ifdef DEBUG
01270         if(*it==NULL)
01271           std::cerr << "plist::Array construction from replicated value given NULL via clone" << std::endl;
01272 #  endif
01273       }
01274 #else
01275       for(ArrayBase::iterator it=arr.begin(); it!=arr.end(); ++it)
01276         *it=t.clone();
01277 #endif
01278       myRef.insert(arr.begin(),arr.end());
01279     }
01280     //! copy constructor (don't assign listeners)
01281     ArrayOf(const ArrayOf& d) : ArrayBase(d), Alloc(d) {}
01282     //! assignment (don't assign listeners); subclass should call fireEntriesChanged after calling this (and updating its own storage)
01283     ArrayOf& operator=(const ArrayOf& a);
01284 
01285     virtual void set(const ObjectBase& ob) { const ArrayBase& a=dynamic_cast<const ArrayBase&>(ob); set(a); }
01286     virtual void set(const ArrayBase& a); //!< handles polymorphic assignment of ArrayBase subclasses, similar to operator=(ArrayOf), but allows conversion of entries
01287     
01288     //! destructor
01289     ~ArrayOf() { }
01290     
01291     //! adds a new entry to the end of the array, (de)allocation retained by caller
01292     virtual void addEntry(PO& val, const std::string& comment="") { if(comment.size()>0) setComment(size(),comment); arr.push_back(&val); fireEntryAdded(*arr.back()); }
01293     //! adds a new entry to the end of the array, (de)allocation responsibility of array
01294     virtual void addEntry(PO* val, const std::string& comment="") { if(comment.size()>0) setComment(size(),comment); arr.push_back(val); takeObject(size()-1,val); fireEntryAdded(*arr.back()); }
01295     
01296     //! replaces previous entry at the specified @a index, which must be less than or equal to the current array size
01297     virtual void setEntry(size_t index, PO& val, bool warnExists=false);
01298     //! displaces previous entry at the specified @a index, which must be less than or equal to the current array size
01299     virtual void addEntry(size_t index, PO& val, const std::string& comment="");
01300     //! replaces previous entry at the specified @a index, which must be less than or equal to the current array size; control of (de)allocation will be assumed by the Array
01301     virtual void setEntry(size_t index, PO* val, bool warnExists=false);
01302     //! displaces previous entry at the specified @a index, which must be less than or equal to the current array size; control of (de)allocation will be assumed by the Array
01303     virtual void addEntry(size_t index, PO* val, const std::string& comment="");
01304     
01305     //! adds a new entry to the end of the array, (de)allocation retained by caller
01306     virtual void forceEntry(ObjectBase& val, const std::string& comment="") { if(PO* po=dynamic_cast<PO*>(&val)) addEntry(*po,comment); else throw bad_format(NULL,"plist::ArrayBase::forceEntry() of wrong type"); }
01307     //! adds a new entry to the end of the array, (de)allocation responsibility of array
01308     virtual void forceEntry(ObjectBase* val, const std::string& comment="") { if(PO* po=dynamic_cast<PO*>(val)) addEntry(po,comment); else throw bad_format(NULL,"plist::ArrayBase::forceEntry() of wrong type"); }
01309     //! displaces previous entry at the specified @a index, which must be less than or equal to the current array size
01310     virtual void forceEntry(size_t index, ObjectBase& val, const std::string& comment="") { if(PO* po=dynamic_cast<PO*>(&val)) addEntry(index,*po,comment); else throw bad_format(NULL,"plist::ArrayBase::forceEntry() of wrong type"); }
01311     //! displaces previous entry at the specified @a index, which must be less than or equal to the current array size; control of (de)allocation will be assumed by the Array
01312     virtual void forceEntry(size_t index, ObjectBase* val, const std::string& comment="") { if(PO* po=dynamic_cast<PO*>(val)) addEntry(index,po,comment); else throw bad_format(NULL,"plist::ArrayBase::forceEntry() of wrong type"); }
01313     
01314     //! return the value at position @a index, which must exist (no range checking)
01315     PO& getEntry(size_t index) const { return dynamic_cast<PO&>(*arr[index]); }
01316     //! return the value at position @a index, which must exist (no range checking, equivalent to getEntry(index))
01317     PO& operator[](size_t index) const { return dynamic_cast<PO&>(*arr[index]); }
01318     
01319     //! return an STL const_iterator to the first entry
01320     const_iterator begin() const { return arr.begin(); }
01321     //! return the one-past-end const_iterator
01322     const_iterator end() const { return arr.end(); }
01323     //! return first element
01324     PO& front() const { return dynamic_cast<PO&>(*arr.front()); }
01325     //! return last element
01326     PO& back() const { return dynamic_cast<PO&>(*arr.back()); }
01327     
01328     //! returns true if the array can store the specified object
01329     virtual bool canContain(const ObjectBase& obj) { return (dynamic_cast<const PO*>(&obj)!=NULL); }
01330     
01331     //! clone implementation for Array
01332     PLIST_CLONE_DEF(ArrayOf,(new ArrayOf<PO,Alloc>(*this)));
01333       
01334   protected:
01335     //! called with each node being loaded so subclass can handle appropriately
01336     virtual bool loadXMLNode(size_t index, xmlNode* val, const std::string& comment);
01337   };
01338   
01339   // This specialization allows use of the generic Array(n) constructor, but everything will be NULL: user must be sure to initialize all of the elements!
01340   template<> ArrayOf<ObjectBase, ObjectBase::conversion_policy<ArrayBase,ObjectBase>::value_conversion>::ArrayOf(storage_t::size_type n) ATTR_deprecated;
01341   
01342   /*! plist::Array can handle any mixture of types, whereas plist::ArrayOf<PO> will @e only
01343    *  accept the plist objects of type PO (or their subclasses).  This typedef is simply for
01344    *  convenience and passes ObjectBase for the template parameter. */
01345   typedef ArrayOf<ObjectBase> Array;
01346   extern template class ArrayOf<ObjectBase>;
01347   extern template class ArrayOf<Dictionary>;
01348   extern template class ArrayOf<Primitive<float> >;
01349   extern template class ArrayOf<Primitive<double> >;
01350   
01351 
01352   template<typename PO, typename Alloc>
01353   void ArrayOf<PO,Alloc>::setEntry(size_t index, PO& val, bool warnExists/*=false*/) {
01354     if(index==size()) {
01355       arr.push_back(&val);
01356       fireEntryAdded(val);
01357     } else {
01358       if(arr[index]==&val)
01359         return;
01360       if(warnExists) {
01361         std::cerr << "Warning: new entry "<<index<<" ("<<val<<") conflicted with previous entry "<<index<<" ("<<(*arr[index])<<")"<<std::endl;
01362         std::cerr << "         (use setEntry(...,false) if you expect you might need to overwrite)" << std::endl;
01363       }
01364       arr[index]=&val;
01365       fireEntriesChanged();
01366     }
01367   }
01368   template<typename PO, typename Alloc>
01369   void ArrayOf<PO,Alloc>::addEntry(size_t index, PO& val, const std::string& comment/*=""*/) {
01370     if(index==size()) {
01371       arr.push_back(&val);
01372     } else {
01373       ArrayBase::storage_t::iterator it=arr.begin();
01374       advance(it,index);
01375       arr.insert(it,&val);
01376     }
01377     if(comment.size()>0)
01378       setComment(index,comment);
01379     fireEntryAdded(val);
01380   }
01381   template<typename PO, typename Alloc>
01382   void ArrayOf<PO,Alloc>::setEntry(size_t index, PO* val, bool warnExists/*=false*/) {
01383     if(index>size())
01384       throw bad_format(NULL,"Error: attempted to setEntry() at index beyond one-past-end of plist::Array");
01385     else if(index==size()) {
01386       arr.push_back(val);
01387       fireEntryAdded(*val);
01388     } else {
01389       if(arr[index]==val)
01390         return;
01391       if(warnExists) {
01392         std::cerr << "Warning: new entry "<<index<<" ("<<val<<") conflicted with previous entry "<<index<<" ("<<(*arr[index])<<")"<<std::endl;
01393         std::cerr << "         (use setEntry(...,false) if you expect you might need to overwrite)" << std::endl;
01394       }
01395       std::set<ObjectBase*>::iterator it=myRef.find(arr[index]);
01396       if(it!=myRef.end()) {
01397         myRef.erase(*it);
01398         delete arr[index];
01399       }
01400       arr[index]=val;
01401       takeObject(index,val);
01402       fireEntriesChanged();
01403     }
01404   }
01405   template<typename PO, typename Alloc>
01406   void ArrayOf<PO,Alloc>::addEntry(size_t index, PO* val, const std::string& comment/*=""*/) {
01407     if(index>size())
01408       throw bad_format(NULL,"Error: attempted to addEntry() at index beyond one-past-end of plist::Array");
01409     else if(index==size()) {
01410       arr.push_back(val);
01411     } else {
01412       ArrayBase::storage_t::iterator it=arr.begin();
01413       advance(it,index);
01414       arr.insert(it,val);
01415     }
01416     takeObject(index,val);
01417     if(comment.size()>0)
01418       setComment(index,comment);
01419     fireEntryAdded(*val);
01420   }
01421   
01422   //! implements the clone function for Array
01423   PLIST_CLONE_IMPT2(PO,Alloc,ArrayOf,(new ArrayOf<PO,Alloc>(*this)));
01424   
01425   template<typename PO, typename Alloc>
01426   bool ArrayOf<PO,Alloc>::loadXMLNode(size_t index, xmlNode* val, const std::string& comment) {
01427     if(index<size()) {
01428       //have pre-existing entry
01429       try {
01430         //it's reasonable to assume that in common usage, the same type will be used each time
01431         //so let's try to load into the current entry as is
01432         arr[index]->loadXML(val);
01433         if(comment.size()>0)
01434           setComment(index,comment);
01435         return true;
01436       } catch(...) {
01437         // apparently that didn't work, we'll need to make a new reference or clone
01438         if(loadPolicy!=SYNC) // can we do that?
01439           throw; // nope -- give up
01440         // still here? let's try a fresh load using the polymorphic loader (fall through below)
01441       }
01442     } else if(!(savePolicy&ADDITIONS)) {
01443       if(warnUnused && savePolicy==FIXED)
01444         std::cerr << "Warning: plist::Array ran out of registered items (" << size() << ") during load.  Ignoring extraneous items from source..." << std::endl;
01445       return false;
01446     }
01447     PO * cobj = plist::loadXML<PO>(val);
01448     if(index<size()) {
01449       setEntry(index,cobj,false);
01450       setComment(index,comment);
01451     } else {
01452       addEntry(index,cobj,comment);
01453     }
01454     return true;
01455   }
01456   
01457   template<typename PO, typename Alloc>
01458   void ArrayOf<PO,Alloc>::set(const ArrayBase& a) {
01459     if(&a == this)
01460       return;
01461     // if we're doing a large list, might be worth checking if we're actually the same type
01462     if(const ArrayOf* ao = dynamic_cast<const ArrayOf*>(&a)) {
01463       operator=(*ao); // same template type, use faster version!
01464       return;
01465     }
01466     ArrayBase::operator=(a);
01467     // otherwise we'll have to check the conversion on every single entry...
01468     for(unsigned int index=0; index<a.size(); ++index) {
01469       ObjectBase* val = &a[index];
01470       const std::string comment = a.getComment(index);
01471       if(index<size()) {
01472         //have pre-existing entry
01473         try {
01474           //it's reasonable to assume that in common usage, the same type will be used each time
01475           //so let's try to load into the current entry as is
01476           arr[index]->set(*val);
01477           if(comment.size()>0)
01478             setComment(index,comment);
01479           continue;
01480         } catch(const bad_format& ex) {
01481           // apparently that didn't work, we'll need to make a new reference or clone
01482           if(loadPolicy!=SYNC) // can we do that?
01483             throw; // nope -- give up
01484           // still here? let's try a fresh load using the polymorphic loader (fall through below)
01485         }
01486       } else if(!(savePolicy&ADDITIONS)) {
01487         if(warnUnused && savePolicy==FIXED)
01488           std::cerr << "Warning: plist::Array ran out of registered items (" << size() << ") during load.  Ignoring extraneous items from source..." << std::endl;
01489         break;
01490       }
01491       PO* obj=dynamic_cast<PO*>(val);
01492       if(obj==NULL) {
01493         obj = plist::allocate<PO>();
01494         try {
01495           obj->set(*val);
01496         } catch(...) {
01497           delete obj;
01498           throw;
01499         }
01500         myRef.insert(obj);
01501       } else {
01502         obj = dynamic_cast<PO*>(obj->clone());
01503         if(obj==NULL) {
01504           // this shouldn't be possible: obj is a PO, so obj->clone should be as well
01505           throw std::runtime_error("plist::Array could not assign clone of object due to impossible type conflict (bad clone?)");
01506         }
01507         myRef.insert(obj);
01508       }
01509       if(index<size()) {
01510         setEntry(index,obj,false);
01511         setComment(index,comment);
01512       } else {
01513         addEntry(index,obj,comment);
01514       }
01515     }
01516   }
01517 
01518   template<typename PO, typename Alloc>
01519   ArrayOf<PO,Alloc>& ArrayOf<PO,Alloc>::operator=(const ArrayOf& a) {
01520     if(&a==this)
01521       return *this;
01522     ArrayBase::operator=(a);
01523     for(unsigned int index=0; index<a.size(); ++index) {
01524       PO* val = &a[index];
01525       const std::string comment = a.getComment(index);
01526       if(index<size()) {
01527         //have pre-existing entry
01528         try {
01529           //it's reasonable to assume that in common usage, the same type will be used each time
01530           //so let's try to load into the current entry as is
01531           plist::assign(*dynamic_cast<PO*>(arr[index]),*val);
01532           if(comment.size()>0)
01533             setComment(index,comment);
01534           continue;
01535         } catch(const bad_format& ex) {
01536           // apparently that didn't work, we'll need to make a new reference or clone
01537           if(loadPolicy!=SYNC) // can we do that?
01538             throw; // nope -- give up
01539           // still here? let's try a fresh load using the polymorphic loader (fall through below)
01540         }
01541       } else if(!(savePolicy&ADDITIONS)) {
01542         if(warnUnused && savePolicy==FIXED)
01543           std::cerr << "Warning: plist::Array ran out of registered items (" << size() << ") during load.  Ignoring extraneous items from source..." << std::endl;
01544         break;
01545       }
01546       PO* obj = dynamic_cast<PO*>(val->clone());
01547       if(obj==NULL) {
01548         // this shouldn't be possible: val is a PO, so obj->clone should be as well
01549         throw std::runtime_error("plist::Array could not assign clone of object due to impossible type conflict (bad clone?)");
01550       }
01551       myRef.insert(obj);
01552       if(index<size()) {
01553         setEntry(index,obj,false);
01554         setComment(index,comment);
01555       } else {
01556         addEntry(index,obj,comment);
01557       }
01558     }
01559     return *this;
01560   }
01561     
01562   //! take a regex and maximum depth for display (displays entries whos names match the filter @a sel
01563   /*! @param os the ostream to write into
01564    *  @param c the collection to dump
01565    *  @param sel the regular expression in string form
01566    *  @param selType how to interpret @a sel, this is the flags argument to regcomp(), e.g. REG_EXTENDED or REG_BASIC
01567    *  @param depth maximum recursion depth for sub-collections */
01568   std::ostream& filteredDisplay(std::ostream& os, const ObjectBase& c, const std::string& sel, int selType, unsigned int depth);
01569   
01570   //! take a compiled regex and maximum depth for display
01571   /*! @param os the ostream to write into
01572    *  @param c the collection to dump
01573    *  @param reg a pre-compiled regular expression, or NULL to match everything
01574    *  @param depth maximum recursion depth for sub-collections */
01575   std::ostream& filteredDisplay(std::ostream& os, const ObjectBase& c, const regex_t* reg, unsigned int depth);
01576   
01577   //! provides textual output
01578   inline std::ostream& operator<<(std::ostream& os, const DictionaryBase& d) { return filteredDisplay(os,d,NULL,-1U); }
01579   
01580   //! provides textual output
01581   inline std::ostream& operator<<(std::ostream& os, const ArrayBase& d) { return filteredDisplay(os,d,NULL,-1U); }
01582   
01583 } //namespace plist
01584 
01585 /*! @file
01586  * @brief 
01587  * @author Ethan Tira-Thompson (ejt) (Creator)
01588  */
01589 
01590 #endif

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