Homepage
Demos
Overview
Downloads
Dev. Resources
Reference
Credits

plist.cc

Go 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     // find first element node within the plist
00053     xmlNode* cur=root->children;
00054     while(cur!=NULL && cur->type!=XML_ELEMENT_NODE)
00055       cur=cur->next;
00056     if(cur==NULL) //empty plist
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     //do we have a key with this name?
00131     dict_t::const_iterator it=dict.find(name);
00132     if(it!=dict.end())
00133       return it->second; //yes, return it
00134     
00135     //perhaps there's a sub-dictionary, separated by '.'
00136     size_t p=name.find(".");
00137     if(p==string::npos)
00138       return NULL; //no, '.'s found -- go away
00139     it=dict.find(name.substr(0,p));
00140     if(it==dict.end())
00141       return NULL; //no entry matching prefix -- go away
00142     const Dictionary* d=dynamic_cast<const Dictionary*>(it->second);
00143     if(d==NULL)
00144       return NULL; //matching prefix is not a dictionary -- go away
00145     
00146     //found a matching sub-dictionary, have it find the rest recursively
00147     return d->findEntry(name.substr(p+1));
00148   }
00149 
00150   void Dictionary::LoadXML(xmlNode* node) {
00151     //check if our node has been set to NULL (invalid or not found)
00152     if(node==NULL)
00153       return;
00154     
00155     std::string comment;
00156     
00157     //process children nodes
00158     for(xmlNode* cur = skipToElement(node->children,comment); cur!=NULL; cur = skipToElement(cur->next,comment)) {
00159             
00160       //find the next key node
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       //find the following value (non-key) node
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       //find corresponding entry
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     //check if our node has been set to NULL (invalid or not found)
00191     if(node==NULL)
00192       return;
00193     
00194     //set the type of the current node
00195     xmlNodeSetName(node,(const xmlChar*)"dict");
00196     
00197     // we'll use this to keep track of which nodes were already present, so we'll
00198     // know which ones were missing for which we need to make new nodes
00199     dict_t seen;
00200 
00201     //find the depth of the target node in the xml tree to maintain proper indentation
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) { //if we hit the document node, discount it and we're done
00206         if(indentStr.size()>0)
00207           indentStr=indentStr.substr(0,indentStr.size()-perIndent.size());
00208         break;
00209       }
00210       indentStr+=perIndent;
00211     }
00212     
00213     //This will hold any comments found between elements -- if no comment is found, a new one may be added
00214     std::string comment;
00215 
00216     //process children nodes
00217     for(xmlNode* cur = skipToElement(node->children,comment); cur!=NULL; cur = skipToElement(cur->next,comment)) {
00218       
00219       //find the next key node
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       //find the following value (non-key) node
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       //find corresponding entry
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 //if not a sub-dictionary, and comment doesn't already start with entry name, prepend entry name
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       // the main dictionary has entries that weren't seen... find which ones
00265       // if needed, this could be made faster (O(n) vs. current O(n lg n)) by assuming the maps
00266       // are sorted and moving two iterators through together instead of repeated find()'s
00267       for(dict_t::const_iterator it=dict.begin(); it!=dict.end(); ++it) {
00268         if(seen.find(it->first)==seen.end()) {
00269           //we didn't see this node in the existing xml tree, have to add a new node pair for it
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++; //increment early in case the listener changes subscription
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++; //increment early in case the listener changes subscription
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++; //increment early in case the listener changes subscription
00399       (*cur)->plistValueChanged(*this);
00400     }
00401   }
00402   
00403 } //namespace plist
00404 
00405 /*! @file
00406  * @brief 
00407  * @author Ethan Tira-Thompson (ejt) (Creator)
00408  *
00409  * $Author: ejt $
00410  * $Name: tekkotsu-2_4_1 $
00411  * $Revision: 1.6 $
00412  * $State: Exp $
00413  * $Date: 2005/07/27 05:58:24 $
00414  */

Tekkotsu v2.4.1
Generated Tue Aug 16 16:32:48 2005 by Doxygen 1.4.4