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) const {
00017     if(l!=NULL) {
00018       if(collectionListeners==NULL)
00019         collectionListeners=new std::set<CollectionListener*>;
00020       collectionListeners->insert(l);
00021     }
00022   }
00023     
00024   void Collection::removeCollectionListener(CollectionListener* l) const {
00025     if(collectionListeners==NULL)
00026       return;
00027     std::set<CollectionListener*>::iterator it=collectionListeners->find(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) const {
00038     if(l==NULL)
00039       return false;
00040     if(collectionListeners==NULL)
00041       return false;
00042     std::set<CollectionListener*>::iterator it=collectionListeners->find(l);
00043     return it!=collectionListeners->end();
00044   }
00045     
00046   long Collection::toLong() const { throw std::runtime_error("Unable to cast collection to integer value"); }
00047   double Collection::toDouble() const { throw std::runtime_error("Unable to cast collection to floating point value"); }
00048 
00049   void Collection::fireEntryAdded(ObjectBase& val) {
00050     if(collectionListeners==NULL)
00051       return;
00052     // copy primitive listeners so we aren't screwed if a listener is removed during processing, particularly if it's the *last* listener
00053     std::set<CollectionListener*> pls=*collectionListeners;
00054     for(std::set<CollectionListener*>::const_iterator it=pls.begin(); collectionListeners!=NULL && it!=pls.end(); ++it) {
00055       // make sure current listener hasn't been removed
00056       if(collectionListeners->find(*it)!=collectionListeners->end())
00057         (*it)->plistCollectionEntryAdded(*this,val);
00058     }
00059   }
00060   
00061   void Collection::fireEntryRemoved(ObjectBase& val) {
00062     if(collectionListeners==NULL)
00063       return;
00064     // copy primitive listeners so we aren't screwed if a listener is removed during processing, particularly if it's the *last* listener
00065     std::set<CollectionListener*> pls=*collectionListeners;
00066     for(std::set<CollectionListener*>::const_iterator it=pls.begin(); collectionListeners!=NULL && it!=pls.end(); ++it) {
00067       // make sure current listener hasn't been removed
00068       if(collectionListeners->find(*it)!=collectionListeners->end())
00069         (*it)->plistCollectionEntryRemoved(*this,val);
00070     }
00071   }
00072   
00073   void Collection::fireEntriesChanged() {
00074     if(collectionListeners==NULL)
00075       return;
00076     // copy primitive listeners so we aren't screwed if a listener is removed during processing, particularly if it's the *last* listener
00077     std::set<CollectionListener*> pls=*collectionListeners;
00078     for(std::set<CollectionListener*>::const_iterator it=pls.begin(); collectionListeners!=NULL && it!=pls.end(); ++it) {
00079       // make sure current listener hasn't been removed
00080       if(collectionListeners->find(*it)!=collectionListeners->end())
00081         (*it)->plistCollectionEntriesChanged(*this);
00082     }
00083   }
00084   
00085   std::string Collection::getIndentationPrefix(xmlNode* node) {
00086     std::string indentStr;
00087     for(xmlNode* cur=node->parent; cur!=NULL; cur=cur->parent) {
00088       if((void*)cur==(void*)node->doc) { //if we hit the document node, discount it and we're done
00089         if(indentStr.size()>0)
00090           indentStr=indentStr.substr(0,indentStr.size()-perIndent().size());
00091         break;
00092       }
00093       indentStr+=perIndent();
00094     }
00095     return indentStr;
00096   }
00097   
00098   size_t Collection::getIndex(const std::string& name) {
00099     char * endp=0;
00100     long index=strtol(name.c_str(),&endp,0);
00101     if(index<0)
00102       return (size_t)-1;
00103     //throw bad_format(NULL,"Collection::getIndex passed negative index encoded in string: "+name);
00104     if(*endp!='\0')
00105       return (size_t)-1;
00106     //throw bad_format(NULL,"Collection::getIndex was called with a non-numeric value");
00107     return index;
00108   }
00109   
00110   bool DictionaryBase::removeEntry(const std::string& name) {
00111     storage_t::iterator it=dict.find(name);
00112     if(it==dict.end())
00113       return false;
00114     //still here, then we found exact name match
00115     ObjectBase* obj=it->second;
00116     dict.erase(it);
00117     comments.erase(name);
00118     fireEntryRemoved(*obj);
00119     return true;
00120   }
00121   
00122   bool DictionaryBase::renameEntry(const std::string& oldname, const std::string& newname) {
00123     storage_t::iterator oit=dict.find(oldname);
00124     if(oit==dict.end())
00125       return false;
00126     
00127     // check for previous inhabitant of the new name
00128     storage_t::iterator nit=dict.find(newname);
00129     if(nit!=dict.end()) {
00130       // we found exact name match on the new name -- remove previous entry
00131       ObjectBase* obj=nit->second;
00132       dict.erase(nit);
00133       comments.erase(newname);
00134       fireEntryRemoved(*obj);
00135     }
00136     
00137     ObjectBase* val=oit->second;
00138     dict.erase(oit);
00139     dict[newname]=val;
00140     
00141     // now move comment along too
00142     comments_t::iterator cit=comments.find(oldname);
00143     if(cit==comments.end()) { // no comment for item being moved...
00144       // any comments by a previous resident of the new name?
00145       cit = comments.find(newname);
00146       if(cit!=comments.end()) // if so, remove them
00147         comments.erase(cit);
00148     } else {
00149       // item being moved has a comment, bring it along...
00150       string com = cit->second;
00151       comments.erase(cit);
00152       comments[newname]=com;
00153     }
00154     fireEntriesChanged();
00155     return true;
00156   }
00157   
00158   bool DictionaryBase::swapEntry(const std::string& a, const std::string& b) {
00159     storage_t::iterator ait = dict.find(a);
00160     storage_t::iterator bit = dict.find(b);
00161     if(ait==dict.end() && bit==dict.end())
00162       return false;
00163     else if(ait==dict.end())
00164       return renameEntry(b,a);
00165     else if(bit==dict.end())
00166       return renameEntry(a,b);
00167     
00168     swap(ait->second,bit->second);
00169     
00170     // swap comments too
00171     comments_t::iterator acit = comments.find(a);
00172     comments_t::iterator bcit = comments.find(b);
00173     if(acit != comments.end()) {
00174       if(bcit != comments.end()) {
00175         // have comments for both
00176         swap(acit->second,bcit->second);
00177       } else {
00178         // only have a comment for a
00179         string com = acit->second;
00180         comments.erase(acit);
00181         comments[b]=com;
00182       }
00183     } else if(bcit != comments.end()) {
00184       // only have a comment for b
00185       string com = bcit->second;
00186       comments.erase(bcit);
00187       comments[a]=com;
00188     }
00189     fireEntriesChanged();
00190     return true;
00191   }
00192 
00193   ObjectBase* DictionaryBase::resolveEntry(const std::string& path) const {
00194     //do we have a key with this name?
00195     const_iterator it=dict.find(path);
00196     if(it!=dict.end())
00197       return it->second; //yes, return it
00198     
00199     //perhaps there's a sub-dictionary
00200     string::size_type p;
00201     it=getSubEntry(path,p);
00202     if(it==dict.end()) {
00203       // got noth'n
00204       return NULL;
00205     }
00206     
00207     //found a matching sub-collection, have it find the rest recursively
00208     const Collection* d=dynamic_cast<const Collection*>(it->second);
00209     return d->resolveEntry(path.substr(p+1));
00210   }
00211 
00212   void DictionaryBase::setComment(const std::string& name, const std::string& comment) {
00213     if(comment.size()>0)
00214       comments[name]=comment;
00215     else
00216       comments.erase(name);
00217   }
00218 
00219   const std::string& DictionaryBase::getComment(const std::string& name) const {
00220     storage_t::const_iterator it=dict.find(name);
00221     if(it==dict.end())
00222       return emptyStr();
00223     //found exact name match
00224     comments_t::const_iterator cit=comments.find(name);
00225     return (cit!=comments.end()) ? cit->second : emptyStr();
00226   }
00227   
00228   void DictionaryBase::loadXML(xmlNode* node) {
00229     //check if our node has been set to NULL (invalid or not found)
00230     if(node==NULL)
00231       return;
00232     if(!xNodeHasName(node,"dict"))
00233       throw bad_format(node,"Dictionary::loadXML expected <dict> value, got "+std::string((const char*)xNodeGetName(node)));
00234   
00235     std::string comment;
00236     std::set<std::string> seen;
00237     //process children nodes
00238     for(xmlNode* cur = skipToElement(node->children,comment); cur!=NULL; cur = skipToElement(cur->next,comment)) {
00239             
00240       //find the next key node
00241       xmlNode * k=cur;
00242       if(xmlStrcmp(k->name, (const xmlChar *)"key"))
00243         throw bad_format(k,"Dictionary format error: expect data in pairs of key and value (two values found in a row)");
00244       cur=skipToElement(cur->next);
00245       
00246       //find the following value (non-key) node
00247       xmlNode * v=cur;
00248       if(v==NULL)
00249         throw bad_format(cur,"Dictionary format error: expect data in pairs of key and value (dictionary ended with hanging key)");
00250       if(!xmlStrcmp(v->name, (const xmlChar *)"key"))
00251         throw bad_format(v,"Dictionary format error: expect data in pairs of key and value (two keys found in a row)");
00252       
00253       //find corresponding entry
00254       xmlChar* cont=xmlNodeGetContent(k);
00255       string key=(const char*)cont;
00256       xmlFree(cont);
00257       seen.insert(key);
00258       loadXMLNode(key,v,comment);
00259     }
00260     if((loadPolicy&REMOVALS) && seen.size()!=size()) {
00261       std::set<std::string> rem;
00262       for(const_iterator it=begin(); it!=end(); ++it) {
00263         if(seen.find(it->first)==seen.end())
00264           rem.insert(it->first);
00265       }
00266       for(std::set<std::string>::const_iterator it=rem.begin(); it!=rem.end(); ++it)
00267         removeEntry(*it);
00268     }
00269   }
00270   
00271   void DictionaryBase::saveXML(xmlNode* node, bool onlyOverwrite, std::set<std::string>& seen) const {
00272     //check if our node has been set to NULL (invalid or not found)
00273     if(node==NULL)
00274       return;
00275     
00276     //set the type of the current node
00277     xmlNodeSetName(node,(const xmlChar*)"dict");
00278     
00279     //find the depth of the target node in the xml tree to maintain proper indentation
00280     std::string indentStr=getIndentationPrefix(node);
00281     
00282     //This will hold any comments found between elements -- if no comment is found, a new one may be added
00283     std::string comment;
00284 
00285     //process children nodes
00286     xmlNode* prev=node->children;
00287     for(xmlNode* cur = skipToElement(node->children,comment); cur!=NULL; cur = skipToElement(cur,comment)) {
00288       
00289       //find the next key node
00290       xmlNode * k=cur;
00291       if(xmlStrcmp(k->name, (const xmlChar *)"key"))
00292         throw bad_format(k,"Dictionary format error: expect data in pairs of key and value (two values found in a row)");
00293       cur=skipToElement(cur->next);
00294       
00295       //find the following value (non-key) node
00296       xmlNode * v=cur;
00297       if(v==NULL)
00298         throw bad_format(cur,"Dictionary format error: expect data in pairs of key and value (dictionary ended with hanging key)");
00299       if(!xmlStrcmp(v->name, (const xmlChar *)"key"))
00300         throw bad_format(v,"Dictionary format error: expect data in pairs of key and value (two keys found in a row)");
00301       
00302       xmlChar* cont=xmlNodeGetContent(k);
00303       std::string key=(const char*)cont;
00304       xmlFree(cont);
00305       if(!saveOverXMLNode(k,v,key,comment,indentStr,seen)) {
00306         cur=xNodeGetNextNode(cur);
00307         if(savePolicy&REMOVALS) {
00308           while(prev!=cur) {
00309             xmlNode* n=prev;
00310             prev=xNodeGetNextNode(prev);
00311             xmlUnlinkNode(n);
00312             xmlFreeNode(n);
00313           }
00314         } else {
00315           if(warnUnused && savePolicy==FIXED)
00316             cerr << "Warning: saving over existing plist dictionary, key '" << key << "' does not match a registered variable.  Ignoring..." << endl;
00317         }
00318         prev=cur;
00319       }
00320       prev=cur=xNodeGetNextNode(cur);
00321     }
00322 
00323     if(!onlyOverwrite && seen.size()!=dict.size()) {
00324       // clear text nodes from end of dictionary back to last entry
00325       for(xmlNode* cur=node->last; cur!=NULL && cur->type==XML_TEXT_NODE; cur=node->last) {
00326         xmlUnlinkNode(cur);
00327         xmlFreeNode(cur);
00328       }
00329       
00330       // the main dictionary has entries that weren't seen... find which ones
00331       // if needed, this could be made faster (O(n) vs. current O(n lg n)) by assuming the maps
00332       // are sorted and moving two iterators through together instead of repeated find()'s
00333       for(storage_t::const_iterator it=dict.begin(); it!=dict.end(); ++it) {
00334         if(seen.find(it->first)==seen.end()) {
00335           //we didn't see this node in the existing xml tree, have to add a new node pair for it
00336           saveXMLNode(node,it->first,it->second,indentStr);
00337         }
00338       }
00339       std::string parentIndent;
00340       if(indentStr.size()>=perIndent().size())
00341         parentIndent=indentStr.substr(perIndent().size());
00342       xmlAddChild(node,xmlNewText((const xmlChar*)("\n"+parentIndent).c_str()));
00343     }
00344   }
00345 
00346   std::string DictionaryBase::toString() const {
00347     stringstream s;
00348     s << *this;
00349     return s.str();
00350   }
00351   
00352   unsigned int DictionaryBase::getLongestKeyLen(const regex_t* reg/*=NULL*/, unsigned int depth/*=-1*/) const {
00353     if(depth==0)
00354       return 0;
00355     size_t longest=0;
00356     size_t seplen=subCollectionSep().size();
00357     for(DictionaryBase::const_iterator it=begin(); it!=end(); ++it) {
00358       if(reg!=NULL && regexec(reg,it->first.c_str(),0,NULL,0)!=0)
00359         continue;
00360       size_t cur=it->first.size();
00361       if(Collection* dp=dynamic_cast<Collection*>(it->second))
00362         cur+=dp->getLongestKeyLen(reg,depth-1)+seplen;
00363       longest=std::max(longest,cur);
00364     }
00365     return longest;
00366   }
00367   
00368   DictionaryBase::iterator DictionaryBase::getSubEntry(const std::string& name, std::string::size_type& seppos) {
00369     seppos=name.find(subCollectionSep());
00370     if(seppos==string::npos)
00371       return dict.end(); //no '.'s found -- go away
00372     iterator it=dict.find(name.substr(0,seppos));
00373     if(it==dict.end())
00374       return dict.end(); //no entry matching prefix -- go away
00375     const Collection* d=dynamic_cast<const Collection*>(it->second);
00376     if(d==NULL)
00377       return dict.end(); //matching prefix is not a collection -- go away
00378     return it;
00379   }
00380   DictionaryBase::const_iterator DictionaryBase::getSubEntry(const std::string& name, std::string::size_type& seppos) const {
00381     seppos=name.find(subCollectionSep());
00382     if(seppos==string::npos)
00383       return dict.end(); //no '.'s found -- go away
00384     const_iterator it=dict.find(name.substr(0,seppos));
00385     if(it==dict.end())
00386       return dict.end(); //no entry matching prefix -- go away
00387     const Collection* d=dynamic_cast<const Collection*>(it->second);
00388     if(d==NULL)
00389       return dict.end(); //matching prefix is not a collection -- go away
00390     return it;
00391   }
00392     
00393   void DictionaryBase::clear() {
00394     storage_t::size_type s=dict.size();
00395     // this bit of trickiness is to handle element destructors doing things to the list while it's being cleared
00396     std::set<ObjectBase*> refs=myRef;
00397     dict.clear();
00398     myRef.clear();
00399     comments.clear();
00400     if(s>0) //only fire if we had entries to begin with
00401       fireEntriesChanged();
00402     for(std::set<ObjectBase*>::iterator it=refs.begin(); it!=refs.end(); ++it)
00403       delete *it;
00404   }
00405   
00406   void DictionaryBase::takeObject(const std::string& /*name*/, ObjectBase* obj) {
00407     myRef.insert(obj);
00408   }
00409 
00410   void DictionaryBase::fireEntryRemoved(ObjectBase& val) {
00411     Collection::fireEntryRemoved(val);
00412     std::set<ObjectBase*>::iterator it=myRef.find(&val);
00413     if(it!=myRef.end()) {
00414       myRef.erase(it);
00415       delete &val;
00416     }
00417   }
00418   
00419   void DictionaryBase::cloneMyRef() {
00420     for(iterator dit=dict.begin(); dit!=dict.end(); ++dit) {
00421       std::set<ObjectBase*>::iterator rit=myRef.find(dit->second);
00422       if(rit!=myRef.end()) {
00423         myRef.erase(rit);
00424         myRef.insert(dit->second=dynamic_cast<ObjectBase*>((dit->second)->clone()));
00425       }
00426     }
00427     
00428     //slower implementation, but can handle multiple pointers to the same instance (which we don't support elsewhere, so no point in doing it)
00429     /*
00430      std::set<ObjectBase*> ns;
00431     for(std::set<ObjectBase*>::iterator it=myRef.begin(); it!=myRef.end(); ++it) {
00432       ObjectBase* n=dynamic_cast<ObjectBase*>((*it)->clone());
00433       bool used=false;
00434       for(iterator dit=dict.begin(); dit!=dict.end(); ++dit) {
00435         if(*it==dit->second) {
00436           dit->second=n;
00437           used=true;
00438         }
00439       }
00440       if(!used) {
00441         cerr << "Warning: dictionary claims control over pointer not found in dictionary" << endl;
00442         delete n;
00443       } else
00444         ns.insert(n);
00445     }
00446     myRef=ns;
00447     */
00448   }
00449   
00450   bool DictionaryBase::saveOverXMLNode(xmlNode* k, xmlNode* val, const std::string& key, std::string comment, const std::string& indentStr, std::set<std::string>& seen) const {
00451     //find corresponding entry
00452     storage_t::const_iterator it=findEntry(key);
00453     if(it==dict.end())
00454       return false;
00455     if(comment.size()==0) {
00456       bool isSub=dynamic_cast<Collection*>(it->second);
00457       bool isFirst=true;
00458       const std::string indentedNewline="\n"+indentStr;
00459       const std::string headline=("======== "+it->first+" ========");
00460       comments_t::const_iterator cit=comments.find(key);
00461       if(cit!=comments.end()) {
00462         while(k->prev!=NULL && xNodeIsText(k->prev)) {
00463           xmlNode* n=k->prev;
00464           xmlUnlinkNode(n);
00465           xmlFreeNode(n);
00466         }
00467       }
00468       if(isSub) {
00469         isFirst=(skipToElement(k->parent->children)==k);
00470         xmlAddPrevSibling(k,xmlNewText((const xmlChar*)(isFirst ? indentedNewline : indentedNewline+indentedNewline).c_str()));
00471         xmlAddPrevSibling(k,xmlNewComment((const xmlChar*)headline.c_str()));
00472       }
00473       if(cit!=comments.end()) {
00474         if(isSub || cit->second.find(key)<KEY_IN_COMMENT_MAX_POS)
00475           comment=cit->second;
00476         else //if not a sub-dictionary, and comment doesn't already start with entry name, prepend entry name
00477           comment=key+": "+cit->second;
00478         string::size_type pos=comment.rfind('\n');
00479         while(pos!=string::npos) {
00480           if(comment.compare(pos+1,indentStr.size(),indentStr)!=0)
00481             comment.insert(pos+1,indentStr);
00482           if(pos==0)
00483             break;
00484           pos = comment.rfind('\n',pos-1);
00485         }
00486         if(!isSub)
00487           isFirst=(skipToElement(k->parent->children)==k);
00488         xmlAddPrevSibling(k,xmlNewText((const xmlChar*)(isFirst ? indentedNewline : indentedNewline+indentedNewline).c_str()));
00489         xmlAddPrevSibling(k,xmlNewComment((const xmlChar*)comment.c_str()));
00490         xmlAddPrevSibling(k,xmlNewText((const xmlChar*)indentedNewline.c_str()));
00491       }
00492     }
00493     it->second->saveXML(val);
00494     if(seen.find(key)!=seen.end()) {
00495       std::cerr << "WARNING: plist::Dictionary found duplicate key " << key << " during save" << std::endl;
00496     } else {
00497       seen.insert(key);
00498     }
00499     return true;
00500   }
00501   
00502   void DictionaryBase::saveXMLNode(xmlNode* node, const std::string& key, const ObjectBase* val, const std::string& indentStr) const {
00503     bool isSub=dynamic_cast<const Collection*>(val);
00504     bool isFirst=(node->children==NULL);
00505     const std::string indentedNewline="\n"+indentStr;
00506     const std::string headline=("======== "+key+" ========");
00507     if(isSub) {
00508       xmlAddChild(node,xmlNewText((const xmlChar*)(isFirst ? indentedNewline : indentedNewline+indentedNewline).c_str()));
00509       xmlAddChild(node,xmlNewComment((const xmlChar*)headline.c_str()));
00510     }
00511     std::string comment;
00512     comments_t::const_iterator cit=comments.find(key);
00513     if(cit!=comments.end()) {
00514       if(isSub || cit->second.find(key)<KEY_IN_COMMENT_MAX_POS)
00515         comment=cit->second;
00516       else
00517         comment=key+": "+cit->second;
00518       string::size_type pos=comment.rfind('\n');
00519       while(pos!=string::npos) {
00520         if(comment.compare(pos+1,indentStr.size(),indentStr)!=0)
00521           comment.insert(pos+1,indentStr);
00522         if(pos==0)
00523           break;
00524         pos = comment.rfind('\n',pos-1);
00525       }
00526       xmlAddChild(node,xmlNewText((const xmlChar*)(isSub || isFirst ? indentedNewline : indentedNewline+indentedNewline).c_str()));
00527       xmlAddChild(node,xmlNewComment((const xmlChar*)comment.c_str()));
00528     }
00529     xmlAddChild(node,xmlNewText((const xmlChar*)indentedNewline.c_str()));
00530     xmlNode* k=xmlNewChild(node,NULL,(const xmlChar*)"key",(const xmlChar*)key.c_str());
00531     if(k==NULL)
00532       throw bad_format(node,"Error: plist Dictionary xml error on saving key");
00533     xmlAddChild(node,xmlNewText((const xmlChar*)" "));
00534     xmlNode* v=xmlNewChild(node,NULL,(const xmlChar*)"",NULL);
00535     if(v==NULL)
00536       throw bad_format(node,"Error: plist Dictionary xml error on saving value");
00537     val->saveXML(v);
00538   }
00539   
00540   
00541   bool ArrayBase::removeEntry(size_t index) {
00542     if(index>=arr.size())
00543        return false;
00544     storage_t::iterator it=arr.begin();
00545     advance(it,index);
00546     ObjectBase* obj=*it;
00547     arr.erase(it);
00548     comments.erase(index);
00549     fireEntryRemoved(*obj);
00550     return true;
00551   }
00552 
00553   ObjectBase* ArrayBase::resolveEntry(const std::string& path) const {
00554     size_t index=getIndex(path);
00555     if(index<size())
00556       return &getEntry(index);
00557     std::string::size_type p;
00558     const_iterator it=getSubEntry(path,p);
00559     if(it==arr.end())
00560       throw bad_format(NULL,"Array::resolveEntry(name) was called with an invalid numeric name:" + path);
00561     const Collection * d=dynamic_cast<const Collection*>(*it);
00562     return d->resolveEntry(path.substr(p+1));
00563   }
00564     
00565   void ArrayBase::clear() {
00566     storage_t::size_type s=arr.size();
00567     // this bit of trickiness is to handle element destructors doing things to the list while it's being cleared
00568     std::set<ObjectBase*> refs=myRef;
00569     arr.clear();
00570     comments.clear();
00571     myRef.clear();
00572     if(s>0) //only fire if we had entries to begin with
00573       fireEntriesChanged();
00574     for(std::set<ObjectBase*>::iterator it=refs.begin(); it!=refs.end(); ++it)
00575       delete *it;
00576   }
00577   
00578   void ArrayBase::setComment(size_t index, const std::string& comment) {
00579     if(comment.size()>0)
00580       comments[index]=comment;
00581     else
00582       comments.erase(index);
00583   }
00584   
00585   const std::string& ArrayBase::getComment(size_t index) const {
00586     comments_t::const_iterator it=comments.find(index);
00587     if(it==comments.end())
00588       return emptyStr();
00589     else
00590       return it->second;
00591   }
00592   
00593   void ArrayBase::loadXML(xmlNode* node) {
00594     //check if our node has been set to NULL (invalid or not found)
00595     if(node==NULL)
00596       return;
00597     if(!xNodeHasName(node,"array"))
00598       throw bad_format(node,"Array::loadXML expected <array> value, got "+std::string((const char*)xNodeGetName(node)));
00599     
00600     std::string comment;
00601     unsigned int i=0;
00602     for(xmlNode* cur = skipToElement(xNodeGetChildren(node),comment); cur!=NULL; cur = skipToElement(xNodeGetNextNode(cur),comment)) {
00603       if(!loadXMLNode(i++, cur, comment))
00604          break;
00605     }
00606     if(loadPolicy&REMOVALS) {
00607       while(i<size())
00608         removeEntry(size()-1);
00609     } 
00610   }
00611   
00612   void ArrayBase::saveXML(xmlNode* node) const {
00613     //check if our node has been set to NULL (invalid or not found)
00614     if(node==NULL)
00615       return;
00616     
00617     //set the type of the current node
00618     xmlNodeSetName(node,(const xmlChar*)"array");
00619     
00620     //find the depth of the target node in the xml tree to maintain proper indentation
00621     std::string indentStr=getIndentationPrefix(node);
00622     std::string parentIndent;
00623     if(indentStr.size()>=perIndent().size())
00624       parentIndent=indentStr.substr(perIndent().size());
00625     
00626     //This will hold any comments found between elements -- if no comment is found, a new one may be added
00627     std::string comment;
00628     
00629     //This will be the index of the item we're loading next
00630     unsigned int i=0;
00631     
00632     //process children nodes
00633     xmlNode * prev=xNodeGetChildren(node);
00634     for(xmlNode* cur = skipToElement(prev,comment); cur!=NULL; cur = skipToElement(cur,comment)) {
00635       
00636       if(i==arr.size()) {
00637         if(savePolicy&REMOVALS) {
00638           while(prev!=NULL) {
00639             xmlNode* n=prev;
00640             prev=xNodeGetNextNode(prev);
00641             xmlUnlinkNode(n);
00642             xmlFreeNode(n);
00643           }
00644         } else {
00645           if(warnUnused && savePolicy==FIXED)
00646             std::cerr << "Warning: plist::Array ignoring extraneous items in destination during save..." << std::endl;
00647         }
00648         break;
00649       }
00650       if(comment.size()==0) {
00651         comments_t::const_iterator cit=comments.find(i);
00652         if(cit!=comments.end()) {
00653           char buf[20];
00654           unsigned int len;
00655           snprintf(buf,20,"%u%n",i,&len);
00656           if(/*isSub ||*/ strncmp(cit->second.c_str(),buf,len)==0)
00657             comment=cit->second;
00658           else { //if not a sub-dictionary, and comment doesn't already start with entry name, prepend entry name
00659             comment=buf;
00660             comment+=": "+cit->second;
00661           }
00662           xmlAddPrevSibling(cur,xmlNewText((const xmlChar*)("\n"+indentStr).c_str()));
00663           std::string::size_type pos=comment.rfind('\n');
00664           while(pos!=std::string::npos) {
00665             if(comment.compare(pos+1,indentStr.size(),indentStr)!=0)
00666               comment.insert(pos+1,indentStr);
00667             if(pos==0)
00668               break;
00669             pos = comment.rfind('\n',pos-1);
00670           }
00671           xmlAddPrevSibling(cur,xmlNewComment((const xmlChar*)comment.c_str()));
00672           xmlAddPrevSibling(cur,xmlNewText((const xmlChar*)("\n"+indentStr).c_str()));
00673         }
00674       }
00675       arr[i++]->saveXML(cur);
00676       prev=cur=xNodeGetNextNode(cur);
00677     }
00678     
00679     if(!(savePolicy&ADDITIONS))
00680       return;
00681     
00682     bool hadUnsaved = (i<arr.size());
00683     for(; i<arr.size(); ++i) {
00684       comments_t::const_iterator cit=comments.find(i);
00685       if(cit!=comments.end()) {
00686         char buf[20];
00687         unsigned int len;
00688         snprintf(buf,20,"%u%n",i,&len);
00689         if(/*isSub ||*/ strncmp(cit->second.c_str(),buf,len)==0)
00690           comment=cit->second;
00691         else { //if not a sub-dictionary, and comment doesn't already start with entry name, prepend entry name
00692           comment=buf;
00693           comment+=": "+cit->second;
00694         }
00695         xmlAddChild(node,xmlNewText((const xmlChar*)("\n"+indentStr).c_str()));
00696         std::string::size_type pos=comment.rfind('\n');
00697         while(pos!=std::string::npos) {
00698           if(comment.compare(pos+1,indentStr.size(),indentStr)!=0)
00699             comment.insert(pos+1,indentStr);
00700           if(pos==0)
00701             break;
00702           pos = comment.rfind('\n',pos-1);
00703         }
00704         xmlAddChild(node,xmlNewComment((const xmlChar*)comment.c_str()));
00705       }
00706       xmlAddChild(node,xmlNewText((const xmlChar*)("\n"+indentStr).c_str()));
00707       xmlNode* v=xmlNewChild(node,NULL,(const xmlChar*)"",NULL);
00708       if(v==NULL)
00709         throw bad_format(node,"Error: plist Array xml error on saving value");
00710       arr[i]->saveXML(v);
00711     }
00712     if(hadUnsaved)
00713       xmlAddChild(node,xmlNewText((const xmlChar*)("\n"+parentIndent).c_str()));
00714   }
00715   
00716   std::string ArrayBase::toString() const {
00717     std::stringstream s;
00718     s << *this;
00719     return s.str();
00720   }
00721   
00722   unsigned int ArrayBase::getLongestKeyLen(const regex_t* reg/*=NULL*/, unsigned int depth/*=-1*/) const {
00723     if(depth==0)
00724       return 0;
00725     size_t longest=0;
00726     size_t seplen=subCollectionSep().size();
00727     for(size_t i=0; i<size(); ++i) {
00728       std::stringstream s;
00729       s << i;
00730       if(reg!=NULL && regexec(reg,s.str().c_str(),0,NULL,0)!=0)
00731         continue;
00732       size_t cur=s.str().size();
00733       if(Collection* dp=dynamic_cast<Collection*>(arr[i]))
00734         cur+=dp->getLongestKeyLen(reg,depth-1)+seplen;
00735       longest=std::max(longest,cur);
00736     }
00737     return longest;
00738   }
00739   
00740   void ArrayBase::takeObject(size_t /*index*/, ObjectBase* obj) {
00741     myRef.insert(obj);
00742   }
00743   
00744   void ArrayBase::fireEntryRemoved(ObjectBase& val) {
00745     Collection::fireEntryRemoved(val);
00746     std::set<ObjectBase*>::iterator it=myRef.find(&val);
00747     if(it!=myRef.end()) {
00748       myRef.erase(it);
00749       delete &val;
00750     }
00751   }
00752   
00753   ArrayBase::iterator ArrayBase::getSubEntry(const std::string& name, std::string::size_type& seppos) {
00754     seppos=name.find(subCollectionSep());
00755     if(seppos==std::string::npos)
00756       return arr.end(); //no '.'s found -- go away
00757     size_t index=getIndex(name.substr(0,seppos));
00758     if(index>=size())
00759       return arr.end(); //no entry matching prefix -- go away
00760     iterator it=arr.begin();
00761     advance(it,index);
00762     const Collection* d=dynamic_cast<const Collection*>(*it);
00763     if(d==NULL)
00764       return arr.end(); //matching prefix is not a collection -- go away
00765     return it;
00766   }
00767   ArrayBase::const_iterator ArrayBase::getSubEntry(const std::string& name, std::string::size_type& seppos) const {
00768     seppos=name.find(subCollectionSep());
00769     if(seppos==std::string::npos)
00770       return arr.end(); //no '.'s found -- go away
00771     size_t index=getIndex(name.substr(0,seppos));
00772     if(index>=size())
00773       return arr.end(); //no entry matching prefix -- go away
00774     const_iterator it=arr.begin();
00775     advance(it,index);
00776     const Collection* d=dynamic_cast<const Collection*>(*it);
00777     if(d==NULL)
00778       return arr.end(); //matching prefix is not a collection -- go away
00779     return it;
00780   }
00781   
00782   void ArrayBase::cloneMyRef() {
00783     for(iterator dit=arr.begin(); dit!=arr.end(); ++dit) {
00784       std::set<ObjectBase*>::iterator rit=myRef.find(*dit);
00785       if(rit!=myRef.end()) {
00786         myRef.erase(rit);
00787         myRef.insert(*dit=dynamic_cast<ObjectBase*>((*dit)->clone()));
00788       }
00789     }
00790   }
00791   
00792   std::ostream& filteredDisplay(std::ostream& os, const ObjectBase& c, const std::string& sel, int selType, unsigned int depth) {
00793     if(sel.size()==0)
00794       return filteredDisplay(os,c,NULL,depth);
00795     regex_t r;
00796     if(regcomp(&r,sel.c_str(),selType|REG_NOSUB)==0)
00797       filteredDisplay(os,c,&r,depth);
00798     regfree(&r);
00799     return os;
00800   }
00801     
00802   std::ostream& filteredDisplay(std::ostream& os, const ObjectBase& c, const regex_t* reg, unsigned int depth) {
00803     unsigned int seplen=Collection::subCollectionSep().size();
00804     unsigned int out=0;
00805     
00806     if(const ArrayBase* a=dynamic_cast<const ArrayBase*>(&c)) {
00807       unsigned int longest=std::max(a->getLongestKeyLen(reg,depth),static_cast<unsigned int>(os.width()));
00808       for(unsigned long i=0; i<a->size(); ++i) {
00809         stringstream ns;
00810         ns << i;
00811         if(reg!=NULL && regexec(reg,ns.str().c_str(),0,NULL,0)!=0)
00812           continue;
00813         out++;
00814         if(depth==0)
00815           return os << right << setw(longest) << "" << " = [...]" << endl;
00816         if(Collection* dp=dynamic_cast<Collection*>(&(*a)[i])) {
00817           stringstream ss;
00818           ss << left << std::setw(longest-snprintf(NULL,0,"%lu",i)-seplen);
00819           filteredDisplay(ss,*dp,reg,depth-1);
00820           std::string line;
00821           for(getline(ss,line); ss; std::getline(ss,line))
00822             os << (ns.str() + Collection::subCollectionSep() + line) << std::endl;
00823         } else {
00824           os << std::left << std::setw(longest) << ns.str() << " = " << (*a)[i] << std::endl;
00825         }
00826       }
00827       if(out==0)
00828         return os << right << setw(longest) << "" << " = (empty array)" << endl;
00829       
00830     } else if(const DictionaryBase* d=dynamic_cast<const DictionaryBase*>(&c)) {
00831       unsigned int longest=std::max(d->getLongestKeyLen(reg,depth),static_cast<unsigned int>(os.width()));
00832       for(DictionaryBase::storage_t::const_iterator it=d->begin(); it!=d->end(); ++it) {
00833         if(reg!=NULL && regexec(reg,it->first.c_str(),0,NULL,0)!=0)
00834           continue;
00835         out++;
00836         if(depth==0)
00837           return os << right << setw(longest) << "" << " = [...]" << endl;
00838         if(Collection* dp=dynamic_cast<Collection*>(it->second)) {
00839           stringstream ss;
00840           ss << left << setw(longest-it->first.size()-seplen);
00841           filteredDisplay(ss,*dp,reg,depth-1);
00842           string line;
00843           for(getline(ss,line); ss; getline(ss,line))
00844             os << (it->first + Collection::subCollectionSep() + line) << endl;
00845         } else {
00846           os << left << setw(longest) << it->first << " = " << *it->second << endl;
00847         }
00848       }
00849       if(out==0)
00850         return os << right << setw(longest) << "" << " = (empty dictionary)" << endl;
00851       
00852     } else {
00853       os << c.toString();
00854     }
00855     return os;
00856   }
00857   
00858 } //namespace plist
00859 
00860 
00861 /*! @file
00862  * @brief 
00863  * @author Ethan Tira-Thompson (ejt) (Creator)
00864  *
00865  * $Author: ejt $
00866  * $Name: tekkotsu-4_0 $
00867  * $Revision: 1.13 $
00868  * $State: Exp $
00869  * $Date: 2007/10/26 22:33:20 $
00870  */

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