Tekkotsu Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

plistCollections.cc

Go to the documentation of this file.
00001 #include "plistCollections.h"
00002 #include <libxml/xmlmemory.h>
00003 #include <libxml/parser.h>
00004 #include <iomanip>
00005 
00006 //better to put this here instead of the header
00007 using namespace std; 
00008 
00009 namespace plist {
00010 
00011   Collection::~Collection() {
00012     delete collectionListeners;
00013     collectionListeners=NULL;
00014   }
00015   
00016   void Collection::addCollectionListener(CollectionListener* l) {
00017     if(l!=NULL) {
00018       if(collectionListeners==NULL)
00019         collectionListeners=new std::list<CollectionListener*>;
00020       collectionListeners->push_back(l);
00021     }
00022   }
00023     
00024   void Collection::removeCollectionListener(CollectionListener* l) {
00025     if(collectionListeners==NULL)
00026       return;
00027     std::list<CollectionListener*>::iterator it=find(collectionListeners->begin(),collectionListeners->end(),l);
00028     if(it!=collectionListeners->end()) {
00029       collectionListeners->erase(it);
00030       if(collectionListeners->empty()) {
00031         delete collectionListeners;
00032         collectionListeners=NULL;
00033       }
00034     }
00035   }
00036   
00037   bool Collection::isCollectionListener(CollectionListener* l) {
00038     if(l==NULL)
00039       return false;
00040     if(collectionListeners==NULL)
00041       return false;
00042     std::list<CollectionListener*>::iterator it=find(collectionListeners->begin(),collectionListeners->end(),l);
00043     return it!=collectionListeners->end();
00044   }
00045     
00046   void Collection::fireEntryAdded(ObjectBase& val) {
00047     if(collectionListeners==NULL)
00048       return;
00049     std::list<CollectionListener*>::iterator it=collectionListeners->begin();
00050     while(it!=collectionListeners->end()) {
00051       std::list<CollectionListener*>::iterator cur=it++; //increment early in case the listener changes subscription
00052       (*cur)->plistCollectionEntryAdded(*this,val);
00053     }
00054   }
00055   
00056   void Collection::fireEntryRemoved(ObjectBase& val) {
00057     if(collectionListeners==NULL)
00058       return;
00059     std::list<CollectionListener*>::iterator it=collectionListeners->begin();
00060     while(it!=collectionListeners->end()) {
00061       std::list<CollectionListener*>::iterator cur=it++; //increment early in case the listener changes subscription
00062       (*cur)->plistCollectionEntryRemoved(*this,val);
00063     }
00064   }
00065   
00066   void Collection::fireEntriesChanged() {
00067     if(collectionListeners==NULL)
00068       return;
00069     std::list<CollectionListener*>::iterator it=collectionListeners->begin();
00070     while(it!=collectionListeners->end()) {
00071       std::list<CollectionListener*>::iterator cur=it++; //increment early in case the listener changes subscription
00072       (*cur)->plistCollectionEntriesChanged(*this);
00073     }
00074   }
00075   
00076 
00077 
00078   
00079   
00080   void Dictionary::setEntry(const std::string& name, ObjectBase& val, bool warnExists/*=false*/) {
00081     storage_t::iterator it=dict.find(name);
00082     if(it!=dict.end()) {
00083       //found exact name match
00084       if(&val==it->second)
00085         return; // same val reference already registered
00086       if(warnExists) {
00087         cerr << "Warning: new entry ("<<name<<","<<val<<") conflicted with previous entry ("<<name<<","<<(it->second)<<")"<<endl;
00088         cerr << "         (use setEntry(...,false) if you expect you might need to overwrite)" << endl;
00089       }
00090       removeEntry(name);
00091       //fall through to add new val
00092     } else {
00093       //perhaps there's a sub-dictionary
00094       string::size_type p;
00095       it=getSubEntry(name,p);
00096       if(it!=dict.end()) {
00097         //found a matching sub-collection, have it find the rest recursively
00098         Collection* d=dynamic_cast<Collection*>(it->second);
00099         d->setEntry(name.substr(p+1),val,warnExists);
00100         return;
00101       }
00102       //if still here, no sub-collection, fall through to add new entry
00103     }
00104     dict[name]=&val;
00105     fireEntryAdded(val);
00106   }
00107   void Dictionary::addEntry(const std::string& name, ObjectBase& val, const std::string& comment, bool warnExists) {
00108     storage_t::iterator it=dict.find(name);
00109     if(it!=dict.end()) {
00110       //found exact name match
00111       if(&val==it->second)
00112         return; // same val reference already registered
00113       if(warnExists) {
00114         cerr << "Warning: new entry ("<<name<<","<<val<<") conflicted with previous entry ("<<name<<","<<(it->second)<<")"<<endl;
00115         cerr << "         (use setEntry() if you expect you might need to overwrite)" << endl;
00116       }
00117       removeEntry(name);
00118       //fall through to add new val
00119     } else {
00120       //perhaps there's a sub-dictionary
00121       string::size_type p;
00122       it=getSubEntry(name,p);
00123       if(it!=dict.end()) {
00124         //found a matching sub-collection, have it find the rest recursively
00125         Collection* d=dynamic_cast<Collection*>(it->second);
00126         d->addEntry(name.substr(p+1),val,comment);
00127         return;
00128       }
00129       //if still here, no sub-collection, fall through to add new entry
00130     }
00131     if(comment.size()>0)
00132       comments[name]=comment;
00133     dict[name]=&val;
00134     fireEntryAdded(val);
00135   }
00136   void Dictionary::setEntry(const std::string& name, ObjectBase* val, bool warnExists/*=false*/) {
00137     storage_t::iterator it=dict.find(name);
00138     if(it!=dict.end()) {
00139       //found exact name match
00140       if(val==it->second)
00141         return; // same val reference already registered
00142       if(warnExists) {
00143         cerr << "Warning: new entry ("<<name<<","<<val<<") conflicted with previous entry ("<<name<<","<<(it->second)<<")"<<endl;
00144         cerr << "         (use setEntry(...,false) if you expect you might need to overwrite)" << endl;
00145       }
00146       removeEntry(name);
00147       //fall through to add new val
00148     } else {
00149       //perhaps there's a sub-dictionary
00150       string::size_type p;
00151       it=getSubEntry(name,p);
00152       if(it!=dict.end()) {
00153         //found a matching sub-collection, have it find the rest recursively
00154         Collection* d=dynamic_cast<Collection*>(it->second);
00155         d->setEntry(name.substr(p+1),val,warnExists);
00156         return;
00157       }
00158       //if still here, no sub-collection, fall through to add new entry
00159     }
00160     dict[name]=val;
00161     takeObject(name,val);
00162     fireEntryAdded(*val);
00163   }
00164   void Dictionary::addEntry(const std::string& name, ObjectBase* val, const std::string& comment, bool warnExists) {
00165     storage_t::iterator it=dict.find(name);
00166     if(it!=dict.end()) {
00167       //found exact name match
00168       if(val==it->second)
00169         return; // same val reference already registered
00170       if(warnExists) {
00171         cerr << "Warning: new entry ("<<name<<","<<val<<") conflicted with previous entry ("<<name<<","<<(it->second)<<")"<<endl;
00172         cerr << "         (use setEntry() if you expect you might need to overwrite)" << endl;
00173       }
00174       removeEntry(name);
00175       //fall through to add new val
00176     } else {
00177       //perhaps there's a sub-dictionary
00178       string::size_type p;
00179       it=getSubEntry(name,p);
00180       if(it!=dict.end()) {
00181         //found a matching sub-collection, have it find the rest recursively
00182         if(Dictionary* d=dynamic_cast<Dictionary*>(it->second)) {
00183           d->addEntry(name.substr(p+1),val,comment,warnExists);
00184         } else {
00185           Collection* c=dynamic_cast<Collection*>(it->second);
00186           c->addEntry(name.substr(p+1),val,comment);
00187         }
00188         return;
00189       }
00190       //if still here, no sub-collection, fall through to add new entry
00191     }
00192     dict[name]=val;
00193     if(comment.size()>0)
00194       comments[name]=comment;
00195     takeObject(name,val);
00196     fireEntryAdded(*val);
00197   }
00198   
00199   
00200   void Dictionary::removeEntry(const std::string& name) {
00201     storage_t::iterator it=dict.find(name);
00202     if(it!=dict.end()) {
00203       //found exact name match
00204       ObjectBase* obj=it->second;
00205       dict.erase(it);
00206       fireEntryRemoved(*obj);
00207     } else {
00208       //perhaps there's a sub-dictionary
00209       string::size_type p;
00210       it=getSubEntry(name,p);
00211       if(it!=dict.end()) {
00212         //found a matching sub-collection, have it find the rest recursively
00213         Collection* d=dynamic_cast<Collection*>(it->second);
00214         d->removeEntry(name.substr(p+1));
00215       }
00216     }
00217   }
00218 
00219   ObjectBase* Dictionary::getEntry(const std::string& name) const {
00220     //do we have a key with this name?
00221     const_iterator it=dict.find(name);
00222     if(it!=dict.end())
00223       return it->second; //yes, return it
00224     
00225     //perhaps there's a sub-dictionary
00226     string::size_type p;
00227     it=getSubEntry(name,p);
00228     if(it!=dict.end()) {
00229       //found a matching sub-collection, have it find the rest recursively
00230       const Collection* d=dynamic_cast<const Collection*>(it->second);
00231       return d->getEntry(name.substr(p+1));
00232     }
00233     
00234     //got nothin'
00235     return NULL;
00236   }
00237 
00238   void Dictionary::setComment(const std::string& name, const std::string& comment) {
00239     storage_t::iterator it=dict.find(name);
00240     if(it==dict.end()) {
00241       //perhaps there's a sub-dictionary
00242       string::size_type p;
00243       it=getSubEntry(name,p);
00244       if(it!=dict.end()) {
00245         //found a matching sub-collection, have it find the rest recursively
00246         Collection* d=dynamic_cast<Collection*>(it->second);
00247         d->setComment(name.substr(p+1),comment);
00248         return;
00249       }
00250     }
00251     if(comment.size()>0)
00252       comments[name]=comment;
00253     else
00254       comments.erase(name);
00255   }
00256 
00257   const std::string& Dictionary::getComment(const std::string& name) const {
00258     storage_t::const_iterator it=dict.find(name);
00259     if(it!=dict.end()) {
00260       //found exact name match
00261       comments_t::const_iterator cit=comments.find(name);
00262       return (cit!=comments.end()) ? cit->second : emptyStr();
00263     } else {
00264       //perhaps there's a sub-dictionary
00265       string::size_type p;
00266       it=getSubEntry(name,p);
00267       if(it!=dict.end()) {
00268         //found a matching sub-collection, have it find the rest recursively
00269         const Collection* d=dynamic_cast<const Collection*>(it->second);
00270         return d->getComment(name.substr(p+1));
00271       }
00272       return emptyStr();
00273     }
00274   }
00275 
00276   void Dictionary::loadXML(xmlNode* node) {
00277     //check if our node has been set to NULL (invalid or not found)
00278     if(node==NULL)
00279       return;
00280     
00281     std::string comment;
00282     set<std::string> seen;
00283     //process children nodes
00284     for(xmlNode* cur = skipToElement(node->children,comment); cur!=NULL; cur = skipToElement(cur->next,comment)) {
00285             
00286       //find the next key node
00287       xmlNode * k=cur;
00288       if(xmlStrcmp(k->name, (const xmlChar *)"key"))
00289         throw bad_format(k,"Dictionary format error: expect data in pairs of key and value (two values found in a row)");
00290       cur=skipToElement(cur->next);
00291       
00292       //find the following value (non-key) node
00293       xmlNode * v=cur;
00294       if(v==NULL)
00295         throw bad_format(cur,"Dictionary format error: expect data in pairs of key and value (dictionary ended with hanging key)");
00296       if(!xmlStrcmp(v->name, (const xmlChar *)"key"))
00297         throw bad_format(v,"Dictionary format error: expect data in pairs of key and value (two keys found in a row)");
00298       
00299       //find corresponding entry
00300       xmlChar* cont=xmlNodeGetContent(k);
00301       string key=(const char*)cont;
00302       xmlFree(cont);
00303       seen.insert(key);
00304       loadXMLNode(key,v,comment);
00305     }
00306     if(trimExtraLoad && seen.size()!=size()) {
00307       set<std::string> rem;
00308       for(const_iterator it=begin(); it!=end(); ++it) {
00309         if(seen.find(it->first)==seen.end())
00310           rem.insert(it->first);
00311       }
00312       for(set<std::string>::const_iterator it=rem.begin(); it!=rem.end(); ++it)
00313         removeEntry(*it);
00314     }
00315   }
00316   
00317   void Dictionary::saveXML(xmlNode* node) const {
00318     //check if our node has been set to NULL (invalid or not found)
00319     if(node==NULL)
00320       return;
00321     
00322     //set the type of the current node
00323     xmlNodeSetName(node,(const xmlChar*)"dict");
00324     
00325     // we'll use this to keep track of which nodes were already present, so we'll
00326     // know which ones were missing for which we need to make new nodes
00327     storage_t seen;
00328 
00329     //find the depth of the target node in the xml tree to maintain proper indentation
00330     std::string perIndent("    ");
00331     std::string indentStr;
00332     for(xmlNode* cur=node->parent; cur!=NULL; cur=cur->parent) {
00333       if((void*)cur==(void*)node->doc) { //if we hit the document node, discount it and we're done
00334         if(indentStr.size()>0)
00335           indentStr=indentStr.substr(0,indentStr.size()-perIndent.size());
00336         break;
00337       }
00338       indentStr+=perIndent;
00339     }
00340     
00341     //This will hold any comments found between elements -- if no comment is found, a new one may be added
00342     std::string comment;
00343 
00344     //process children nodes
00345     xmlNode* prev=node->children;
00346     for(xmlNode* cur = skipToElement(node->children,comment); cur!=NULL; cur = skipToElement(cur,comment)) {
00347       
00348       //find the next key node
00349       xmlNode * k=cur;
00350       if(xmlStrcmp(k->name, (const xmlChar *)"key"))
00351         throw bad_format(k,"Dictionary format error: expect data in pairs of key and value (two values found in a row)");
00352       cur=skipToElement(cur->next);
00353       
00354       //find the following value (non-key) node
00355       xmlNode * v=cur;
00356       if(v==NULL)
00357         throw bad_format(cur,"Dictionary format error: expect data in pairs of key and value (dictionary ended with hanging key)");
00358       if(!xmlStrcmp(v->name, (const xmlChar *)"key"))
00359         throw bad_format(v,"Dictionary format error: expect data in pairs of key and value (two keys found in a row)");
00360       
00361       //find corresponding entry
00362       xmlChar* cont=xmlNodeGetContent(k);
00363       string key=(const char*)cont;
00364       xmlFree(cont);
00365       storage_t::const_iterator it=dict.find(key);
00366       if(it==dict.end()) {
00367         cur=xNodeGetNextNode(cur);
00368         if(trimExtraSave) {
00369           while(prev!=cur) {
00370             xmlNode* n=prev;
00371             prev=xNodeGetNextNode(prev);
00372             xmlUnlinkNode(n);
00373             xmlFreeNode(n);
00374           }
00375         } else {
00376           if(warnUnused)
00377             cerr << "Warning: saving over existing plist dictionary, key '" << key << "' does not match a registered variable.  Ignoring..." << endl;
00378         }
00379         prev=cur;
00380         continue;
00381       }
00382       if(comment.size()==0) {
00383         bool isSub=dynamic_cast<Collection*>(it->second);
00384         if(isSub) {
00385           xmlAddPrevSibling(k,xmlNewText((const xmlChar*)"\n"));
00386           xmlAddPrevSibling(k,xmlNewComment((const xmlChar*)("======== "+it->first+" ========").c_str()));
00387         }
00388         comments_t::const_iterator cit=comments.find(key);
00389         if(cit!=comments.end()) {
00390           if(isSub || cit->second.substr(0,key.size())==key)
00391             comment=cit->second;
00392           else //if not a sub-dictionary, and comment doesn't already start with entry name, prepend entry name
00393             comment=key+": "+cit->second;
00394           xmlAddPrevSibling(k,xmlNewText((const xmlChar*)"\n"));
00395           xmlAddPrevSibling(k,xmlNewComment((const xmlChar*)comment.c_str()));
00396           xmlAddPrevSibling(k,xmlNewText((const xmlChar*)("\n"+indentStr).c_str()));
00397         }
00398       }
00399       it->second->saveXML(v);
00400       seen.insert(*it);
00401       prev=cur=xNodeGetNextNode(cur);
00402     }
00403 
00404     if(seen.size()!=dict.size()) {
00405       // the main dictionary has entries that weren't seen... find which ones
00406       // if needed, this could be made faster (O(n) vs. current O(n lg n)) by assuming the maps
00407       // are sorted and moving two iterators through together instead of repeated find()'s
00408       for(storage_t::const_iterator it=dict.begin(); it!=dict.end(); ++it) {
00409         if(seen.find(it->first)==seen.end()) {
00410           //we didn't see this node in the existing xml tree, have to add a new node pair for it
00411           bool isSub=dynamic_cast<Collection*>(it->second);
00412           if(isSub) {
00413             xmlAddChild(node,xmlNewText((const xmlChar*)"\n"));
00414             xmlAddChild(node,xmlNewComment((const xmlChar*)("======== "+it->first+" ========").c_str()));
00415           }
00416           comments_t::const_iterator cit=comments.find(it->first);
00417           if(cit!=comments.end()) {
00418             if(isSub || cit->second.substr(0,it->first.size())==it->first)
00419               comment=cit->second;
00420             else
00421               comment=it->first+": "+cit->second;
00422             xmlAddChild(node,xmlNewText((const xmlChar*)"\n"));
00423             xmlAddChild(node,xmlNewComment((const xmlChar*)comment.c_str()));
00424           }
00425           xmlAddChild(node,xmlNewText((const xmlChar*)("\n"+indentStr).c_str()));
00426           xmlNode* k=xmlNewChild(node,NULL,(const xmlChar*)"key",(const xmlChar*)it->first.c_str());
00427           if(k==NULL)
00428             throw bad_format(node,"Error: plist Dictionary xml error on saving key");
00429           xmlAddChild(node,xmlNewText((const xmlChar*)" "));
00430           xmlNode* v=xmlNewChild(node,NULL,(const xmlChar*)"",NULL);
00431           if(v==NULL)
00432             throw bad_format(node,"Error: plist Dictionary xml error on saving value");
00433           if(indentStr.size()>=perIndent.size())
00434             xmlAddChild(node,xmlNewText((const xmlChar*)("\n"+indentStr.substr(perIndent.size())).c_str()));
00435           else
00436             xmlAddChild(node,xmlNewText((const xmlChar*)("\n")));
00437           it->second->saveXML(v);
00438         }
00439       }
00440     }
00441   }
00442 
00443   std::string Dictionary::toString() const {
00444     stringstream s;
00445     s << *this;
00446     return s.str();
00447   }
00448   
00449   //! implements the clone function for Dictionaries
00450   PLIST_CLONE_IMP(Dictionary,new Dictionary(*this));
00451 
00452   unsigned int Dictionary::getLongestKeyLen() const {
00453     size_t longest=0;
00454     size_t seplen=subCollectionSep().size();
00455     for(Dictionary::const_iterator it=begin(); it!=end(); ++it) {
00456       size_t cur=it->first.size();
00457       if(Collection* dp=dynamic_cast<Collection*>(it->second))
00458         cur+=getLongestKeyLenOther(*dp)+seplen;
00459       longest=std::max(longest,cur);
00460     }
00461     return longest;
00462   }
00463   
00464   Dictionary::iterator Dictionary::getSubEntry(const std::string& name, std::string::size_type& seppos) {
00465     seppos=name.find(subCollectionSep());
00466     if(seppos==string::npos)
00467       return dict.end(); //no '.'s found -- go away
00468     iterator it=dict.find(name.substr(0,seppos));
00469     if(it==dict.end())
00470       return dict.end(); //no entry matching prefix -- go away
00471     const Collection* d=dynamic_cast<const Collection*>(it->second);
00472     if(d==NULL)
00473       return dict.end(); //matching prefix is not a collection -- go away
00474     return it;
00475   }
00476   Dictionary::const_iterator Dictionary::getSubEntry(const std::string& name, std::string::size_type& seppos) const {
00477     seppos=name.find(subCollectionSep());
00478     if(seppos==string::npos)
00479       return dict.end(); //no '.'s found -- go away
00480     const_iterator it=dict.find(name.substr(0,seppos));
00481     if(it==dict.end())
00482       return dict.end(); //no entry matching prefix -- go away
00483     const Collection* d=dynamic_cast<const Collection*>(it->second);
00484     if(d==NULL)
00485       return dict.end(); //matching prefix is not a collection -- go away
00486     return it;
00487   }
00488     
00489   std::ostream& operator<<(std::ostream& os, const Dictionary& d) {
00490     unsigned int longest=std::max(Collection::getLongestKeyLenOther(d),static_cast<unsigned int>(os.width()));
00491     unsigned int seplen=Collection::subCollectionSep().size();
00492     for(Dictionary::storage_t::const_iterator it=d.dict.begin(); it!=d.dict.end(); ++it) {
00493       if(Collection* dp=dynamic_cast<Collection*>(it->second)) {
00494         stringstream ss;
00495         ss << setw(longest-it->first.size()-seplen) << *dp;
00496         string line;
00497         for(getline(ss,line); ss; getline(ss,line))
00498           os << it->first << Collection::subCollectionSep() << line << endl;
00499       } else {
00500         os << left << setw(longest) << it->first << " = " << *it->second << endl;
00501       }
00502     }
00503     return os;
00504   }
00505   
00506 
00507 
00508   
00509   void Array::setEntry(size_t index, ObjectBase& val, bool warnExists/*=false*/) {
00510     if(index==size()) {
00511       arr.push_back(&val);
00512       fireEntryAdded(val);
00513     } else {
00514       if(arr[index]==&val)
00515         return;
00516       if(warnExists) {
00517         cerr << "Warning: new entry "<<index<<" ("<<val<<") conflicted with previous entry "<<index<<" ("<<(*arr[index])<<")"<<endl;
00518         cerr << "         (use setEntry(...,false) if you expect you might need to overwrite)" << endl;
00519       }
00520       arr[index]=&val;
00521       fireEntriesChanged();
00522     }
00523   }
00524   void Array::addEntry(size_t index, ObjectBase& val, const std::string& comment/*=""*/) {
00525     if(index==size()) {
00526       arr.push_back(&val);
00527     } else {
00528       storage_t::iterator it=arr.begin();
00529       advance(it,index);
00530       arr.insert(it,&val);
00531     }
00532     if(comment.size()>0)
00533       setComment(index,comment);
00534     fireEntryAdded(val);
00535   }
00536   void Array::setEntry(size_t index, ObjectBase* val, bool warnExists/*=false*/) {
00537     if(index>size())
00538       throw bad_format(NULL,"Error: attempted to setEntry() past end of Array");
00539     else if(index==size()) {
00540       arr.push_back(val);
00541       fireEntryAdded(*val);
00542     } else {
00543       if(arr[index]==val)
00544         return;
00545       if(warnExists) {
00546         cerr << "Warning: new entry "<<index<<" ("<<val<<") conflicted with previous entry "<<index<<" ("<<(*arr[index])<<")"<<endl;
00547         cerr << "         (use setEntry(...,false) if you expect you might need to overwrite)" << endl;
00548       }
00549       std::set<ObjectBase*>::iterator it=myRef.find(arr[index]);
00550       if(it!=myRef.end()) {
00551         myRef.erase(*it);
00552         delete arr[index];
00553       }
00554       arr[index]=val;
00555       takeObject(index,val);
00556       fireEntriesChanged();
00557     }
00558   }
00559   void Array::addEntry(size_t index, ObjectBase* val, const std::string& comment/*=""*/) {
00560     if(index>size())
00561       throw bad_format(NULL,"Error: attempted to setEntry() past end of Array");
00562     else if(index==size()) {
00563       arr.push_back(val);
00564     } else {
00565       storage_t::iterator it=arr.begin();
00566       advance(it,index);
00567       arr.insert(it,val);
00568     }
00569     takeObject(index,val);
00570     if(comment.size()>0)
00571       setComment(index,comment);
00572     fireEntryAdded(*val);
00573   }
00574   
00575   void Array::removeEntry(size_t index) {
00576     storage_t::iterator it=arr.begin();
00577     advance(it,index);
00578     ObjectBase* obj=*it;
00579     arr.erase(it);
00580     fireEntryRemoved(*obj);
00581   }
00582   
00583 
00584   void Array::setEntry(const std::string& name, ObjectBase& val, bool warnExists/*=false*/) {
00585     size_t index=getIndex(name);
00586     if(index>size()) {
00587       string::size_type p;
00588       const_iterator it=getSubEntry(name,p);
00589       if(it!=arr.end()) {
00590         Collection * d=dynamic_cast<Collection*>(*it);
00591         d->setEntry(name.substr(p+1),val,warnExists);
00592       } else
00593         throw bad_format(NULL,("Array::setEntry(name,val,warn) was called with an invalid numeric name:" + name).c_str());
00594     } else {
00595       setEntry(index,val,warnExists);
00596     }
00597   }
00598   void Array::addEntry(const std::string& name, ObjectBase& val, const std::string& comment/*=""*/) {
00599     size_t index=getIndex(name);
00600     if(index>size()) {
00601       string::size_type p;
00602       const_iterator it=getSubEntry(name,p);
00603       if(it!=arr.end()) {
00604         Collection * d=dynamic_cast<Collection*>(*it);
00605         d->addEntry(name.substr(p+1),val,comment);
00606       } else
00607         throw bad_format(NULL,("Array::addEntry(name,val,comment) was called with an invalid numeric name:" + name).c_str());
00608     } else {
00609       addEntry(index,val,comment);
00610     }
00611   }
00612   void Array::setEntry(const std::string& name, ObjectBase* val, bool warnExists/*=false*/) {
00613     size_t index=getIndex(name);
00614     if(index>size()) {
00615       string::size_type p;
00616       const_iterator it=getSubEntry(name,p);
00617       if(it!=arr.end()) {
00618         Collection * d=dynamic_cast<Collection*>(*it);
00619         d->setEntry(name.substr(p+1),val,warnExists);
00620       } else
00621         throw bad_format(NULL,("Array::setEntry(name,*val,warn) was called with an invalid numeric name:" + name).c_str());
00622     } else {
00623       setEntry(index,val,warnExists);
00624     }
00625   }
00626   void Array::addEntry(const std::string& name, ObjectBase* val, const std::string& comment/*=""*/) {
00627     size_t index=getIndex(name);
00628     if(index>size()) {
00629       string::size_type p;
00630       const_iterator it=getSubEntry(name,p);
00631       if(it!=arr.end()) {
00632         Collection * d=dynamic_cast<Collection*>(*it);
00633         d->addEntry(name.substr(p+1),val,comment);
00634       } else
00635         throw bad_format(NULL,("Array::addEntry(name,*val,comment) was called with an invalid numeric name:" + name).c_str());
00636     } else {
00637       addEntry(index,val,comment);
00638     }
00639   }
00640   
00641   void Array::removeEntry(const std::string& name) {
00642     size_t index=getIndex(name);
00643     if(index>size()) {
00644       string::size_type p;
00645       iterator it=getSubEntry(name,p);
00646       if(it!=arr.end()) {
00647         Collection * d=dynamic_cast<Collection*>(*it);
00648         d->removeEntry(name.substr(p+1));
00649       } else
00650         throw bad_format(NULL,("Array::removeEntry(name) was called with an invalid numeric name:" + name).c_str());
00651     } else {
00652       removeEntry(index);
00653     }
00654   }
00655   ObjectBase* Array::getEntry(const std::string& name) const {
00656     size_t index=getIndex(name);
00657     if(index>size()) {
00658       string::size_type p;
00659       const_iterator it=getSubEntry(name,p);
00660       if(it!=arr.end()) {
00661         const Collection * d=dynamic_cast<const Collection*>(*it);
00662         return d->getEntry(name.substr(p+1));
00663       } else
00664         throw bad_format(NULL,("Array::getEntry(name) was called with an invalid numeric name:" + name).c_str());
00665     } else {
00666       return &getEntry(index);
00667     }
00668   }
00669     
00670   void Array::setComment(size_t index, const std::string& comment) {
00671     if(comment.size()>0)
00672       comments[index]=comment;
00673     else
00674       comments.erase(index);
00675   }
00676   
00677   const std::string& Array::getComment(size_t index) const {
00678     comments_t::const_iterator it=comments.find(index);
00679     if(it==comments.end())
00680       return emptyStr();
00681     else
00682       return it->second;
00683   }
00684   
00685   void Array::setComment(const std::string& name, const std::string& comment) {
00686     size_t index=getIndex(name);
00687     if(index>size()) {
00688       string::size_type p;
00689       const_iterator it=getSubEntry(name,p);
00690       if(it!=arr.end()) {
00691         Collection * d=dynamic_cast<Collection*>(*it);
00692         d->setComment(name.substr(p+1),comment);
00693       } else
00694         throw bad_format(NULL,("Array::setComment(name,comment) was called with an invalid numeric name:" + name).c_str());
00695     } else {
00696       setComment(index,comment);
00697     }
00698   }
00699   
00700   const std::string& Array::getComment(const std::string& name) const {
00701     size_t index=getIndex(name);
00702     if(index>size()) {
00703       string::size_type p;
00704       const_iterator it=getSubEntry(name,p);
00705       if(it!=arr.end()) {
00706         const Collection * d=dynamic_cast<const Collection*>(*it);
00707         return d->getComment(name.substr(p+1));
00708       } else
00709         throw bad_format(NULL,("Array::getComment(name) was called with an invalid numeric name:" + name).c_str());
00710     } else {
00711       return getComment(index);
00712     }
00713   }
00714   
00715   void Array::loadXML(xmlNode* node) {
00716     //check if our node has been set to NULL (invalid or not found)
00717     if(node==NULL)
00718       return;
00719     
00720     std::string comment;
00721     unsigned int i=0;
00722     for(xmlNode* cur = skipToElement(node->children,comment); cur!=NULL; cur = skipToElement(cur->next,comment)) {
00723       if(!loadXMLNode(i++, cur, comment))
00724          break;
00725     }
00726     if(trimExtraLoad) {
00727       while(i<size())
00728         removeEntry(size()-1);
00729     } 
00730   }
00731   
00732   void Array::saveXML(xmlNode* node) const {
00733     //check if our node has been set to NULL (invalid or not found)
00734     if(node==NULL)
00735       return;
00736     
00737     //set the type of the current node
00738     xmlNodeSetName(node,(const xmlChar*)"array");
00739     
00740     //find the depth of the target node in the xml tree to maintain proper indentation
00741     std::string perIndent("    ");
00742     std::string indentStr;
00743     for(xmlNode* cur=node->parent; cur!=NULL; cur=cur->parent) {
00744       if((void*)cur==(void*)node->doc) { //if we hit the document node, discount it and we're done
00745         if(indentStr.size()>0)
00746           indentStr=indentStr.substr(0,indentStr.size()-perIndent.size());
00747         break;
00748       }
00749       indentStr+=perIndent;
00750     }
00751     std::string parentIndent;
00752     if(indentStr.size()>=perIndent.size())
00753       parentIndent=indentStr.substr(perIndent.size());
00754     
00755     //This will hold any comments found between elements -- if no comment is found, a new one may be added
00756     std::string comment;
00757     
00758     //This will be the index of the item we're loading next
00759     unsigned int i=0;
00760     
00761     //process children nodes
00762     xmlNode * prev=node->children;
00763     for(xmlNode* cur = skipToElement(node->children,comment); cur!=NULL; cur = skipToElement(cur,comment)) {
00764       
00765       if(i==arr.size()) {
00766         if(trimExtraSave) {
00767           while(prev!=NULL) {
00768             xmlNode* n=prev;
00769             prev=xNodeGetNextNode(prev);
00770             xmlUnlinkNode(n);
00771             xmlFreeNode(n);
00772           }
00773         } else {
00774           if(warnUnused)
00775             cerr << "Warning: plist::Array ignoring extraneous items in destination during save..." << endl;
00776         }
00777         break;
00778       }
00779       if(comment.size()==0) {
00780         comments_t::const_iterator cit=comments.find(i);
00781         if(cit!=comments.end()) {
00782           char buf[20];
00783           unsigned int len;
00784           snprintf(buf,20,"%u%n",i,&len);
00785           if(/*isSub ||*/ strncmp(cit->second.c_str(),buf,len)==0)
00786             comment=cit->second;
00787           else { //if not a sub-dictionary, and comment doesn't already start with entry name, prepend entry name
00788             comment=buf;
00789             comment+=": "+cit->second;
00790           }
00791           xmlAddPrevSibling(cur,xmlNewText((const xmlChar*)"\n"));
00792           xmlAddPrevSibling(cur,xmlNewComment((const xmlChar*)comment.c_str()));
00793           xmlAddPrevSibling(cur,xmlNewText((const xmlChar*)("\n"+indentStr).c_str()));
00794         }
00795       }
00796       arr[i]->saveXML(cur);
00797       prev=cur=xNodeGetNextNode(cur);
00798     }
00799     
00800     bool hadUnsaved = (i<arr.size());
00801     for(; i<arr.size(); ++i) {
00802       comments_t::const_iterator cit=comments.find(i);
00803       if(cit!=comments.end()) {
00804         char buf[20];
00805         unsigned int len;
00806         snprintf(buf,20,"%u%n",i,&len);
00807         if(/*isSub ||*/ strncmp(cit->second.c_str(),buf,len)==0)
00808           comment=cit->second;
00809         else { //if not a sub-dictionary, and comment doesn't already start with entry name, prepend entry name
00810           comment=buf;
00811           comment+=": "+cit->second;
00812         }
00813         xmlAddChild(node,xmlNewText((const xmlChar*)("\n"+parentIndent).c_str()));
00814         xmlAddChild(node,xmlNewComment((const xmlChar*)comment.c_str()));
00815       }
00816       xmlAddChild(node,xmlNewText((const xmlChar*)("\n"+indentStr).c_str()));
00817       xmlNode* v=xmlNewChild(node,NULL,(const xmlChar*)"",NULL);
00818       if(v==NULL)
00819         throw bad_format(node,"Error: plist Array xml error on saving value");
00820       arr[i]->saveXML(v);
00821     }
00822     if(hadUnsaved)
00823       xmlAddChild(node,xmlNewText((const xmlChar*)("\n"+parentIndent).c_str()));
00824   }
00825   
00826   std::string Array::toString() const {
00827     stringstream s;
00828     s << *this;
00829     return s.str();
00830   }
00831   
00832   //! implements the clone function for Array
00833   PLIST_CLONE_IMP(Array,new Array(*this));
00834 
00835   size_t Array::getIndex(const std::string& name) const {
00836     char * endp=0;
00837     long index=strtol(name.c_str(),&endp,0);
00838     if(index<0)
00839       return (size_t)-1;
00840       //throw bad_format(NULL,"Array passed negative index encoded in string: "+name);
00841     /*
00842     if(*endp=='.') {
00843       const Collection* d=dynamic_cast<const Collection*>(arr[index]);
00844       if(d==NULL)
00845         return (size_t)-1; //matching prefix is not a dictionary -- go away
00846       
00847       //found a matching sub-dictionary, have it find the rest recursively
00848       return index;
00849     }*/
00850     if(*endp!='\0')
00851       return (size_t)-1;
00852     //throw bad_format(NULL,"Array::setEntry(name,val) was called with a non-numeric name");
00853     return index;
00854   }
00855     
00856   unsigned int Array::getLongestKeyLen() const {
00857     size_t longest=0;
00858     size_t seplen=subCollectionSep().size();
00859     for(size_t i=0; i<size(); ++i) {
00860       size_t cur=snprintf(NULL,0,"%lu",static_cast<unsigned long>(i));
00861       if(Collection* dp=dynamic_cast<Collection*>(arr[i]))
00862         cur+=getLongestKeyLenOther(*dp)+seplen;
00863       longest=std::max(longest,cur);
00864     }
00865     return longest;
00866   }
00867   
00868   Array::iterator Array::getSubEntry(const std::string& name, std::string::size_type& seppos) {
00869     seppos=name.find(subCollectionSep());
00870     if(seppos==string::npos)
00871       return arr.end(); //no '.'s found -- go away
00872     size_t index=getIndex(name.substr(0,seppos));
00873     if(index>=size())
00874       return arr.end(); //no entry matching prefix -- go away
00875     iterator it=arr.begin();
00876     advance(it,index);
00877     const Collection* d=dynamic_cast<const Collection*>(*it);
00878     if(d==NULL)
00879       return arr.end(); //matching prefix is not a collection -- go away
00880     return it;
00881   }
00882   Array::const_iterator Array::getSubEntry(const std::string& name, std::string::size_type& seppos) const {
00883     seppos=name.find(subCollectionSep());
00884     if(seppos==string::npos)
00885       return arr.end(); //no '.'s found -- go away
00886     size_t index=getIndex(name.substr(0,seppos));
00887     if(index>=size())
00888       return arr.end(); //no entry matching prefix -- go away
00889     const_iterator it=arr.begin();
00890     advance(it,index);
00891     const Collection* d=dynamic_cast<const Collection*>(*it);
00892     if(d==NULL)
00893       return arr.end(); //matching prefix is not a collection -- go away
00894     return it;
00895   }
00896   std::ostream& operator<<(std::ostream& os, const Array& d) {
00897     unsigned int longest=std::max(Collection::getLongestKeyLenOther(d),static_cast<unsigned int>(os.width()));
00898     unsigned int seplen=Collection::subCollectionSep().size();
00899     for(unsigned long i=0; i<d.arr.size(); ++i) {
00900       if(Collection* dp=dynamic_cast<Collection*>(d.arr[i])) {
00901         stringstream ss;
00902         ss << setw(longest-snprintf(NULL,0,"%lu",i)-seplen) << *dp;
00903         string line;
00904         for(getline(ss,line); ss; getline(ss,line))
00905           os << i << Collection::subCollectionSep() << line << endl;
00906       } else {
00907         os << left << setw(longest) << i << " = " << *d.arr[i] << endl;
00908       }
00909     }//Array::storage_t::const_iterator it=d.arr.begin(); it!=d.arr.end(); ++it) {
00910     return os;
00911   }
00912   
00913   void Dictionary::clear() {
00914     for(std::set<ObjectBase*>::iterator it=myRef.begin(); it!=myRef.end(); ++it)
00915       delete *it;
00916     myRef.clear();
00917     dict.clear();
00918     fireEntriesChanged();
00919   }
00920   
00921   void Dictionary::takeObject(const std::string& /*name*/, ObjectBase* obj) {
00922     myRef.insert(obj);
00923   }
00924 
00925   void Dictionary::fireEntryRemoved(ObjectBase& val) {
00926     Dictionary::fireEntryRemoved(val);
00927     std::set<ObjectBase*>::iterator it=myRef.find(&val);
00928     if(it!=myRef.end()) {
00929       delete &val;
00930       myRef.erase(it);
00931     }
00932   }
00933   
00934   void Dictionary::cloneMyRef() {
00935     for(iterator dit=dict.begin(); dit!=dict.end(); ++dit) {
00936       std::set<ObjectBase*>::iterator rit=myRef.find(dit->second);
00937       if(rit!=myRef.end()) {
00938         myRef.erase(rit);
00939         myRef.insert(dit->second=dynamic_cast<ObjectBase*>((dit->second)->clone()));
00940       }
00941     }
00942     
00943     //slower implementation, but can handle multiple pointers to the same instance (which we don't support elsewhere, so no point in doing it)
00944     /*
00945      std::set<ObjectBase*> ns;
00946     for(std::set<ObjectBase*>::iterator it=myRef.begin(); it!=myRef.end(); ++it) {
00947       ObjectBase* n=dynamic_cast<ObjectBase*>((*it)->clone());
00948       bool used=false;
00949       for(iterator dit=dict.begin(); dit!=dict.end(); ++dit) {
00950         if(*it==dit->second) {
00951           dit->second=n;
00952           used=true;
00953         }
00954       }
00955       if(!used) {
00956         cerr << "Warning: dictionary claims control over pointer not found in dictionary" << endl;
00957         delete n;
00958       } else
00959         ns.insert(n);
00960     }
00961     myRef=ns;
00962     */
00963   }
00964   
00965   void Array::clear() {
00966     for(std::set<ObjectBase*>::iterator it=myRef.begin(); it!=myRef.end(); ++it)
00967       delete *it;
00968     myRef.clear();
00969     arr.clear();
00970     fireEntriesChanged();
00971   }
00972   
00973   void Array::takeObject(size_t /*index*/, ObjectBase* obj) {
00974     myRef.insert(obj);
00975   }
00976   
00977   void Array::fireEntryRemoved(ObjectBase& val) {
00978     Array::fireEntryRemoved(val);
00979     std::set<ObjectBase*>::iterator it=myRef.find(&val);
00980     if(it!=myRef.end()) {
00981       delete &val;
00982       myRef.erase(it);
00983     }
00984   }
00985   
00986   void Array::cloneMyRef() {
00987     for(iterator dit=arr.begin(); dit!=arr.end(); ++dit) {
00988       std::set<ObjectBase*>::iterator rit=myRef.find(*dit);
00989       if(rit!=myRef.end()) {
00990         myRef.erase(rit);
00991         myRef.insert(*dit=dynamic_cast<ObjectBase*>((*dit)->clone()));
00992       }
00993     }
00994     
00995     //slower implementation, but can handle multiple pointers to the same instance (which we don't support elsewhere, so no point in doing it)
00996     /*
00997     std::set<ObjectBase*> ns;
00998     for(std::set<ObjectBase*>::iterator it=myRef.begin(); it!=myRef.end(); ++it) {
00999       ObjectBase* n=dynamic_cast<ObjectBase*>((*it)->clone());
01000       bool used=false;
01001       for(iterator dit=arr.begin(); dit!=arr.end(); ++dit) {
01002         if(*it==*dit) {
01003           *dit=n;
01004           used=true;
01005         }
01006       }
01007       if(!used) {
01008         cerr << "Warning: dictionary claims control over pointer not found in dictionary" << endl;
01009         delete n;
01010       } else
01011         ns.insert(n);
01012     }
01013     myRef=ns;
01014     */
01015   }
01016   
01017   bool Dictionary::loadXMLNode(const std::string& key, xmlNode* val, const std::string& comment) {
01018     storage_t::const_iterator it=dict.find(key);
01019     if(it!=dict.end()) {
01020       //found pre-existing entry
01021       try {
01022         //it's reasonable to assume that in common usage, the same type will be used each time
01023         //so let's try to load into the current entry as is
01024         it->second->loadXML(val);
01025         if(comment.size()>0)
01026           setComment(key,comment);
01027         return true;
01028       } catch(const bad_format& ex) {
01029         //apparently that didn't work, let's try a fresh load using the polymorphic loader (fall through below)
01030         removeEntry(key);
01031       }
01032     } else if(!trimExtraLoad) {
01033       if(warnUnused)
01034         std::cerr << "Warning: reading plist dictionary, key '" << key << "' does not match a registered variable.  Ignoring..." << std::endl;
01035       return false;
01036     }
01037     ObjectBase * obj=plist::loadXML(val);
01038     if(obj==NULL)
01039       throw bad_format(val,"Dictionary encountered an unknown value type");
01040     addEntry(key,obj,comment);
01041     return true;
01042   }
01043   
01044   bool Array::loadXMLNode(size_t index, xmlNode* val, const std::string& comment) {
01045     if(index<size()) {
01046       //have pre-existing entry
01047       try {
01048         //it's reasonable to assume that in common usage, the same type will be used each time
01049         //so let's try to load into the current entry as is
01050         arr[index]->loadXML(val);
01051         if(comment.size()>0)
01052           setComment(index,comment);
01053         return true;
01054       } catch(const bad_format& ex) {
01055         //apparently that didn't work, let's try a fresh load using the polymorphic loader (fall through below)
01056         removeEntry(index);
01057       }
01058     } else if(!trimExtraLoad) {
01059       if(warnUnused)
01060         cerr << "Warning: plist::Array ran out of registered items (" << size() << ") during load.  Ignoring extraneous items from source..." << endl;
01061       return false;
01062     }
01063     ObjectBase * obj=plist::loadXML(val);
01064     if(obj==NULL)
01065       throw bad_format(val,"Array encountered an unknown value type");
01066     addEntry(index,obj,comment);
01067     return true;
01068   }
01069   
01070 } //namespace plist
01071 
01072 
01073 /*! @file
01074  * @brief 
01075  * @author Ethan Tira-Thompson (ejt) (Creator)
01076  *
01077  * $Author: ejt $
01078  * $Name: tekkotsu-3_0 $
01079  * $Revision: 1.5 $
01080  * $State: Exp $
01081  * $Date: 2006/09/16 06:01:41 $
01082  */

Tekkotsu v3.0
Generated Wed Oct 4 00:03:45 2006 by Doxygen 1.4.7