plist.ccGo to the documentation of this file.00001 #include "plist.h"
00002 #include <libxml/xmlmemory.h>
00003 #include <libxml/parser.h>
00004 #include <iomanip>
00005
00006 using namespace std;
00007
00008 namespace plist {
00009
00010 ObjectBase::ObjectBase()
00011 : XMLLoadSave()
00012 {}
00013
00014 ObjectBase::~ObjectBase() {}
00015
00016 void ObjectBase::setParseTree(xmlDoc * doc) const {
00017 XMLLoadSave::setParseTree(doc);
00018 if(xmldocument==NULL)
00019 return;
00020 xmlNodePtr cur = xmlNewNode(NULL,(const xmlChar*)"plist");
00021 xmlNewProp(cur,(const xmlChar*)"version",(const xmlChar*)"1.0");
00022 xmlDocSetRootElement(xmldocument,cur);
00023 xmlCreateIntSubset(xmldocument,(const xmlChar*)"plist",(const xmlChar*)"-//Apple Computer//DTD PLIST 1.0//EN",(const xmlChar*)"http://www.apple.com/DTDs/PropertyList-1.0.dtd");
00024 }
00025
00026 xmlNode* ObjectBase::FindRootXMLElement(xmlDoc* doc) const {
00027 if(doc==NULL)
00028 return NULL;
00029 xmlNode* root=XMLLoadSave::FindRootXMLElement(doc);
00030 string filename;
00031 if(doc->name!=NULL && doc->name[0]!='\0') {
00032 filename="document '";
00033 filename+=doc->name;
00034 filename+="' ";
00035 }
00036 if (root == NULL)
00037 throw bad_format(root,"Error: plist read empty document");
00038 if (xmlStrcmp(root->name, (const xmlChar *)"plist"))
00039 throw bad_format(root,"Error: plist read document of the wrong type, root node != plist");
00040 if (!xmlHasProp(root,(const xmlChar*)"version"))
00041 cerr << "Warning: plist " << filename << "does not carry version number, assuming 1.0" << endl;
00042 else {
00043 xmlChar* strv=xmlGetProp(root,(const xmlChar*)"version");
00044 double version=strtod((const char*)strv,NULL);
00045 if(version>1.0)
00046 cerr << "WARNING: plist " << filename << "is version " << strv << ", this software only knows 1.0. Trying anyway..." << endl;
00047 if(version==0)
00048 cerr << "WARNING: plist " << filename << "seems to have invalid version '" << strv << "', this software only knows 1.0. Trying anyway..." << endl;
00049 xmlFree(strv);
00050 }
00051
00052
00053 xmlNode* cur=root->children;
00054 while(cur!=NULL && cur->type!=XML_ELEMENT_NODE)
00055 cur=cur->next;
00056 if(cur==NULL)
00057 cur = xmlNewChild(root,NULL,(const xmlChar*)"",NULL);
00058 return cur;
00059 }
00060
00061 int ObjectBase::xStrEqual(const xChar* a, const xChar* b) {
00062 return xmlStrEqual(a,b);
00063 }
00064 int ObjectBase::xStrCaseEqual(const xChar* a, const xChar* b) {
00065 return !xmlStrcasecmp(a,b);
00066 }
00067 ObjectBase::xChar* ObjectBase::xNodeGetContent(xmlNode* node) {
00068 return xmlNodeGetContent(node);
00069 }
00070 void ObjectBase::xNodeSetContent(xmlNode* node, const xChar* content) {
00071 xmlNodeSetContent(node,content);
00072 }
00073 const ObjectBase::xChar* ObjectBase::xNodeGetName(xmlNode* node) {
00074 return node->name;
00075 }
00076 bool ObjectBase::xNodeHasName(xmlNode* node, const char* name) {
00077 return xmlStrEqual(node->name,(const xmlChar*)name);
00078 }
00079 void ObjectBase::xNodeSetName(xmlNode* node, const xChar* name) {
00080 xmlNodeSetName(node,name);
00081 }
00082 xmlAttr* ObjectBase::xHasProperty(xmlNode* node, const xChar* name) {
00083 return xmlHasProp(node,name);
00084 }
00085 ObjectBase::xChar* ObjectBase::xGetProperty(xmlNode* node, const xChar* name) {
00086 return xmlGetProp(node,name);
00087 }
00088 long ObjectBase::xGetLineNo(xmlNode* node) {
00089 return xmlGetLineNo(node);
00090 }
00091 void ObjectBase::xFree(void* ptr) {
00092 xmlFree(ptr);
00093 }
00094
00095 Dictionary::~Dictionary() {
00096 delete dictionaryListeners;
00097 dictionaryListeners=NULL;
00098 }
00099
00100 void Dictionary::addEntry(const std::string& name, ObjectBase& val) {
00101 dict_t::iterator it=dict.find(name);
00102 if(it!=dict.end()) {
00103 cerr << "Warning: addEntry("<<name<<","<<val<<") conflicts with previous addEntry("<<name<<","<<val<<")"<<endl;
00104 cerr << " (use setEntry() if you expect you might need to overwrite)" << endl;
00105 it->second=&val;
00106 return;
00107 }
00108 dict[name]=&val;
00109 }
00110
00111 void Dictionary::addEntry(const std::string& name, ObjectBase& val, const std::string& comment) {
00112 comments[name]=comment;
00113 dict_t::iterator it=dict.find(name);
00114 if(it!=dict.end()) {
00115 cerr << "Warning: addEntry("<<name<<","<<val<<") conflicts with previous addEntry("<<name<<","<<val<<")"<<endl;
00116 cerr << " (use setEntry() if you expect you might need to overwrite)" << endl;
00117 it->second=&val;
00118 return;
00119 }
00120 dict[name]=&val;
00121 }
00122
00123 const std::string& Dictionary::getComment(const std::string& name) const {
00124 comments_t::const_iterator it=comments.find(name);
00125 static const std::string empty;
00126 return (it!=comments.end()) ? it->second : empty;
00127 }
00128
00129 ObjectBase* Dictionary::findEntry(const std::string& name) const {
00130
00131 dict_t::const_iterator it=dict.find(name);
00132 if(it!=dict.end())
00133 return it->second;
00134
00135
00136 size_t p=name.find(".");
00137 if(p==string::npos)
00138 return NULL;
00139 it=dict.find(name.substr(0,p));
00140 if(it==dict.end())
00141 return NULL;
00142 const Dictionary* d=dynamic_cast<const Dictionary*>(it->second);
00143 if(d==NULL)
00144 return NULL;
00145
00146
00147 return d->findEntry(name.substr(p+1));
00148 }
00149
00150 void Dictionary::LoadXML(xmlNode* node) {
00151
00152 if(node==NULL)
00153 return;
00154
00155 std::string comment;
00156
00157
00158 for(xmlNode* cur = skipToElement(node->children,comment); cur!=NULL; cur = skipToElement(cur->next,comment)) {
00159
00160
00161 xmlNode * k=cur;
00162 if(xmlStrcmp(k->name, (const xmlChar *)"key"))
00163 throw bad_format(k,"Dictionary format error: expect data in pairs of key and value (two values found in a row)");
00164 cur=skipToElement(cur->next);
00165
00166
00167 xmlNode * v=cur;
00168 if(v==NULL)
00169 throw bad_format(cur,"Dictionary format error: expect data in pairs of key and value (dictionary ended with hanging key)");
00170 if(!xmlStrcmp(v->name, (const xmlChar *)"key"))
00171 throw bad_format(v,"Dictionary format error: expect data in pairs of key and value (two keys found in a row)");
00172
00173
00174 xmlChar* cont=xmlNodeGetContent(k);
00175 string key=(const char*)cont;
00176 xmlFree(cont);
00177 dict_t::const_iterator it=dict.find(key);
00178 if(it==dict.end()) {
00179 if(warnUnused)
00180 cerr << "Warning: reading plist dictionary, key '" << key << "' does not match a registered variable. Ignoring..." << endl;
00181 continue;
00182 }
00183 if(comment.size()!=0)
00184 setComment(key,comment);
00185 it->second->LoadXML(v);
00186 }
00187 }
00188
00189 void Dictionary::SaveXML(xmlNode* node) const {
00190
00191 if(node==NULL)
00192 return;
00193
00194
00195 xmlNodeSetName(node,(const xmlChar*)"dict");
00196
00197
00198
00199 dict_t seen;
00200
00201
00202 std::string perIndent(" ");
00203 std::string indentStr;
00204 for(xmlNode* cur=node->parent; cur!=NULL; cur=cur->parent) {
00205 if((void*)cur==(void*)node->doc) {
00206 if(indentStr.size()>0)
00207 indentStr=indentStr.substr(0,indentStr.size()-perIndent.size());
00208 break;
00209 }
00210 indentStr+=perIndent;
00211 }
00212
00213
00214 std::string comment;
00215
00216
00217 for(xmlNode* cur = skipToElement(node->children,comment); cur!=NULL; cur = skipToElement(cur->next,comment)) {
00218
00219
00220 xmlNode * k=cur;
00221 if(xmlStrcmp(k->name, (const xmlChar *)"key"))
00222 throw bad_format(k,"Dictionary format error: expect data in pairs of key and value (two values found in a row)");
00223 cur=skipToElement(cur->next);
00224
00225
00226 xmlNode * v=cur;
00227 if(v==NULL)
00228 throw bad_format(cur,"Dictionary format error: expect data in pairs of key and value (dictionary ended with hanging key)");
00229 if(!xmlStrcmp(v->name, (const xmlChar *)"key"))
00230 throw bad_format(v,"Dictionary format error: expect data in pairs of key and value (two keys found in a row)");
00231
00232
00233 xmlChar* cont=xmlNodeGetContent(k);
00234 string key=(const char*)cont;
00235 xmlFree(cont);
00236 dict_t::const_iterator it=dict.find(key);
00237 if(it==dict.end()) {
00238 if(warnUnused)
00239 cerr << "Warning: reading plist dictionary, key '" << key << "' does not match a registered variable. Ignoring..." << endl;
00240 continue;
00241 }
00242 if(comment.size()==0) {
00243 bool isSub=dynamic_cast<Dictionary*>(it->second);
00244 if(isSub) {
00245 xmlAddPrevSibling(k,xmlNewText((const xmlChar*)"\n"));
00246 xmlAddPrevSibling(k,xmlNewComment((const xmlChar*)("======== "+it->first+" ========").c_str()));
00247 }
00248 comments_t::const_iterator cit=comments.find(key);
00249 if(cit!=comments.end()) {
00250 if(isSub || cit->second.substr(0,key.size())==key)
00251 comment=cit->second;
00252 else
00253 comment=key+": "+cit->second;
00254 xmlAddPrevSibling(k,xmlNewText((const xmlChar*)"\n"));
00255 xmlAddPrevSibling(k,xmlNewComment((const xmlChar*)comment.c_str()));
00256 xmlAddPrevSibling(k,xmlNewText((const xmlChar*)("\n"+indentStr).c_str()));
00257 }
00258 }
00259 it->second->SaveXML(v);
00260 seen.insert(*it);
00261 }
00262
00263 if(seen.size()!=dict.size()) {
00264
00265
00266
00267 for(dict_t::const_iterator it=dict.begin(); it!=dict.end(); ++it) {
00268 if(seen.find(it->first)==seen.end()) {
00269
00270 bool isSub=dynamic_cast<Dictionary*>(it->second);
00271 if(isSub) {
00272 xmlAddChild(node,xmlNewText((const xmlChar*)"\n"));
00273 xmlAddChild(node,xmlNewComment((const xmlChar*)("======== "+it->first+" ========").c_str()));
00274 }
00275 comments_t::const_iterator cit=comments.find(it->first);
00276 if(cit!=comments.end()) {
00277 if(isSub || cit->second.substr(0,it->first.size())==it->first)
00278 comment=cit->second;
00279 else
00280 comment=it->first+": "+cit->second;
00281 xmlAddChild(node,xmlNewText((const xmlChar*)"\n"));
00282 xmlAddChild(node,xmlNewComment((const xmlChar*)comment.c_str()));
00283 }
00284 xmlAddChild(node,xmlNewText((const xmlChar*)("\n"+indentStr).c_str()));
00285 xmlNode* k=xmlNewChild(node,NULL,(const xmlChar*)"key",(const xmlChar*)it->first.c_str());
00286 if(k==NULL)
00287 throw bad_format(node,"Error: plist Dictionary xml error on saving key");
00288 xmlAddChild(node,xmlNewText((const xmlChar*)" "));
00289 xmlNode* v=xmlNewChild(node,NULL,(const xmlChar*)"",NULL);
00290 if(v==NULL)
00291 throw bad_format(node,"Error: plist Dictionary xml error on saving value");
00292 if(indentStr.size()>=perIndent.size())
00293 xmlAddChild(node,xmlNewText((const xmlChar*)("\n"+indentStr.substr(perIndent.size())).c_str()));
00294 else
00295 xmlAddChild(node,xmlNewText((const xmlChar*)("\n")));
00296 it->second->SaveXML(v);
00297 }
00298 }
00299 }
00300 }
00301
00302 unsigned int Dictionary::getLongestKeyLen() const {
00303 size_t longest=0;
00304 for(Dictionary::dict_t::const_iterator it=dict.begin(); it!=dict.end(); ++it) {
00305 size_t cur=it->first.size();
00306 if(Dictionary* dp=dynamic_cast<Dictionary*>(it->second))
00307 cur+=dp->getLongestKeyLen()+1;
00308 longest=std::max(longest,cur);
00309 }
00310 return longest;
00311 }
00312
00313 void Dictionary::addDictionaryListener(DictionaryListener* vl) {
00314 if(vl!=NULL) {
00315 if(dictionaryListeners==NULL)
00316 dictionaryListeners=new std::list<DictionaryListener*>;
00317 dictionaryListeners->push_back(vl);
00318 }
00319 }
00320
00321 void Dictionary::removeDictionaryListener(DictionaryListener* vl) {
00322 if(dictionaryListeners==NULL)
00323 return;
00324 std::list<DictionaryListener*>::iterator it=find(dictionaryListeners->begin(),dictionaryListeners->end(),vl);
00325 if(it!=dictionaryListeners->end()) {
00326 dictionaryListeners->erase(it);
00327 if(dictionaryListeners->empty()) {
00328 delete dictionaryListeners;
00329 dictionaryListeners=NULL;
00330 }
00331 }
00332 }
00333
00334 void Dictionary::fireEntryAdded(ObjectBase& val) {
00335 if(dictionaryListeners==NULL)
00336 return;
00337 std::list<DictionaryListener*>::iterator it=dictionaryListeners->begin();
00338 while(it!=dictionaryListeners->end()) {
00339 std::list<DictionaryListener*>::iterator cur=it++;
00340 (*cur)->plistDictionaryEntryAdded(*this,val);
00341 }
00342 }
00343
00344 void Dictionary::fireEntryRemoved(ObjectBase& val) {
00345 if(dictionaryListeners==NULL)
00346 return;
00347 std::list<DictionaryListener*>::iterator it=dictionaryListeners->begin();
00348 while(it!=dictionaryListeners->end()) {
00349 std::list<DictionaryListener*>::iterator cur=it++;
00350 (*cur)->plistDictionaryEntryRemoved(*this,val);
00351 }
00352 }
00353
00354 std::ostream& operator<<(std::ostream& os, const Dictionary& d) {
00355 unsigned int longest=std::max(d.getLongestKeyLen(),static_cast<unsigned int>(os.width()));
00356 for(Dictionary::dict_t::const_iterator it=d.dict.begin(); it!=d.dict.end(); ++it)
00357 if(Dictionary* dp=dynamic_cast<Dictionary*>(it->second)) {
00358 stringstream ss;
00359 ss << setw(longest-it->first.size()-1) << *dp;
00360 string line;
00361 for(getline(ss,line); ss; getline(ss,line))
00362 os << it->first << "." << line << endl;
00363 } else {
00364 os << left << setw(longest) << it->first << " = " << *it->second << endl;
00365 }
00366 return os;
00367 }
00368
00369 PrimitiveBase::~PrimitiveBase() {
00370 delete primitiveListeners;
00371 primitiveListeners=NULL;
00372 }
00373
00374 void PrimitiveBase::addPrimitiveListener(PrimitiveListener* vl) {
00375 if(vl!=NULL) {
00376 if(primitiveListeners==NULL)
00377 primitiveListeners=new std::list<PrimitiveListener*>;
00378 primitiveListeners->push_back(vl);
00379 }
00380 }
00381 void PrimitiveBase::removePrimitiveListener(PrimitiveListener* vl) {
00382 if(primitiveListeners==NULL)
00383 return;
00384 std::list<PrimitiveListener*>::iterator it=find(primitiveListeners->begin(),primitiveListeners->end(),vl);
00385 if(it!=primitiveListeners->end()) {
00386 primitiveListeners->erase(it);
00387 if(primitiveListeners->empty()) {
00388 delete primitiveListeners;
00389 primitiveListeners=NULL;
00390 }
00391 }
00392 }
00393 void PrimitiveBase::fireValueChanged() const {
00394 if(primitiveListeners==NULL)
00395 return;
00396 std::list<PrimitiveListener*>::const_iterator it=primitiveListeners->begin();
00397 while(it!=primitiveListeners->end()) {
00398 std::list<PrimitiveListener*>::const_iterator cur=it++;
00399 (*cur)->plistValueChanged(*this);
00400 }
00401 }
00402
00403 }
00404
00405
00406
00407
00408
00409
00410
00411
00412
00413
00414
|