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

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