00001 #include "plistCollections.h"
00002 #include "string_util.h"
00003 #include <libxml/xmlmemory.h>
00004 #include <libxml/parser.h>
00005 #include <iomanip>
00006
00007
00008 using namespace std;
00009
00010 namespace plist {
00011 template class DictionaryOf<ObjectBase>;
00012 template class ArrayOf<ObjectBase>;
00013 template class ArrayOf<Dictionary>;
00014 template class ArrayOf<Primitive<float> >;
00015 template class ArrayOf<Primitive<double> >;
00016
00017
00018 template<> ArrayOf<ObjectBase, ObjectBase::conversion_policy<ArrayBase,ObjectBase>::value_conversion>::ArrayOf(storage_t::size_type n)
00019 : ArrayBase(false), ObjectBase::conversion_policy<ArrayBase,ObjectBase>::value_conversion()
00020 {
00021 arr.resize(n);
00022 }
00023
00024 template<> ObjectBase* loadXML(xmlNode* node) {
00025 ObjectBase * obj=plist::loadXML(node);
00026 if(obj==NULL)
00027 throw XMLLoadSave::bad_format(node,"plist::loadXML encountered an unknown value type");
00028 return obj;
00029 }
00030
00031 template<> PrimitiveBase* loadXML(xmlNode* node) {
00032 ObjectBase * obj=plist::loadXML(node);
00033 if(obj==NULL)
00034 throw XMLLoadSave::bad_format(node,"plist::loadXML encountered an unknown value type");
00035 PrimitiveBase * cobj = dynamic_cast<PrimitiveBase*>(obj);
00036 if(cobj==NULL) {
00037 delete obj;
00038 throw XMLLoadSave::bad_format(node,"plist::loadXML<PrimitiveBase*> attempted to load non-primitive (e.g. collection?)");
00039 }
00040 return cobj;
00041 }
00042 template<> Collection* loadXML(xmlNode* node) {
00043 ObjectBase * obj=plist::loadXML(node);
00044 if(obj==NULL)
00045 throw XMLLoadSave::bad_format(node,"plist::loadXML encountered an unknown value type");
00046 Collection * cobj = dynamic_cast<Collection*>(obj);
00047 if(cobj==NULL) {
00048 delete obj;
00049 throw XMLLoadSave::bad_format(node,"plist::loadXML<Collection*> attempted to load non-collection (e.g. primitive?)");
00050 }
00051 return cobj;
00052 }
00053
00054 Collection::~Collection() {
00055 delete collectionListeners;
00056 collectionListeners=NULL;
00057 }
00058
00059 bool Collection::resolveAssignment(const std::string& arg) {
00060 std::stringstream errstream;
00061 return resolveAssignment(arg,errstream);
00062 }
00063
00064 bool Collection::resolveAssignment(const std::string& arg, std::ostream& errstream) {
00065 string::size_type eqpos = arg.find("=");
00066 string value=string_util::trim(arg.substr(eqpos+1));
00067 bool isAppend = (arg[eqpos-1]=='+');
00068 string key=string_util::trim(arg.substr(0,isAppend?eqpos-1:eqpos));
00069 plist::ObjectBase* ob=resolveEntry(key);
00070 if(ob==NULL) {
00071 if(isAppend) {
00072 errstream << "Cannot append to unknown array '" << key << '\'' << endl;
00073 } else {
00074 string::size_type dotpos = key.rfind(".");
00075 string parent = key.substr(0,dotpos);
00076 string entry = key.substr(dotpos+1);
00077 ob=resolveEntry(parent);
00078 if(ob==NULL) {
00079 errstream << "'" << key << "' is unknown" << endl;
00080 } else if(plist::Collection* col=dynamic_cast<plist::Collection*>(ob)) {
00081 if((col->getLoadPolicy() & plist::Collection::ADDITIONS) != plist::Collection::ADDITIONS) {
00082 errstream << "Collection '" << parent << "' is not dynamically resizeable, cannot add entry for '" << entry << "'" << endl;
00083 } else if(plist::ArrayBase::StringConversion* arr=dynamic_cast<plist::ArrayBase::StringConversion*>(ob)) {
00084 try {
00085 size_t i=atoi(entry.c_str());
00086 arr->addValue(i,value);
00087 return true;
00088 } catch(const XMLLoadSave::bad_format& bf) {
00089 errstream << "'" << value << "' is a bad value for '" << key << "'" << endl;
00090 errstream << bf.what() << endl;
00091 } catch(const std::exception& e) {
00092 errstream << "An exception occured: " << e.what() << endl;
00093 }
00094 } else if(plist::DictionaryBase::StringConversion* dict=dynamic_cast<plist::DictionaryBase::StringConversion*>(ob)) {
00095 try {
00096 dict->addValue(entry,value);
00097 return true;
00098 } catch(const XMLLoadSave::bad_format& bf) {
00099 errstream << "'" << value << "' is a bad value for '" << key << "'" << endl;
00100 errstream << bf.what() << endl;
00101 } catch(const std::exception& e) {
00102 errstream << "An exception occured: " << e.what() << endl;
00103 }
00104 } else {
00105 errstream << "Unknown collection type or conversion policy for adding new entry '" << entry <<"' to '" << parent << "' from string value '" << value << '\'' << endl;
00106 }
00107 } else {
00108 errstream << "Cannot add subentries to non-collection '" << parent << "'" << endl;
00109 }
00110 }
00111 } else if(isAppend) {
00112 if(dynamic_cast<plist::Collection*>(ob)) {
00113 if(plist::ArrayBase* arr=dynamic_cast<plist::ArrayBase*>(ob)) {
00114 if((arr->getLoadPolicy() & plist::Collection::ADDITIONS) != plist::Collection::ADDITIONS) {
00115 errstream << "Array '" << key << "' is not dynamically resizeable, cannot append entry for '" << value << "'" << endl;
00116 } else if(plist::ArrayBase::StringConversion* strarr=dynamic_cast<plist::ArrayBase::StringConversion*>(ob)) {
00117 try {
00118 strarr->addValue(value);
00119 return true;
00120 } catch(const XMLLoadSave::bad_format& bf) {
00121 errstream << "'" << value << "' is a bad value for '" << key << "'" << endl;
00122 errstream << bf.what() << endl;
00123 } catch(const std::exception& e) {
00124 errstream << "An exception occured: " << e.what() << endl;
00125 }
00126 } else {
00127 errstream << "Internal error: don't know how to convert string value '" << value << "' into new entry for '" << key << "'" << endl;
00128 }
00129 } else {
00130 errstream << "Cannot append unnamed new entries to non-array, specify new entry name and use '='" << endl;
00131 }
00132 } else {
00133 errstream << "Cannot add subentries to non-array '" << key << "'" << endl;
00134 }
00135 } else {
00136 if(plist::PrimitiveBase* pbp=dynamic_cast<plist::PrimitiveBase*>(ob)) {
00137 try {
00138 pbp->set(value);
00139 return true;
00140 } catch(const XMLLoadSave::bad_format& bf) {
00141 errstream << "'" << value << "' is a bad value for '" << key << "'" << endl;
00142 errstream << bf.what() << endl;
00143 } catch(const std::exception& e) {
00144 errstream << "An exception occured: " << e.what() << endl;
00145 }
00146 } else {
00147 errstream << "Cannot assign to a collection" << endl;
00148 }
00149 }
00150 return false;
00151 }
00152
00153 void Collection::addCollectionListener(CollectionListener* l) const {
00154 if(l!=NULL) {
00155 if(collectionListeners==NULL)
00156 collectionListeners=new std::set<CollectionListener*>;
00157 collectionListeners->insert(l);
00158 }
00159 }
00160
00161 void Collection::removeCollectionListener(CollectionListener* l) const {
00162 if(collectionListeners==NULL)
00163 return;
00164 std::set<CollectionListener*>::iterator it=collectionListeners->find(l);
00165 if(it!=collectionListeners->end()) {
00166 collectionListeners->erase(it);
00167 if(collectionListeners->empty()) {
00168 delete collectionListeners;
00169 collectionListeners=NULL;
00170 }
00171 }
00172 }
00173
00174 bool Collection::isCollectionListener(CollectionListener* l) const {
00175 if(l==NULL)
00176 return false;
00177 if(collectionListeners==NULL)
00178 return false;
00179 std::set<CollectionListener*>::iterator it=collectionListeners->find(l);
00180 return it!=collectionListeners->end();
00181 }
00182
00183 long Collection::toLong() const { throw std::runtime_error("Unable to cast collection to integer value"); }
00184 double Collection::toDouble() const { throw std::runtime_error("Unable to cast collection to floating point value"); }
00185
00186 void Collection::fireEntryAdded(ObjectBase& val) {
00187 if(collectionListeners==NULL)
00188 return;
00189
00190 std::set<CollectionListener*> pls=*collectionListeners;
00191 for(std::set<CollectionListener*>::const_iterator it=pls.begin(); collectionListeners!=NULL && it!=pls.end(); ++it) {
00192
00193 if(collectionListeners->count(*it)>0)
00194 (*it)->plistCollectionEntryAdded(*this,val);
00195 }
00196 }
00197
00198 void Collection::fireEntryRemoved(ObjectBase& val) {
00199 if(collectionListeners==NULL)
00200 return;
00201
00202 std::set<CollectionListener*> pls=*collectionListeners;
00203 for(std::set<CollectionListener*>::const_iterator it=pls.begin(); collectionListeners!=NULL && it!=pls.end(); ++it) {
00204
00205 if(collectionListeners->count(*it)>0)
00206 (*it)->plistCollectionEntryRemoved(*this,val);
00207 }
00208 }
00209
00210 void Collection::fireEntriesChanged() {
00211 if(collectionListeners==NULL)
00212 return;
00213
00214 std::set<CollectionListener*> pls=*collectionListeners;
00215 for(std::set<CollectionListener*>::const_iterator it=pls.begin(); collectionListeners!=NULL && it!=pls.end(); ++it) {
00216
00217 if(collectionListeners->count(*it)>0)
00218 (*it)->plistCollectionEntriesChanged(*this);
00219 }
00220 }
00221
00222 std::string Collection::getIndentationPrefix(xmlNode* node) {
00223 std::string indentStr;
00224 for(xmlNode* cur=node->parent; cur!=NULL; cur=cur->parent) {
00225 if((void*)cur==(void*)node->doc) {
00226 if(indentStr.size()>0)
00227 indentStr=indentStr.substr(0,indentStr.size()-perIndent().size());
00228 break;
00229 }
00230 indentStr+=perIndent();
00231 }
00232 return indentStr;
00233 }
00234
00235 size_t Collection::getIndex(const std::string& name) {
00236 char * endp=0;
00237 long index=strtol(name.c_str(),&endp,0);
00238 if(index<0)
00239 return (size_t)-1;
00240
00241 if(*endp!='\0')
00242 return (size_t)-1;
00243
00244 return index;
00245 }
00246
00247
00248 void AutoCollectionListener::deactivate() {
00249 src.removeCollectionListener(this);
00250 if(const ArrayBase * arr=dynamic_cast<const ArrayBase*>(&src)) {
00251 for(plist::ArrayBase::const_iterator it=arr->begin(); it!=arr->end(); ++it)
00252 if(PrimitiveBase * pb = dynamic_cast<PrimitiveBase*>(*it))
00253 pb->removePrimitiveListener(this);
00254 } else if(const DictionaryBase * dict=dynamic_cast<const DictionaryBase*>(&src)) {
00255 for(plist::DictionaryBase::const_iterator it=dict->begin(); it!=dict->end(); ++it)
00256 if(PrimitiveBase * pb = dynamic_cast<PrimitiveBase*>(it->second))
00257 pb->removePrimitiveListener(this);
00258 } else {
00259 std::cerr << "plist::AutoCollectionListener could not unsubscribe from source entries because source is not a known Collection type" << std::endl;
00260 }
00261 }
00262 void AutoCollectionListener::plistCollectionEntryAdded(plist::Collection& , ObjectBase& primitive) {
00263 if(plist::PrimitiveBase * pb = dynamic_cast<plist::PrimitiveBase*>(&primitive)) {
00264 pb->addPrimitiveListener(this);
00265 if(updateOnNew)
00266 plistValueChanged(*pb);
00267 } else
00268 plistSubCollectionAdded(dynamic_cast<plist::Collection&>(primitive));
00269 }
00270 void AutoCollectionListener::plistCollectionEntryRemoved(plist::Collection& , ObjectBase& primitive) {
00271 if(plist::PrimitiveBase * pb = dynamic_cast<plist::PrimitiveBase*>(&primitive))
00272 pb->removePrimitiveListener(this);
00273 else
00274 plistSubCollectionRemoved(dynamic_cast<plist::Collection&>(primitive));
00275 }
00276 void AutoCollectionListener::plistCollectionEntriesChanged(plist::Collection& ) {
00277 if(const ArrayBase * arr=dynamic_cast<const ArrayBase*>(&src)) {
00278 for(plist::ArrayBase::const_iterator it=arr->begin(); it!=arr->end(); ++it)
00279 if(PrimitiveBase * pb = dynamic_cast<PrimitiveBase*>(*it))
00280 pb->addPrimitiveListener(this);
00281 } else if(const DictionaryBase * dict=dynamic_cast<const DictionaryBase*>(&src)) {
00282 for(plist::DictionaryBase::const_iterator it=dict->begin(); it!=dict->end(); ++it)
00283 if(PrimitiveBase * pb = dynamic_cast<PrimitiveBase*>(it->second))
00284 pb->addPrimitiveListener(this);
00285 } else {
00286 std::cerr << "plist::AutoCollectionListener could not unsubscribe from source entries because source is not a known Collection type" << std::endl;
00287 }
00288 }
00289
00290
00291 bool DictionaryBase::removeEntry(const std::string& name) {
00292 storage_t::iterator it=dict.find(name);
00293 if(it==dict.end())
00294 return false;
00295
00296 ObjectBase* obj=it->second;
00297 dict.erase(it);
00298 comments.erase(name);
00299 fireEntryRemoved(*obj);
00300 return true;
00301 }
00302
00303 bool DictionaryBase::renameEntry(const std::string& oldname, const std::string& newname) {
00304 storage_t::iterator oit=dict.find(oldname);
00305 if(oit==dict.end())
00306 return false;
00307
00308
00309 storage_t::iterator nit=dict.find(newname);
00310 if(nit!=dict.end()) {
00311
00312 ObjectBase* obj=nit->second;
00313 dict.erase(nit);
00314 comments.erase(newname);
00315 fireEntryRemoved(*obj);
00316 }
00317
00318 ObjectBase* val=oit->second;
00319 dict.erase(oit);
00320 dict[newname]=val;
00321
00322
00323 comments_t::iterator cit=comments.find(oldname);
00324 if(cit==comments.end()) {
00325
00326 cit = comments.find(newname);
00327 if(cit!=comments.end())
00328 comments.erase(cit);
00329 } else {
00330
00331 string com = cit->second;
00332 comments.erase(cit);
00333 comments[newname]=com;
00334 }
00335 fireEntriesChanged();
00336 return true;
00337 }
00338
00339 bool DictionaryBase::swapEntry(const std::string& a, const std::string& b) {
00340 storage_t::iterator ait = dict.find(a);
00341 storage_t::iterator bit = dict.find(b);
00342 if(ait==dict.end() && bit==dict.end())
00343 return false;
00344 else if(ait==dict.end())
00345 return renameEntry(b,a);
00346 else if(bit==dict.end())
00347 return renameEntry(a,b);
00348
00349 swap(ait->second,bit->second);
00350
00351
00352 comments_t::iterator acit = comments.find(a);
00353 comments_t::iterator bcit = comments.find(b);
00354 if(acit != comments.end()) {
00355 if(bcit != comments.end()) {
00356
00357 swap(acit->second,bcit->second);
00358 } else {
00359
00360 string com = acit->second;
00361 comments.erase(acit);
00362 comments[b]=com;
00363 }
00364 } else if(bcit != comments.end()) {
00365
00366 string com = bcit->second;
00367 comments.erase(bcit);
00368 comments[a]=com;
00369 }
00370 fireEntriesChanged();
00371 return true;
00372 }
00373
00374 ObjectBase* DictionaryBase::resolveEntry(const std::string& path) const {
00375
00376 const_iterator it=dict.find(path);
00377 if(it!=dict.end())
00378 return it->second;
00379
00380
00381 string::size_type p;
00382 it=getSubEntry(path,p);
00383 if(it==dict.end()) {
00384
00385 return NULL;
00386 }
00387
00388
00389 const Collection* d=dynamic_cast<const Collection*>(it->second);
00390 return d->resolveEntry(path.substr(p+1));
00391 }
00392
00393 void DictionaryBase::setComment(const std::string& name, const std::string& comment) {
00394 if(comment.size()==0)
00395 comments.erase(name);
00396 else if(comment.find("--")!=std::string::npos)
00397 throw std::runtime_error("per XML spec, comment string ('"+comment+"' cannot contain '--')");
00398 else
00399 comments[name]=comment;
00400 }
00401
00402 const std::string& DictionaryBase::getComment(const std::string& name) const {
00403 storage_t::const_iterator it=dict.find(name);
00404 if(it==dict.end())
00405 return emptyStr();
00406
00407 comments_t::const_iterator cit=comments.find(name);
00408 return (cit!=comments.end()) ? cit->second : emptyStr();
00409 }
00410
00411 void DictionaryBase::loadXML(xmlNode* node) {
00412
00413 if(node==NULL)
00414 return;
00415 if(!xNodeHasName(node,"dict"))
00416 throw bad_format(node,"Dictionary::loadXML expected <dict> value, got "+std::string((const char*)xNodeGetName(node)));
00417
00418 LoadSavePolicy origLoadPolicy=loadPolicy;
00419 xmlChar * att = xmlGetProp(node,(const xmlChar*)"load");
00420 if(att!=NULL) {
00421 if(xmlStrcasecmp(att, (const xmlChar*)"fixed")==0) {
00422 setLoadPolicy(Collection::FIXED);
00423 } else if(xmlStrcasecmp(att, (const xmlChar*)"union")==0) {
00424 setLoadPolicy(Collection::UNION);
00425 } else if(xmlStrcasecmp(att, (const xmlChar*)"intersect")==0) {
00426 setLoadPolicy(Collection::INTERSECT);
00427 } else if(xmlStrcasecmp(att, (const xmlChar*)"sync")==0) {
00428 setLoadPolicy(Collection::SYNC);
00429 } else {
00430 std::cerr << "WARNING: unknown plist::Dictionary load mode '" << (char*)att << "', ignoring..." << std::endl;
00431 std::string file;
00432 xmlChar* uri = xmlNodeGetBase(node->doc,node);
00433 if(uri!=NULL && uri[0]!='\0')
00434 file = std::string(" of ") + (char*)uri + std::string(":");
00435 else
00436 file = " at line ";
00437 xmlFree(uri);
00438 std::cerr << " (from" << file << xmlGetLineNo(node) << ")" << std::endl;
00439 }
00440 }
00441 xmlFree(att); att=NULL;
00442
00443 try {
00444 std::string comment;
00445 std::set<std::string> seen;
00446
00447 for(xmlNode* cur = skipToElement(node->children,comment); cur!=NULL; cur = skipToElement(cur->next,comment)) {
00448
00449
00450 xmlNode * k=cur;
00451 if(xmlStrcmp(k->name, (const xmlChar *)"key"))
00452 throw bad_format(k,"Dictionary format error: expect data in pairs of key and value (two values found in a row)");
00453 cur=skipToElement(cur->next);
00454
00455
00456 xmlNode * v=cur;
00457 if(v==NULL)
00458 throw bad_format(cur,"Dictionary format error: expect data in pairs of key and value (dictionary ended with hanging key)");
00459 if(!xmlStrcmp(v->name, (const xmlChar *)"key"))
00460 throw bad_format(v,"Dictionary format error: expect data in pairs of key and value (two keys found in a row)");
00461
00462
00463 xmlChar* cont=xmlNodeGetContent(k);
00464 string key=(const char*)cont;
00465 xmlFree(cont);
00466 seen.insert(key);
00467 loadXMLNode(key,v,comment);
00468 }
00469 if((loadPolicy&REMOVALS) && seen.size()!=size()) {
00470 std::set<std::string> rem;
00471 for(const_iterator it=begin(); it!=end(); ++it) {
00472 if(seen.find(it->first)==seen.end())
00473 rem.insert(it->first);
00474 }
00475 for(std::set<std::string>::const_iterator it=rem.begin(); it!=rem.end(); ++it)
00476 removeEntry(*it);
00477 }
00478 } catch(...) {
00479 setLoadPolicy(origLoadPolicy);
00480 throw;
00481 }
00482 setLoadPolicy(origLoadPolicy);
00483 }
00484
00485 void DictionaryBase::saveXML(xmlNode* node, bool onlyOverwrite, std::set<std::string>& seen) const {
00486
00487 if(node==NULL)
00488 return;
00489
00490
00491 xmlNodeSetName(node,(const xmlChar*)"dict");
00492
00493
00494 std::string indentStr=getIndentationPrefix(node);
00495
00496
00497 std::string comment;
00498
00499
00500 xmlNode* prev=node->children;
00501 for(xmlNode* cur = skipToElement(node->children,comment); cur!=NULL; cur = skipToElement(cur,comment)) {
00502
00503
00504 xmlNode * k=cur;
00505 if(xmlStrcmp(k->name, (const xmlChar *)"key")) {
00506 cur = k->next;
00507 xmlUnlinkNode(k);
00508 xmlFreeNode(k);
00509 continue;
00510 }
00511 cur=skipToElement(cur->next);
00512
00513
00514 xmlNode * v=cur;
00515 if(v==NULL) {
00516 xmlUnlinkNode(k);
00517 xmlFreeNode(k);
00518 break;
00519 }
00520 if(!xmlStrcmp(v->name, (const xmlChar *)"key"))
00521 throw bad_format(v,"Dictionary format error: expect data in pairs of key and value (two keys found in a row)");
00522
00523 xmlChar* cont=xmlNodeGetContent(k);
00524 std::string key=(const char*)cont;
00525 xmlFree(cont);
00526 if(!saveOverXMLNode(k,v,key,comment,indentStr,seen)) {
00527 cur=xNodeGetNextNode(cur);
00528 if(savePolicy&REMOVALS) {
00529 while(prev!=cur) {
00530 xmlNode* n=prev;
00531 prev=xNodeGetNextNode(prev);
00532 xmlUnlinkNode(n);
00533 xmlFreeNode(n);
00534 }
00535 } else {
00536 if(warnUnused && savePolicy==FIXED)
00537 cerr << "Warning: saving over existing plist dictionary, key '" << key << "' does not match a registered variable. Ignoring..." << endl;
00538 }
00539 prev=cur;
00540 }
00541 prev=cur=xNodeGetNextNode(cur);
00542 }
00543
00544 if(!onlyOverwrite && seen.size()!=dict.size()) {
00545
00546 for(xmlNode* cur=node->last; cur!=NULL && cur->type==XML_TEXT_NODE; cur=node->last) {
00547 xmlUnlinkNode(cur);
00548 xmlFreeNode(cur);
00549 }
00550 size_t longestKeyLen = getLongestKeyLen(NULL,1);
00551
00552
00553
00554 for(storage_t::const_iterator it=dict.begin(); it!=dict.end(); ++it) {
00555 if(seen.find(it->first)==seen.end()) {
00556
00557 saveXMLNode(node,it->first,it->second,indentStr,longestKeyLen);
00558 }
00559 }
00560 std::string parentIndent;
00561 if(indentStr.size()>=perIndent().size())
00562 parentIndent=indentStr.substr(perIndent().size());
00563 xmlAddChild(node,xmlNewText((const xmlChar*)("\n"+parentIndent).c_str()));
00564 }
00565 }
00566
00567 std::string DictionaryBase::toString() const {
00568 stringstream s;
00569 s << *this;
00570 return s.str();
00571 }
00572
00573 unsigned int DictionaryBase::getLongestKeyLen(const regex_t* reg, unsigned int depth) const {
00574 if(depth==0)
00575 return 0;
00576 size_t longest=0;
00577 size_t seplen=subCollectionSep().size();
00578 for(DictionaryBase::const_iterator it=begin(); it!=end(); ++it) {
00579 if(reg!=NULL && regexec(reg,it->first.c_str(),0,NULL,0)!=0)
00580 continue;
00581 size_t cur=it->first.size();
00582 if(Collection* dp=dynamic_cast<Collection*>(it->second))
00583 cur+=dp->getLongestKeyLen(reg,depth-1)+seplen;
00584 longest=std::max(longest,cur);
00585 }
00586 return longest;
00587 }
00588
00589 DictionaryBase::iterator DictionaryBase::getSubEntry(const std::string& name, std::string::size_type& seppos) {
00590 seppos=name.find(subCollectionSep());
00591 if(seppos==string::npos)
00592 return dict.end();
00593 iterator it=dict.find(name.substr(0,seppos));
00594 if(it==dict.end())
00595 return dict.end();
00596 const Collection* d=dynamic_cast<const Collection*>(it->second);
00597 if(d==NULL)
00598 return dict.end();
00599 return it;
00600 }
00601 DictionaryBase::const_iterator DictionaryBase::getSubEntry(const std::string& name, std::string::size_type& seppos) const {
00602 seppos=name.find(subCollectionSep());
00603 if(seppos==string::npos)
00604 return dict.end();
00605 const_iterator it=dict.find(name.substr(0,seppos));
00606 if(it==dict.end())
00607 return dict.end();
00608 const Collection* d=dynamic_cast<const Collection*>(it->second);
00609 if(d==NULL)
00610 return dict.end();
00611 return it;
00612 }
00613
00614 void DictionaryBase::clear() {
00615 storage_t::size_type s=dict.size();
00616
00617 std::set<ObjectBase*> refs=myRef;
00618 dict.clear();
00619 myRef.clear();
00620 comments.clear();
00621 if(s>0)
00622 fireEntriesChanged();
00623 for(std::set<ObjectBase*>::iterator it=refs.begin(); it!=refs.end(); ++it)
00624 delete *it;
00625 }
00626
00627 void DictionaryBase::takeObject(const std::string& , ObjectBase* obj) {
00628 myRef.insert(obj);
00629 }
00630
00631 void DictionaryBase::fireEntryRemoved(ObjectBase& val) {
00632 Collection::fireEntryRemoved(val);
00633 std::set<ObjectBase*>::iterator it=myRef.find(&val);
00634 if(it!=myRef.end()) {
00635 myRef.erase(it);
00636 delete &val;
00637 }
00638 }
00639
00640 void DictionaryBase::cloneMyRef() {
00641 for(iterator dit=dict.begin(); dit!=dict.end(); ++dit) {
00642 std::set<ObjectBase*>::iterator rit=myRef.find(dit->second);
00643 if(rit!=myRef.end()) {
00644 myRef.erase(rit);
00645 myRef.insert(dit->second=dynamic_cast<ObjectBase*>((dit->second)->clone()));
00646 }
00647 }
00648
00649
00650
00651
00652
00653
00654
00655
00656
00657
00658
00659
00660
00661
00662
00663
00664
00665
00666
00667
00668
00669 }
00670
00671 bool DictionaryBase::saveOverXMLNode(xmlNode* k, xmlNode* val, const std::string& key, std::string comment, const std::string& indentStr, std::set<std::string>& seen) const {
00672
00673 storage_t::const_iterator it=findEntry(key);
00674 if(it==dict.end())
00675 return false;
00676 if(comment.size()==0) {
00677 bool isSub=dynamic_cast<const Collection*>(it->second);
00678 if(isSub)
00679 if(const ArrayBase* arr=dynamic_cast<const ArrayBase*>(it->second))
00680 isSub = !arr->getSaveInlineStyle();
00681 bool isFirst=true;
00682 const std::string indentedNewline="\n"+indentStr;
00683 comments_t::const_iterator cit=comments.find(key);
00684 if(cit!=comments.end()) {
00685 while(k->prev!=NULL && xNodeIsText(k->prev)) {
00686 xmlNode* n=k->prev;
00687 xmlUnlinkNode(n);
00688 xmlFreeNode(n);
00689 }
00690 }
00691 if(!saveCondensed) {
00692 const std::string headline=("======== "+it->first+" ========");
00693 if(isSub && static_cast<const Collection*>(it->second)->size()>0) {
00694 isFirst=(skipToElement(k->parent->children)==k);
00695 xmlAddPrevSibling(k,xmlNewText((const xmlChar*)(isFirst ? indentedNewline : indentedNewline+indentedNewline).c_str()));
00696 xmlAddPrevSibling(k,xmlNewComment((const xmlChar*)headline.c_str()));
00697 }
00698 }
00699 if(cit!=comments.end()) {
00700 if(isSub || cit->second.find(key)<KEY_IN_COMMENT_MAX_POS)
00701 comment=cit->second;
00702 else
00703 comment=key+": "+cit->second;
00704 string::size_type pos=comment.rfind('\n');
00705 while(pos!=string::npos) {
00706 if(comment.compare(pos+1,indentStr.size(),indentStr)!=0)
00707 comment.insert(pos+1,indentStr);
00708 if(pos==0)
00709 break;
00710 pos = comment.rfind('\n',pos-1);
00711 }
00712 if(!isSub)
00713 isFirst=(skipToElement(k->parent->children)==k);
00714 xmlAddPrevSibling(k,xmlNewText((const xmlChar*)(isFirst ? indentedNewline : indentedNewline+indentedNewline).c_str()));
00715 xmlAddPrevSibling(k,xmlNewComment((const xmlChar*)comment.c_str()));
00716 xmlAddPrevSibling(k,xmlNewText((const xmlChar*)indentedNewline.c_str()));
00717 }
00718 }
00719 it->second->saveXML(val);
00720 if(seen.find(key)!=seen.end()) {
00721 std::cerr << "WARNING: plist::Dictionary found duplicate key " << key << " during save" << std::endl;
00722 } else {
00723 seen.insert(key);
00724 }
00725 return true;
00726 }
00727
00728 void DictionaryBase::saveXMLNode(xmlNode* node, const std::string& key, const ObjectBase* val, const std::string& indentStr, size_t longestKeyLen) const {
00729 bool isSub=dynamic_cast<const Collection*>(val);
00730 if(isSub)
00731 if(const ArrayBase* arr=dynamic_cast<const ArrayBase*>(val))
00732 isSub = !arr->getSaveInlineStyle();
00733 bool isFirst=(node->children==NULL);
00734 const std::string indentedNewline="\n"+indentStr;
00735 if(!saveCondensed) {
00736 const std::string headline=("======== "+key+" ========");
00737 if(isSub && static_cast<const Collection*>(val)->size()>0) {
00738 xmlAddChild(node,xmlNewText((const xmlChar*)(isFirst ? indentedNewline : indentedNewline+indentedNewline).c_str()));
00739 xmlAddChild(node,xmlNewComment((const xmlChar*)headline.c_str()));
00740 }
00741 }
00742 std::string comment;
00743 comments_t::const_iterator cit=comments.find(key);
00744 if(cit!=comments.end()) {
00745 if(isSub || cit->second.find(key)<KEY_IN_COMMENT_MAX_POS)
00746 comment=cit->second;
00747 else
00748 comment=key+": "+cit->second;
00749 string::size_type pos=comment.rfind('\n');
00750 while(pos!=string::npos) {
00751 if(comment.compare(pos+1,indentStr.size(),indentStr)!=0)
00752 comment.insert(pos+1,indentStr);
00753 if(pos==0)
00754 break;
00755 pos = comment.rfind('\n',pos-1);
00756 }
00757 xmlAddChild(node,xmlNewText((const xmlChar*)(isSub || isFirst ? indentedNewline : indentedNewline+indentedNewline).c_str()));
00758 xmlAddChild(node,xmlNewComment((const xmlChar*)comment.c_str()));
00759 }
00760 xmlAddChild(node,xmlNewText((const xmlChar*)indentedNewline.c_str()));
00761 xmlNode* k=xmlNewChild(node,NULL,(const xmlChar*)"key",(const xmlChar*)key.c_str());
00762 if(k==NULL)
00763 throw bad_format(node,"Error: plist Dictionary xml error on saving key");
00764 string space(longestKeyLen-string_util::utf8len(key)+1,' ');
00765 xmlAddChild(node,xmlNewText((const xmlChar*)space.c_str()));
00766 xmlNode* v=xmlNewChild(node,NULL,(const xmlChar*)"",NULL);
00767 if(v==NULL)
00768 throw bad_format(node,"Error: plist Dictionary xml error on saving value");
00769 val->saveXML(v);
00770 }
00771
00772
00773 bool ArrayBase::removeEntry(size_t index) {
00774 if(index>=arr.size())
00775 return false;
00776 storage_t::iterator it=arr.begin();
00777 advance(it,index);
00778 ObjectBase* obj=*it;
00779 arr.erase(it);
00780 comments.erase(index);
00781 fireEntryRemoved(*obj);
00782 return true;
00783 }
00784
00785 ObjectBase* ArrayBase::resolveEntry(const std::string& path) const {
00786 size_t index=getIndex(path);
00787 if(index<size())
00788 return &getEntry(index);
00789 std::string::size_type p;
00790 const_iterator it=getSubEntry(path,p);
00791 if(it==arr.end())
00792 return NULL;
00793 const Collection * d=dynamic_cast<const Collection*>(*it);
00794 return d->resolveEntry(path.substr(p+1));
00795 }
00796
00797 void ArrayBase::clear() {
00798 storage_t::size_type s=arr.size();
00799
00800 std::set<ObjectBase*> refs=myRef;
00801 arr.clear();
00802 comments.clear();
00803 myRef.clear();
00804 if(s>0)
00805 fireEntriesChanged();
00806 for(std::set<ObjectBase*>::iterator it=refs.begin(); it!=refs.end(); ++it)
00807 delete *it;
00808 }
00809
00810 void ArrayBase::setComment(size_t index, const std::string& comment) {
00811 if(comment.size()==0)
00812 comments.erase(index);
00813 else if(comment.find("--")!=std::string::npos)
00814 throw std::runtime_error("per XML spec, comment string ('"+comment+"' cannot contain '--')");
00815 else
00816 comments[index]=comment;
00817 }
00818
00819 const std::string& ArrayBase::getComment(size_t index) const {
00820 comments_t::const_iterator it=comments.find(index);
00821 if(it==comments.end())
00822 return emptyStr();
00823 else
00824 return it->second;
00825 }
00826
00827 void ArrayBase::loadXML(xmlNode* node) {
00828
00829 if(node==NULL)
00830 return;
00831 if(!xNodeHasName(node,"array"))
00832 throw bad_format(node,"Array::loadXML expected <array> value, got "+std::string((const char*)xNodeGetName(node)));
00833
00834 std::string comment;
00835 unsigned int i=0;
00836 for(xmlNode* cur = skipToElement(xNodeGetChildren(node),comment); cur!=NULL; cur = skipToElement(xNodeGetNextNode(cur),comment)) {
00837 if(!loadXMLNode(i++, cur, comment))
00838 break;
00839 }
00840 if(loadPolicy&REMOVALS) {
00841 while(i<size())
00842 removeEntry(size()-1);
00843 }
00844 }
00845
00846 void ArrayBase::saveXML(xmlNode* node) const {
00847
00848 if(node==NULL)
00849 return;
00850
00851
00852 xmlNodeSetName(node,(const xmlChar*)"array");
00853
00854
00855 std::string indentStr=getIndentationPrefix(node);
00856 std::string parentIndent;
00857 if(indentStr.size()>=perIndent().size())
00858 parentIndent=indentStr.substr(perIndent().size());
00859
00860
00861 std::string comment;
00862
00863
00864 unsigned int i=0;
00865
00866
00867 xmlNode * prev=xNodeGetChildren(node);
00868 for(xmlNode* cur = skipToElement(prev,comment); cur!=NULL; cur = skipToElement(cur,comment)) {
00869
00870 if(i==arr.size()) {
00871 if(savePolicy&REMOVALS) {
00872 while(prev!=NULL) {
00873 xmlNode* n=prev;
00874 prev=xNodeGetNextNode(prev);
00875 xmlUnlinkNode(n);
00876 xmlFreeNode(n);
00877 }
00878 } else {
00879 if(warnUnused && savePolicy==FIXED)
00880 std::cerr << "Warning: plist::Array ignoring extraneous items in destination during save..." << std::endl;
00881 }
00882 break;
00883 }
00884 if(comment.size()==0) {
00885 comments_t::const_iterator cit=comments.find(i);
00886 if(cit!=comments.end()) {
00887 std::stringstream buf;
00888 buf << i;
00889 if( cit->second.compare(0,buf.str().size(),buf.str())==0)
00890 comment=cit->second;
00891 else {
00892 comment=buf.str();
00893 comment+=": "+cit->second;
00894 }
00895 xmlAddPrevSibling(cur,xmlNewText((const xmlChar*)("\n"+indentStr).c_str()));
00896 std::string::size_type pos=comment.rfind('\n');
00897 while(pos!=std::string::npos) {
00898 if(comment.compare(pos+1,indentStr.size(),indentStr)!=0)
00899 comment.insert(pos+1,indentStr);
00900 if(pos==0)
00901 break;
00902 pos = comment.rfind('\n',pos-1);
00903 }
00904 xmlAddPrevSibling(cur,xmlNewComment((const xmlChar*)comment.c_str()));
00905 xmlAddPrevSibling(cur,xmlNewText((const xmlChar*)("\n"+indentStr).c_str()));
00906 }
00907 }
00908 arr[i++]->saveXML(cur);
00909 prev=cur=xNodeGetNextNode(cur);
00910 }
00911
00912 if(!(savePolicy&ADDITIONS))
00913 return;
00914
00915 bool hadUnsaved = (i<arr.size());
00916 for(; i<arr.size(); ++i) {
00917 comments_t::const_iterator cit=comments.find(i);
00918 if(cit!=comments.end()) {
00919 std::stringstream buf;
00920 buf << i;
00921 if( cit->second.compare(0,buf.str().size(),buf.str())==0)
00922 comment=cit->second;
00923 else {
00924 comment=buf.str();
00925 comment+=": "+cit->second;
00926 }
00927 xmlAddChild(node,xmlNewText((const xmlChar*)("\n"+indentStr).c_str()));
00928 std::string::size_type pos=comment.rfind('\n');
00929 while(pos!=std::string::npos) {
00930 if(comment.compare(pos+1,indentStr.size(),indentStr)!=0)
00931 comment.insert(pos+1,indentStr);
00932 if(pos==0)
00933 break;
00934 pos = comment.rfind('\n',pos-1);
00935 }
00936 xmlAddChild(node,xmlNewComment((const xmlChar*)comment.c_str()));
00937 }
00938 if(saveInlineStyle)
00939 xmlAddChild(node,xmlNewText((const xmlChar*)" "));
00940 else
00941 xmlAddChild(node,xmlNewText((const xmlChar*)("\n"+indentStr).c_str()));
00942 xmlNode* v=xmlNewChild(node,NULL,(const xmlChar*)"",NULL);
00943 if(v==NULL)
00944 throw bad_format(node,"Error: plist Array xml error on saving value");
00945 arr[i]->saveXML(v);
00946 }
00947 if(hadUnsaved) {
00948 if(saveInlineStyle)
00949 xmlAddChild(node,xmlNewText((const xmlChar*)" "));
00950 else
00951 xmlAddChild(node,xmlNewText((const xmlChar*)("\n"+parentIndent).c_str()));
00952 }
00953 }
00954
00955 std::string ArrayBase::toString() const {
00956 std::stringstream s;
00957 s << *this;
00958 return s.str();
00959 }
00960
00961 unsigned int ArrayBase::getLongestKeyLen(const regex_t* reg, unsigned int depth) const {
00962 if(depth==0)
00963 return 0;
00964 size_t longest=0;
00965 size_t seplen=subCollectionSep().size();
00966 for(size_t i=0; i<size(); ++i) {
00967 std::stringstream s;
00968 s << i;
00969 if(reg!=NULL && regexec(reg,s.str().c_str(),0,NULL,0)!=0)
00970 continue;
00971 size_t cur=s.str().size();
00972 if(Collection* dp=dynamic_cast<Collection*>(arr[i]))
00973 cur+=dp->getLongestKeyLen(reg,depth-1)+seplen;
00974 longest=std::max(longest,cur);
00975 }
00976 return longest;
00977 }
00978
00979 void ArrayBase::takeObject(size_t , ObjectBase* obj) {
00980 myRef.insert(obj);
00981 }
00982
00983 void ArrayBase::fireEntryRemoved(ObjectBase& val) {
00984 Collection::fireEntryRemoved(val);
00985 std::set<ObjectBase*>::iterator it=myRef.find(&val);
00986 if(it!=myRef.end()) {
00987 myRef.erase(it);
00988 delete &val;
00989 }
00990 }
00991
00992 ArrayBase::iterator ArrayBase::getSubEntry(const std::string& name, std::string::size_type& seppos) {
00993 seppos=name.find(subCollectionSep());
00994 if(seppos==std::string::npos)
00995 return arr.end();
00996 size_t index=getIndex(name.substr(0,seppos));
00997 if(index>=size())
00998 return arr.end();
00999 iterator it=arr.begin();
01000 advance(it,index);
01001 const Collection* d=dynamic_cast<const Collection*>(*it);
01002 if(d==NULL)
01003 return arr.end();
01004 return it;
01005 }
01006 ArrayBase::const_iterator ArrayBase::getSubEntry(const std::string& name, std::string::size_type& seppos) const {
01007 seppos=name.find(subCollectionSep());
01008 if(seppos==std::string::npos)
01009 return arr.end();
01010 size_t index=getIndex(name.substr(0,seppos));
01011 if(index>=size())
01012 return arr.end();
01013 const_iterator it=arr.begin();
01014 advance(it,index);
01015 const Collection* d=dynamic_cast<const Collection*>(*it);
01016 if(d==NULL)
01017 return arr.end();
01018 return it;
01019 }
01020
01021 void ArrayBase::cloneMyRef() {
01022 for(iterator dit=arr.begin(); dit!=arr.end(); ++dit) {
01023 std::set<ObjectBase*>::iterator rit=myRef.find(*dit);
01024 if(rit!=myRef.end()) {
01025 myRef.erase(rit);
01026 myRef.insert(*dit=dynamic_cast<ObjectBase*>((*dit)->clone()));
01027 }
01028 }
01029 }
01030
01031 std::ostream& filteredDisplay(std::ostream& os, const ObjectBase& c, const std::string& sel, int selType, unsigned int depth) {
01032 if(sel.size()==0)
01033 return filteredDisplay(os,c,NULL,depth);
01034 regex_t r;
01035 if(regcomp(&r,sel.c_str(),selType|REG_NOSUB)==0)
01036 filteredDisplay(os,c,&r,depth);
01037 regfree(&r);
01038 return os;
01039 }
01040
01041 std::ostream& filteredDisplay(std::ostream& os, const ObjectBase& c, const regex_t* reg, unsigned int depth) {
01042 unsigned int seplen=Collection::subCollectionSep().size();
01043 unsigned int out=0;
01044
01045 if(const ArrayBase* a=dynamic_cast<const ArrayBase*>(&c)) {
01046 unsigned int longest=std::max(a->getLongestKeyLen(reg,depth),static_cast<unsigned int>(os.width()));
01047 for(unsigned long i=0; i<a->size(); ++i) {
01048 stringstream ns;
01049 ns << i;
01050 if(reg!=NULL && regexec(reg,ns.str().c_str(),0,NULL,0)!=0)
01051 continue;
01052 out++;
01053 if(depth==0)
01054 return os << right << setw(longest) << "" << " = [...]" << endl;
01055 if(Collection* dp=dynamic_cast<Collection*>(&(*a)[i])) {
01056 stringstream ss;
01057 ss << left << std::setw(longest-snprintf(NULL,0,"%lu",i)-seplen);
01058 filteredDisplay(ss,*dp,reg,depth-1);
01059 std::string line;
01060 for(getline(ss,line); ss; std::getline(ss,line))
01061 os << (ns.str() + Collection::subCollectionSep() + line) << std::endl;
01062 } else {
01063 os << std::left << std::setw(longest) << ns.str() << " = " << (*a)[i] << std::endl;
01064 }
01065 }
01066 if(out==0)
01067 return os << right << setw(longest) << "" << " = (empty array)" << endl;
01068
01069 } else if(const DictionaryBase* d=dynamic_cast<const DictionaryBase*>(&c)) {
01070 unsigned int longest=std::max(d->getLongestKeyLen(reg,depth),static_cast<unsigned int>(os.width()));
01071 for(DictionaryBase::storage_t::const_iterator it=d->begin(); it!=d->end(); ++it) {
01072 if(reg!=NULL && regexec(reg,it->first.c_str(),0,NULL,0)!=0)
01073 continue;
01074 out++;
01075 if(depth==0)
01076 return os << right << setw(longest) << "" << " = [...]" << endl;
01077 if(Collection* dp=dynamic_cast<Collection*>(it->second)) {
01078 stringstream ss;
01079 ss << left << setw(longest-it->first.size()-seplen);
01080 filteredDisplay(ss,*dp,reg,depth-1);
01081 string line;
01082 for(getline(ss,line); ss; getline(ss,line))
01083 os << (it->first + Collection::subCollectionSep() + line) << endl;
01084 } else {
01085 os << left << setw(longest) << it->first << " = " << *it->second << endl;
01086 }
01087 }
01088 if(out==0)
01089 return os << right << setw(longest) << "" << " = (empty dictionary)" << endl;
01090
01091 } else {
01092 os << c.toString();
01093 }
01094 return os;
01095 }
01096
01097 }
01098
01099
01100
01101
01102
01103