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) {
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++;
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++;
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++;
00072 (*cur)->plistCollectionEntriesChanged(*this);
00073 }
00074 }
00075
00076
00077
00078
00079
00080 void Dictionary::setEntry(const std::string& name, ObjectBase& val, bool warnExists) {
00081 storage_t::iterator it=dict.find(name);
00082 if(it!=dict.end()) {
00083
00084 if(&val==it->second)
00085 return;
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
00092 } else {
00093
00094 string::size_type p;
00095 it=getSubEntry(name,p);
00096 if(it!=dict.end()) {
00097
00098 Collection* d=dynamic_cast<Collection*>(it->second);
00099 d->setEntry(name.substr(p+1),val,warnExists);
00100 return;
00101 }
00102
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
00111 if(&val==it->second)
00112 return;
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
00119 } else {
00120
00121 string::size_type p;
00122 it=getSubEntry(name,p);
00123 if(it!=dict.end()) {
00124
00125 Collection* d=dynamic_cast<Collection*>(it->second);
00126 d->addEntry(name.substr(p+1),val,comment);
00127 return;
00128 }
00129
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) {
00137 storage_t::iterator it=dict.find(name);
00138 if(it!=dict.end()) {
00139
00140 if(val==it->second)
00141 return;
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
00148 } else {
00149
00150 string::size_type p;
00151 it=getSubEntry(name,p);
00152 if(it!=dict.end()) {
00153
00154 Collection* d=dynamic_cast<Collection*>(it->second);
00155 d->setEntry(name.substr(p+1),val,warnExists);
00156 return;
00157 }
00158
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
00168 if(val==it->second)
00169 return;
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
00176 } else {
00177
00178 string::size_type p;
00179 it=getSubEntry(name,p);
00180 if(it!=dict.end()) {
00181
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
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
00204 ObjectBase* obj=it->second;
00205 dict.erase(it);
00206 fireEntryRemoved(*obj);
00207 } else {
00208
00209 string::size_type p;
00210 it=getSubEntry(name,p);
00211 if(it!=dict.end()) {
00212
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
00221 const_iterator it=dict.find(name);
00222 if(it!=dict.end())
00223 return it->second;
00224
00225
00226 string::size_type p;
00227 it=getSubEntry(name,p);
00228 if(it!=dict.end()) {
00229
00230 const Collection* d=dynamic_cast<const Collection*>(it->second);
00231 return d->getEntry(name.substr(p+1));
00232 }
00233
00234
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
00242 string::size_type p;
00243 it=getSubEntry(name,p);
00244 if(it!=dict.end()) {
00245
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
00261 comments_t::const_iterator cit=comments.find(name);
00262 return (cit!=comments.end()) ? cit->second : emptyStr();
00263 } else {
00264
00265 string::size_type p;
00266 it=getSubEntry(name,p);
00267 if(it!=dict.end()) {
00268
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
00278 if(node==NULL)
00279 return;
00280
00281 std::string comment;
00282 set<std::string> seen;
00283
00284 for(xmlNode* cur = skipToElement(node->children,comment); cur!=NULL; cur = skipToElement(cur->next,comment)) {
00285
00286
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
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
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
00319 if(node==NULL)
00320 return;
00321
00322
00323 xmlNodeSetName(node,(const xmlChar*)"dict");
00324
00325
00326
00327 storage_t seen;
00328
00329
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) {
00334 if(indentStr.size()>0)
00335 indentStr=indentStr.substr(0,indentStr.size()-perIndent.size());
00336 break;
00337 }
00338 indentStr+=perIndent;
00339 }
00340
00341
00342 std::string comment;
00343
00344
00345 xmlNode* prev=node->children;
00346 for(xmlNode* cur = skipToElement(node->children,comment); cur!=NULL; cur = skipToElement(cur,comment)) {
00347
00348
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
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
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
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
00406
00407
00408 for(storage_t::const_iterator it=dict.begin(); it!=dict.end(); ++it) {
00409 if(seen.find(it->first)==seen.end()) {
00410
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
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();
00468 iterator it=dict.find(name.substr(0,seppos));
00469 if(it==dict.end())
00470 return dict.end();
00471 const Collection* d=dynamic_cast<const Collection*>(it->second);
00472 if(d==NULL)
00473 return dict.end();
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();
00480 const_iterator it=dict.find(name.substr(0,seppos));
00481 if(it==dict.end())
00482 return dict.end();
00483 const Collection* d=dynamic_cast<const Collection*>(it->second);
00484 if(d==NULL)
00485 return dict.end();
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) {
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) {
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) {
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) {
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
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
00734 if(node==NULL)
00735 return;
00736
00737
00738 xmlNodeSetName(node,(const xmlChar*)"array");
00739
00740
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) {
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
00756 std::string comment;
00757
00758
00759 unsigned int i=0;
00760
00761
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( strncmp(cit->second.c_str(),buf,len)==0)
00786 comment=cit->second;
00787 else {
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( strncmp(cit->second.c_str(),buf,len)==0)
00808 comment=cit->second;
00809 else {
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
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
00841
00842
00843
00844
00845
00846
00847
00848
00849
00850 if(*endp!='\0')
00851 return (size_t)-1;
00852
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();
00872 size_t index=getIndex(name.substr(0,seppos));
00873 if(index>=size())
00874 return arr.end();
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();
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();
00886 size_t index=getIndex(name.substr(0,seppos));
00887 if(index>=size())
00888 return arr.end();
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();
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 }
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& , 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
00944
00945
00946
00947
00948
00949
00950
00951
00952
00953
00954
00955
00956
00957
00958
00959
00960
00961
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 , 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
00996
00997
00998
00999
01000
01001
01002
01003
01004
01005
01006
01007
01008
01009
01010
01011
01012
01013
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
01021 try {
01022
01023
01024 it->second->loadXML(val);
01025 if(comment.size()>0)
01026 setComment(key,comment);
01027 return true;
01028 } catch(const bad_format& ex) {
01029
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
01047 try {
01048
01049
01050 arr[index]->loadXML(val);
01051 if(comment.size()>0)
01052 setComment(index,comment);
01053 return true;
01054 } catch(const bad_format& ex) {
01055
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 }
01071
01072
01073
01074
01075
01076
01077
01078
01079
01080
01081
01082