00001 #include "plistCollections.h"
00002 #include <libxml/xmlmemory.h>
00003 #include <libxml/parser.h>
00004 #include <iomanip>
00005
00006
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
00053 std::set<CollectionListener*> pls=*collectionListeners;
00054 for(std::set<CollectionListener*>::const_iterator it=pls.begin(); collectionListeners!=NULL && it!=pls.end(); ++it) {
00055
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
00065 std::set<CollectionListener*> pls=*collectionListeners;
00066 for(std::set<CollectionListener*>::const_iterator it=pls.begin(); collectionListeners!=NULL && it!=pls.end(); ++it) {
00067
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
00077 std::set<CollectionListener*> pls=*collectionListeners;
00078 for(std::set<CollectionListener*>::const_iterator it=pls.begin(); collectionListeners!=NULL && it!=pls.end(); ++it) {
00079
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) {
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
00104 if(*endp!='\0')
00105 return (size_t)-1;
00106
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
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
00128 storage_t::iterator nit=dict.find(newname);
00129 if(nit!=dict.end()) {
00130
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
00142 comments_t::iterator cit=comments.find(oldname);
00143 if(cit==comments.end()) {
00144
00145 cit = comments.find(newname);
00146 if(cit!=comments.end())
00147 comments.erase(cit);
00148 } else {
00149
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
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
00176 swap(acit->second,bcit->second);
00177 } else {
00178
00179 string com = acit->second;
00180 comments.erase(acit);
00181 comments[b]=com;
00182 }
00183 } else if(bcit != comments.end()) {
00184
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
00195 const_iterator it=dict.find(path);
00196 if(it!=dict.end())
00197 return it->second;
00198
00199
00200 string::size_type p;
00201 it=getSubEntry(path,p);
00202 if(it==dict.end()) {
00203
00204 return NULL;
00205 }
00206
00207
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
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
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
00238 for(xmlNode* cur = skipToElement(node->children,comment); cur!=NULL; cur = skipToElement(cur->next,comment)) {
00239
00240
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
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
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
00273 if(node==NULL)
00274 return;
00275
00276
00277 xmlNodeSetName(node,(const xmlChar*)"dict");
00278
00279
00280 std::string indentStr=getIndentationPrefix(node);
00281
00282
00283 std::string comment;
00284
00285
00286 xmlNode* prev=node->children;
00287 for(xmlNode* cur = skipToElement(node->children,comment); cur!=NULL; cur = skipToElement(cur,comment)) {
00288
00289
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
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
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
00331
00332
00333 for(storage_t::const_iterator it=dict.begin(); it!=dict.end(); ++it) {
00334 if(seen.find(it->first)==seen.end()) {
00335
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, unsigned int depth) 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();
00372 iterator it=dict.find(name.substr(0,seppos));
00373 if(it==dict.end())
00374 return dict.end();
00375 const Collection* d=dynamic_cast<const Collection*>(it->second);
00376 if(d==NULL)
00377 return dict.end();
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();
00384 const_iterator it=dict.find(name.substr(0,seppos));
00385 if(it==dict.end())
00386 return dict.end();
00387 const Collection* d=dynamic_cast<const Collection*>(it->second);
00388 if(d==NULL)
00389 return dict.end();
00390 return it;
00391 }
00392
00393 void DictionaryBase::clear() {
00394 storage_t::size_type s=dict.size();
00395
00396 std::set<ObjectBase*> refs=myRef;
00397 dict.clear();
00398 myRef.clear();
00399 comments.clear();
00400 if(s>0)
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& , 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
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441
00442
00443
00444
00445
00446
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
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
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
00568 std::set<ObjectBase*> refs=myRef;
00569 arr.clear();
00570 comments.clear();
00571 myRef.clear();
00572 if(s>0)
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
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
00614 if(node==NULL)
00615 return;
00616
00617
00618 xmlNodeSetName(node,(const xmlChar*)"array");
00619
00620
00621 std::string indentStr=getIndentationPrefix(node);
00622 std::string parentIndent;
00623 if(indentStr.size()>=perIndent().size())
00624 parentIndent=indentStr.substr(perIndent().size());
00625
00626
00627 std::string comment;
00628
00629
00630 unsigned int i=0;
00631
00632
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( strncmp(cit->second.c_str(),buf,len)==0)
00657 comment=cit->second;
00658 else {
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( strncmp(cit->second.c_str(),buf,len)==0)
00690 comment=cit->second;
00691 else {
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, unsigned int depth) 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 , 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();
00757 size_t index=getIndex(name.substr(0,seppos));
00758 if(index>=size())
00759 return arr.end();
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();
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();
00771 size_t index=getIndex(name.substr(0,seppos));
00772 if(index>=size())
00773 return arr.end();
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();
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 }
00859
00860
00861
00862
00863
00864
00865
00866
00867
00868
00869
00870