Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | File List | Namespace Members | Class Members | File Members | Related Pages

base/Externalizer.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002   Copyright (C)2002 David Jung <opensim@pobox.com>
00003 
00004   This program/file is free software; you can redistribute it and/or modify
00005   it under the terms of the GNU General Public License as published by
00006   the Free Software Foundation; either version 2 of the License, or
00007   (at your option) any later version.
00008   
00009   This program is distributed in the hope that it will be useful,
00010   but WITHOUT ANY WARRANTY; without even the implied warranty of
00011   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012   GNU General Public License for more details. (http://www.gnu.org)
00013   
00014   You should have received a copy of the GNU General Public License
00015   along with this program; if not, write to the Free Software
00016   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00017   
00018   $Id: Externalizer.cpp 1067 2004-02-27 21:09:46Z jungd $
00019   $Revision: 1.5 $
00020   $Date: 2004-02-27 16:09:46 -0500 (Fri, 27 Feb 2004) $
00021   $Author: jungd $
00022  
00023 ****************************************************************************/
00024 
00025 #include <base/Externalizer>
00026 
00027 #include <base/Application>
00028 #include <base/Math>
00029 #include <base/Externalizable>
00030 #include <base/externalization_error>
00031 #include <base/io_error>
00032 
00033 #include <cstdio>
00034 
00035 using base::Externalizer;
00036 using base::Externalizable;
00037 using base::VFile;
00038 using base::Math;
00039 
00040 using base::dom;
00041 
00042 using std::iostream;
00043 using std::istream;
00044 using std::ostream;
00045 
00046 bool Externalizer::xmlInitialized = false;
00047 
00048 
00049 
00050 const String Externalizer::newline = String("\n");
00051 
00052 
00053 Externalizer::Externalizer(Externalizable::ExternalizationType type, ref<VFile> archive) 
00054   : xmlMode(false), impl(0), doctype(0), isoutput(type & Externalizable::Output), aborted(false),
00055     istream(isoutput?0:&archive->iostream(iostream::in)),
00056     ostream(isoutput?&archive->iostream(iostream::out):0),
00057     path(archive->path())
00058 {
00059 }
00060 
00061 Externalizer::Externalizer(Externalizable::ExternalizationType type, std::ios& stream) 
00062   : xmlMode(false), impl(0), doctype(0), isoutput(type & Externalizable::Output), aborted(false),
00063     path(String())
00064 {
00065   Assert(isoutput?instanceof(stream, std::ostream)
00066                  :instanceof(stream, std::istream));
00067   if (isoutput) 
00068     ostream = dynamic_cast<std::ostream*>(&stream);
00069   else 
00070     istream = dynamic_cast<std::istream*>(&stream);
00071 }
00072 
00073 
00074 Externalizer::~Externalizer()
00075 {
00076   if (xmlMode) {
00077     xmlWriteDocument(xmldoc);
00078     xmlReleaseDocument();
00079   }
00080 }
00081 
00082 
00083 const String Externalizer::inputToConstErrorString("cannot input to a const variable");
00084 const String Externalizer::inputDuringOutputErrorString("cannot input from an output Externalizer");
00085 
00086 
00087 void Externalizer::flush()
00088 {
00089   if (!xmlMode)
00090     if (isoutput) 
00091       (*ostream) << std::flush;
00092 }
00093 
00094 
00095 String Externalizer::readLine()
00096 {
00097   if (isOutput()) abort(inputDuringOutputErrorString);
00098     
00099   if (!xmlMode) {
00100     char linebuf[1024];
00101     istream->getline(linebuf,1024);
00102     linebuf[1023]=char(0);
00103     return String(linebuf);
00104   }
00105   else {
00106     Unimplemented;
00107   }
00108 }
00109 
00110 
00111 String Externalizer::readString(SInt maxLength)
00112 {
00113   if (isOutput()) abort(inputDuringOutputErrorString);
00114   
00115   if (!xmlMode) {
00116     char* buf = new char[maxLength+1];
00117     
00118     input().readsome(buf, maxLength);
00119     buf[maxLength] = 0;
00120     
00121     String str(buf);
00122     delete[] buf;
00123     
00124     return str;
00125   }
00126   else {
00127     Unimplemented;
00128   }
00129 }
00130 
00131 
00132 void Externalizer::writeLine(const String& line)
00133 {
00134   if (!isOutput()) return;
00135 
00136   if (!xmlMode) {
00137     output() << line << std::endl;
00138   }
00139   else {
00140     appendText(xmlcontext.top(), line);
00141   }
00142 }
00143 
00144 
00145 void Externalizer::writeString(const String& str)
00146 {
00147   if (!isOutput()) return;
00148 
00149   if (!xmlMode) {
00150     output() << str;
00151   }
00152   else {
00153   }
00154 }
00155 
00156 void Externalizer::writeComment(const String& comment)
00157 {
00158   if (!isOutput()) return;
00159 
00160   if (!xmlMode)
00161     writeLine(String("# ")+comment);
00162   else {
00163     appendComment(xmlcontext.top(), comment);
00164   }
00165     
00166 }
00167 
00168 
00169 
00170 // XML stuff
00171 
00172 void Externalizer::initXMLDoc(const String& rootElementName)
00173 {
00174   if (xmlMode) return;
00175 
00176   xmlInitialize();
00177 
00178   parser = 0; xmldoc = 0; 
00179   impl = dom::DOMImplementationRegistry::getDOMImplementation(XS("LS"));
00180   xmlMode = true;
00181 
00182   if (isInput()) {
00183     // parse the whole document
00184     xmldoc = xmlParseDocument();
00185     Assert(xmldoc);
00186     dom::DOMElement* docElem = xmldoc->getDocumentElement();
00187     if (!docElem)
00188       throw base::externalization_error(Exception("failed to obtain XML document element - invalid XML document."));
00189 
00190     xmlcontext.push( docElem );
00191    }
00192   else { // output
00193     // create a new document
00194     try {
00195       xmldoc = impl->createDocument(
00196                                   XS("http://www.w3.org/XML/1998/namespace"),                    // root element namespace URI
00197                                   XS(rootElementName),  // root element name
00198                                   doctype);             // document type 
00199     } catch (dom::DOMException& e) {
00200       abort(String("error creating document:")+String(XS(e.msg)) );
00201     }
00202     xmlcontext.push( xmldoc->getDocumentElement() );
00203     xmlcontext.top()->appendChild(xmldoc->createTextNode(XS(newline)));
00204   }
00205   
00206 }
00207 
00208 
00209 dom::DOMElement* Externalizer::createElement(const String& tagname, bool indenting)
00210 {
00211   dom::DOMElement* elem;
00212   
00213   if (!xmlMode) { // first element for document?
00214     String name(tagname);
00215     if (name == "") name = base::Application::getShortName();
00216     initXMLDoc(tagname);
00217     elem = xmldoc->getDocumentElement();
00218   }
00219   else
00220     elem = xmldoc->createElement(XS(tagname));
00221   
00222   // add a newline as the first child of the element so
00223   //  that the formatter knows to indent
00224   if (indenting)
00225     elem->appendChild(xmldoc->createTextNode(XS(newline)));
00226 
00227   return elem;
00228 }
00229 
00230 
00231 void Externalizer::appendElement(dom::DOMNode* n, const String& tagname, bool indenting)
00232 {
00233   n->appendChild( createElement(tagname, indenting) );
00234 }
00235 
00236 
00237 void Externalizer::appendProcessingInstruction(dom::DOMNode* n, const String& target, const String& data)
00238 {
00239   Assert(xmlMode);
00240   dom::DOMProcessingInstruction* pi = xmldoc->createProcessingInstruction(XS(target), XS(data));
00241   n->appendChild(pi);
00242 }
00243 
00244 
00245 void Externalizer::setDocumentType(const String& name, const String& publicid, const String& sysid)
00246 {
00247   if (doctype) return; // already set
00248   
00249   if (!impl)
00250     impl = dom::DOMImplementationRegistry::getDOMImplementation(XS("LS"));
00251   
00252   try {
00253     doctype = impl->createDocumentType(XS(name),XS(publicid), XS(sysid));
00254   } catch (dom::DOMException& e) {
00255     abort(String("error setting document type:")+String(XS(e.msg)) );
00256   }
00257 
00258 }
00259 
00260   
00261 
00262 dom::DOMText* Externalizer::createText(const String& text)
00263 {
00264   Assert(xmlMode);
00265   return xmldoc->createTextNode(XS(text));
00266 }
00267 
00268 
00269 void Externalizer::appendText(dom::DOMNode* n, const String& text)
00270 {
00271   n->appendChild( createText(text) );
00272 }
00273 
00274 
00275 dom::DOMComment* Externalizer::createComment(const String& comment)
00276 {   
00277   Assert(xmlMode);
00278   return xmldoc->createComment(XS(comment));
00279 }
00280 
00281 
00282 void Externalizer::appendComment(dom::DOMNode* n, const String& comment)
00283 {
00284   n->appendChild( createComment(comment) );
00285 }
00286 
00287 
00288 dom::DOMCDATASection* Externalizer::createCDATA(const String& text)
00289 {
00290   Assert(xmlMode);
00291   return xmldoc->createCDATASection(XS(text));
00292 }
00293 
00294 
00295 void Externalizer::appendCDATA(dom::DOMNode* n, const String& text)
00296 {
00297   Assert(xmlMode);
00298   n->appendChild( createCDATA(text) );
00299 }
00300 
00301 /// get first element with name tagname, either n or its immediate children
00302 dom::DOMElement* Externalizer::getFirstElement(dom::DOMNode* n, const String& tagname, bool required)
00303 {
00304   Assert(xmlMode);
00305 
00306   if (isElement(n, tagname)) // is n an element with name tagname?
00307     return dynamic_cast<dom::DOMElement*>(n);
00308   else // no, look in children
00309     return getFirstChildElement(n, tagname, required);
00310 }
00311 
00312 /// get first immediate child element of n with name tagname
00313 dom::DOMElement* Externalizer::getFirstChildElement(dom::DOMNode* n, const String& tagname, bool required)
00314 {
00315   Assert(xmlMode);
00316   dom::DOMElement* e = 0;
00317 
00318   if (n->hasChildNodes()) {
00319     dom::DOMNode* c = n->getFirstChild();
00320     while (c && (e==0) ) {
00321       if (isElement(c, tagname))
00322         e = dynamic_cast<dom::DOMElement*>(c);
00323       
00324       c = c->getNextSibling();
00325     }
00326     
00327   }
00328   
00329   if (required && (e==0)) {
00330     String pstr;
00331     if (isElement(n))
00332       pstr = " within element '"+elementTagName(n)+"'";
00333     abort(String("expected element '")+tagname+"'"+pstr);
00334   }
00335 
00336   return e;
00337 }
00338 
00339 
00340 dom::DOMElement* Externalizer::getNextSiblingElement(dom::DOMNode* n, const String& tagname, bool required)
00341 {
00342   Assert(xmlMode);
00343   dom::DOMElement* e = 0;
00344 
00345   dom::DOMNode* c = n->getNextSibling();
00346   while (c && (e == 0)) {
00347     if (isElement(c, tagname))
00348       e = dynamic_cast<dom::DOMElement*>(c);
00349     
00350     c = c->getNextSibling();
00351   }
00352 
00353   if (required && (e==0))
00354     abort(String("expected ")+tagname+" element");
00355 
00356   return e;
00357 }
00358 
00359 
00360 /// remove element e from the tree (no-op if e is the current context or has no parent)
00361 void Externalizer::removeElement(dom::DOMElement* e)
00362 {
00363   Assert(xmlMode);
00364   if (context()->isSameNode(e)) return;
00365 
00366   dom::DOMNode* p = e->getParentNode();
00367   if (p)
00368     p->removeChild(e);
00369 }
00370 
00371 
00372 String Externalizer::getContainedText(dom::DOMNode* n, bool removeIndentation)
00373 {
00374   Assert(xmlMode);
00375   String text;
00376 
00377   if (n->getNodeType() == dom::DOMNode::TEXT_NODE) {
00378     dom::DOMText* t = dynamic_cast<dom::DOMText*>(n);
00379     text += String(XS(t->getData()));
00380   }
00381   else if (n->getNodeType() == dom::DOMNode::CDATA_SECTION_NODE) {
00382     dom::DOMCDATASection* cd = dynamic_cast<dom::DOMCDATASection*>(n);
00383     text += String(XS(cd->getData()));
00384   }
00385   else if (n->hasChildNodes()) {
00386 
00387     dom::DOMNode* c = n->getFirstChild();
00388 
00389     while (c) {
00390       text += getContainedText(c);
00391       c = c->getNextSibling();
00392     }
00393 
00394   }
00395 
00396   if (removeIndentation) {
00397     
00398     // remove leading spaces from each line
00399     String newtext;
00400     SInt pos=0;
00401     while (pos < SInt(text.size())) {
00402 
00403       // skip over spaces
00404       while (((text[pos] == ' ') || (text[pos] == '\t')) && (pos < SInt(text.size())))
00405         pos++;
00406 
00407       if (pos < SInt(text.size())) {
00408         
00409         // extract text upto (& including) next new-line (or to end if no more newlines)
00410         Int nlpos = text.find( newline, pos );
00411         if (nlpos != String::npos) {
00412           newtext += text.substr(pos, nlpos - pos + 1);
00413           pos = nlpos+1; // pos after newline
00414         }
00415         else {
00416           newtext += text.substr(pos, text.size() - pos);
00417           pos = text.size(); // pos at end (to end the loop)
00418         }
00419         
00420       }
00421 
00422     }
00423     text = newtext;
00424   }
00425 
00426 
00427   return text;
00428 }
00429 
00430 
00431 String Externalizer::getElementAttribute(dom::DOMElement* e, const String& attrname, bool required)
00432 {
00433   Assert(xmlMode);
00434   dom::DOMAttr* attr = e->getAttributeNode( XS(attrname) );
00435   
00436   if (attr == 0) {
00437     if (required)
00438       abort(String("expected '")+attrname+"' attribute of element '"+String(XS(e->getTagName()))+"'" );
00439     else
00440       return String();
00441   }
00442 
00443   if (required && !attr->getSpecified())
00444       abort(String("expected explicit value for '")+attrname+"' attribute of element '"+String(XS(e->getTagName()))+"'" );
00445 
00446   return XS( attr->getValue() );
00447 }
00448 
00449 
00450 String Externalizer::getDefaultedElementAttribute(dom::DOMElement* e, const String& attrname, const String& defaultValue)
00451 {
00452   Assert(xmlMode);
00453   dom::DOMAttr* attr = e->getAttributeNode( XS(attrname) );
00454   
00455   if (attr == 0) 
00456     return defaultValue;
00457   else 
00458     if (!attr->getSpecified())
00459       return (defaultValue=="DTD")?String(XS( attr->getValue() )):defaultValue;
00460 
00461   return XS( attr->getValue() );
00462 }
00463 
00464 
00465 
00466 array<String> Externalizer::splitIntoLines(const String& text, bool removeBlankLines)
00467 {
00468   array<String> lines;
00469 
00470   Int pos=0;
00471   while (pos < text.size()) {
00472     SInt nlpos = text.find( newline, pos );
00473 
00474     if (nlpos != -1) {
00475       if (nlpos-pos != 0) {
00476         String line( text.substr(pos, nlpos - pos) );
00477         lines.push_back( line );
00478       }
00479       else
00480         if (!removeBlankLines)
00481           lines.push_back( String() );
00482       pos = nlpos+1;
00483     }
00484     else {
00485       lines.push_back( text.substr(pos, text.size()-pos) );
00486       pos = text.length();
00487     }
00488   }
00489 
00490   return lines;
00491 }
00492 
00493 
00494 array<String> Externalizer::splitAtDelimiter(const String& line, char delimiter)
00495 {
00496   array<String> fields;
00497 
00498   if (line.size() == 0) return fields;
00499   if (line.size() == 1) {
00500     fields.push_back( line );
00501     return fields;
00502   }
00503 
00504   Int pos=0;
00505   while (pos < line.size()) {
00506     SInt nlpos = line.find( delimiter, pos );
00507     if (nlpos != -1) {
00508       if (nlpos-pos != 0) {
00509         String field( line.substr(pos, nlpos - pos) );
00510         fields.push_back( field );
00511       }
00512       pos = nlpos+1;
00513     }
00514     else {
00515       fields.push_back( line.substr(pos, line.size()-pos) );
00516       pos = line.length();
00517     }
00518   }
00519 
00520   return fields;
00521 }
00522 
00523 
00524 base::Vector Externalizer::stringsToReals(const array<String>& strings, bool acceptUnits)
00525 {
00526   Vector v( zeroVector(strings.size()) );
00527 
00528   for(Int i=0; i<strings.size(); i++) {
00529     const String& s(strings[i]);
00530     v[i] = base::stringToReal(s);
00531     if (s.length() > 2) {
00532       if (s.substr(s.length()-2,s.length()-1) == "in")
00533         v[i] *= 0.0254; // inches to meters
00534       else if (s.substr(s.length()-3,s.length()-1) == "deg")
00535         v[i] = Math::degToRad(v[i]); // degrees to radians
00536     }
00537   }
00538 
00539   return v;
00540 }
00541 
00542 
00543 base::Matrix4 Externalizer::toMatrix4(const String& text)
00544 {
00545   array<String> lines = splitIntoLines(text, true);
00546   if (lines.size() != 4)
00547     throw base::externalization_error(Exception("expected 4x4 matrix"));
00548 
00549   Matrix4 m;
00550   for(Int r=0; r<4; r++) {
00551     array<String> rowEltStrings = splitAtDelimiter(lines[r],' ');
00552     Vector row( stringsToReals(rowEltStrings) );
00553     if (row.size() != 4)
00554       throw base::externalization_error(Exception("expected 4x4 matrix"));
00555     m(r+1,1) = row[0];
00556     m(r+1,2) = row[1];
00557     m(r+1,3) = row[2];
00558     m(r+1,4) = row[3];
00559   }
00560   return m;
00561 }
00562 
00563 String Externalizer::toString(const base::Matrix4& m)
00564 {
00565   char buf[256];
00566   sprintf(buf,"%8.4f %8.4f %8.4f %8.4f\n%8.4f %8.4f %8.4f %8.4f\n%8.4f %8.4f %8.4f %8.4f\n%8.4f %8.4f %8.4f %8.4f\n",
00567           m(1,1),m(1,2),m(1,3),m(1,4),
00568           m(2,1),m(2,2),m(2,3),m(2,4),
00569           m(3,1),m(3,2),m(3,3),m(3,4),
00570           m(4,1),m(4,2),m(4,3),m(4,4) );
00571   return String(buf);
00572 }
00573 
00574 
00575 base::Vector3 Externalizer::toVector3(const String& text, bool acceptUnits)
00576 {
00577   String s1( removeChar(text, '(') );
00578   String s2( removeChar(s1, ')') );
00579   array<String> eltStrings = splitAtDelimiter(s2,',');
00580   Vector v( stringsToReals(eltStrings, acceptUnits) );
00581   if (v.size() != 3)
00582     throw base::externalization_error(Exception("expected 3 dim vector"));
00583   return Vector3(v[0], v[1], v[2]);
00584 }
00585 
00586 base::Vector Externalizer::toVector(const String& text, bool commasep, bool acceptUnits)
00587 {
00588   String s1( removeChar(text, '(') );
00589   String s2( removeChar(s1, ')') );
00590   array<String> eltStrings = splitAtDelimiter(s2,commasep?',':' ');
00591   return stringsToReals(eltStrings, acceptUnits);
00592 }
00593 
00594 
00595 
00596 String Externalizer::toString(const base::Vector3& v)
00597 {
00598   char buf[48];
00599   sprintf(buf,"(%8.4f, %8.4f, %8.4f)",v.x,v.y,v.z);
00600   return String(buf);
00601 }
00602 
00603 
00604 String Externalizer::toString(const base::Vector& v, bool commasep)
00605 {
00606   String str;
00607   char buf[20];
00608   for(Int i=0; i<v.size(); i++) {
00609     sprintf(buf,"%8.4f",v[i]);
00610     str += String(buf);
00611     if (commasep && (i !=v.size()-1) )
00612       str += ",";
00613     else
00614       str += " ";
00615   }
00616 
00617   if (commasep)
00618     str = String("(")+str+")";
00619     
00620   return str;
00621 }
00622 
00623 
00624 String Externalizer::toString(const base::Matrix& m)
00625 {
00626   if ((m.size1() == 0) || (m.size2() == 0)) return String("[]");
00627   
00628   String line("[");
00629   char buf[16];
00630   
00631   for(Int r=0; r<m.size1(); r++) {
00632     String row;
00633     for(Int c=0; c<m.size2(); c++) {
00634       if (c!=m.size2())
00635         sprintf(buf,"%8.4f ",m(r,c)); 
00636       else
00637         sprintf(buf,"%8.4f",m(r,c));
00638       row+=String(buf);
00639     }
00640     if (r < m.size1()-1) row += "; ";
00641     line += row;
00642   }
00643   line += "]";
00644   
00645   return line;
00646 }
00647 
00648 
00649 base::Matrix Externalizer::toMatrix(const String& text)
00650 {
00651   String s1( removeChar(text, '[') );
00652   String s2( removeChar(s1, ']') );
00653   array<String> rowStrings = splitAtDelimiter(s2,';'); // split rows
00654   Int rows = rowStrings.size();
00655   Int cols = splitAtDelimiter(rowStrings[0],' ').size(); // count cols
00656   Matrix m(rows,cols);
00657   
00658   for(Int r=0; r<rows; r++) {
00659     array<String> eltStrings = splitAtDelimiter(rowStrings[r],' ');
00660     matrixRow(m,r) = stringsToReals(eltStrings);
00661   }
00662 
00663   return m;
00664 }
00665 
00666 
00667 String Externalizer::toString(Real v, const String& units)
00668 {
00669   if (units=="m")
00670     return base::realToString(v);
00671   else
00672     return base::realToString(v)+units;
00673 }
00674 
00675 Real Externalizer::toReal(const String& text, bool acceptUnits)
00676 {
00677   Real v = base::stringToReal(text);
00678   if (acceptUnits && (text.length() > 2)) {
00679     if (text.substr(text.length()-2,text.length()-1) == "in")
00680       v *= 0.0254; // inches to meters
00681     else if (text.substr(text.length()-3, text.length()-1) == "deg")
00682       v = Math::degToRad(v); // degrees to radians
00683   }
00684   return v;
00685 }
00686 
00687 
00688 
00689 
00690 String Externalizer::removeChar(const String& text, char toRemove)
00691 {
00692   String newtext;
00693   for(Int i=0; i<text.size(); i++)
00694     if (text[i] != toRemove)
00695       newtext += text[i];
00696   return newtext;
00697 }
00698 
00699 
00700 bool Externalizer::isNumeric(const String& text)
00701 {
00702   bool numeric=true;
00703   std::locale loc;
00704   const std::ctype<char>& ct = std::use_facet<std::ctype<char> >(loc);
00705   Int i=0;
00706   while (numeric && (i<text.size())) {
00707     if (!ct.is(std::ctype_base::digit,text[i]))
00708       numeric=false;
00709     i++;
00710   }
00711   return numeric;
00712 }
00713 
00714 
00715 
00716 
00717 
00718 void Externalizer::xmlInitialize()
00719 {
00720   if (!xmlInitialized) {
00721     try {
00722       dom::XMLPlatformUtils::Initialize();
00723       xmlInitialized = true;
00724     }
00725     catch (const dom::XMLException& toCatch) {
00726       throw std::runtime_error(Exception(String( dom::XMLString::transcode(toCatch.getMessage()) )));
00727     }
00728   }
00729 }
00730 
00731 
00732 
00733 dom::DOMDocument* Externalizer::xmlParseDocument()
00734 {
00735   if (isOutput()) abort(inputDuringOutputErrorString);
00736 
00737   xmlInitialize();
00738 
00739   if (!impl)
00740     impl = dom::DOMImplementationRegistry::getDOMImplementation(XS("LS"));
00741   parser = ((dom::DOMImplementationLS*)impl)->createDOMBuilder(dom::DOMImplementationLS::MODE_SYNCHRONOUS, 0);
00742 
00743   // set some features on this builder
00744   if (parser->canSetFeature(dom::XMLUni::fgDOMValidation, true))
00745     parser->setFeature(dom::XMLUni::fgDOMValidation, true);
00746   if (parser->canSetFeature(dom::XMLUni::fgDOMNamespaces, true))
00747     parser->setFeature(dom::XMLUni::fgDOMNamespaces, true);
00748   if (parser->canSetFeature(dom::XMLUni::fgDOMDatatypeNormalization, true))
00749       parser->setFeature(dom::XMLUni::fgDOMDatatypeNormalization, true);
00750   if (parser->canSetFeature(dom::XMLUni::fgDOMComments, true))
00751     parser->setFeature(dom::XMLUni::fgDOMComments, false);
00752 
00753   // to parse the stream, read the whole thing into a memory buffer
00754   //  and use a MemBufInputSource (inefficient for large files though)
00755   array<XByte> buffer(0,4096); // initial size=0, initial capacity=4096
00756   XByte b;
00757   std::istream& is( input() );
00758   while (is.good()) {
00759     b = is.get();
00760     buffer.push_back(b);
00761   }
00762 
00763   dom::MemBufInputSource memInputSrc(buffer.c_array(),buffer.size(),XS("ID"));
00764   dom::Wrapper4InputSource domInputSrc(&memInputSrc, false);
00765 
00766   dom::DOMDocument *doc = 0;
00767 
00768   try {
00769     doc = parser->parse(domInputSrc);
00770   }
00771   catch (const dom::XMLException& toCatch) {
00772     char* message = dom::XMLString::transcode(toCatch.getMessage());
00773     throw base::externalization_error(Exception(String("XML Error:")+message));
00774   }
00775   catch (const dom::DOMException& toCatch) {
00776     char* message = dom::XMLString::transcode(toCatch.msg);
00777     throw base::externalization_error(Exception(String("XML DOM Error:")+message));
00778   }
00779 
00780   Assert(doc);
00781   return doc;
00782 }
00783 
00784 
00785 void Externalizer::xmlWriteDocument(dom::DOMDocument* doc)
00786 {
00787   if (isInput()) return;
00788 
00789   xmlInitialize();
00790 
00791   dom::DOMNode* docElem = doc->getDocumentElement();
00792   xmlFormat(docElem, 1);
00793 
00794   dom::DOMNode* last = docElem->getLastChild();
00795   if (last)
00796     if (!isNewline(last))
00797       appendText(docElem, newline);
00798 
00799   if (!impl)
00800     impl = dom::DOMImplementationRegistry::getDOMImplementation(XS("LS"));
00801   dom::DOMWriter* writer = ((dom::DOMImplementationLS*)impl)->createDOMWriter();
00802   
00803   // set some features on this writer
00804   if (writer->canSetFeature(dom::XMLUni::fgDOMWRTDiscardDefaultContent, true))
00805     writer->setFeature(dom::XMLUni::fgDOMWRTDiscardDefaultContent, true);
00806   
00807   if (writer->canSetFeature(dom::XMLUni::fgDOMWRTFormatPrettyPrint, true))
00808     writer->setFeature(dom::XMLUni::fgDOMWRTFormatPrettyPrint, false);
00809   
00810 
00811   // output to a memory buffer, then serialize that to the stream
00812   dom::MemBufFormatTarget memBufTarget;
00813   
00814   try {
00815     // do the serialization through DOMWriter::writeNode();
00816     writer->writeNode(&memBufTarget, *doc);
00817   }
00818   catch (const dom::XMLException& toCatch) {
00819     char* message = dom::XMLString::transcode(toCatch.getMessage());
00820     throw base::externalization_error(Exception(String("XML Error:")+message));
00821   }
00822   catch (const dom::DOMException& toCatch) {
00823     char* message = dom::XMLString::transcode(toCatch.msg);
00824     throw base::externalization_error(Exception(String("XML DOM Error:")+message));
00825   }
00826   
00827   // copy to stream
00828   std::ostream& os( output() );
00829   const XByte* bytes = memBufTarget.getRawBuffer();
00830   XByte b = *bytes;
00831   while (b != 0) {
00832     os.put(b);
00833     bytes++;
00834     b = *bytes;
00835   }
00836   os.flush();
00837 
00838 }
00839 
00840 
00841 void Externalizer::xmlReleaseDocument()
00842 {
00843   if (xmlMode) {
00844 
00845     if (parser) {
00846       parser->release();
00847       parser = 0;
00848       xmldoc = 0;
00849     }
00850 
00851     xmlMode=0;
00852   }
00853 
00854 }
00855 
00856 
00857 bool Externalizer::isNewline(dom::DOMNode* n)
00858 {
00859   if (n->getNodeType() == dom::DOMNode::TEXT_NODE) {
00860     dom::DOMText* t = dynamic_cast<dom::DOMText*>(n);
00861     return (dom::XMLString::compareString(t->getData(),
00862                                           XS(newline)) == 0);
00863   }
00864   return false;
00865 }
00866 
00867 
00868 bool Externalizer::equals(const XCh* str1, const String& str2)
00869 {
00870   return (dom::XMLString::compareString(str1, XS(str2)) == 0);
00871 }
00872 
00873 
00874 bool Externalizer::isElement(dom::DOMNode* n, const String& tagname)
00875 {
00876   if (n->getNodeType() == dom::DOMNode::ELEMENT_NODE) {
00877     dom::DOMElement* e = dynamic_cast<dom::DOMElement*>(n);
00878     return equals(e->getTagName(), tagname);
00879   }
00880   return false;
00881 }
00882 
00883 
00884 
00885 // traverse the document and insert indent spaces where appropriate
00886 void Externalizer::xmlFormat(dom::DOMNode* n, Int indent)
00887 {
00888   if (!n->hasChildNodes()) return;
00889 
00890   dom::DOMNode* c = n->getFirstChild();
00891   do {
00892     //Debugcln(DJ,String(indent*2,' ') 
00893     //       << dom::XMLString::transcode(c->getNodeName()) );
00894     
00895     switch (c->getNodeType()) {
00896     case dom::DOMNode::ELEMENT_NODE: {
00897       dom::DOMElement* e = dynamic_cast<dom::DOMElement*>(c);
00898       bool indenting = false;
00899       dom::DOMNode* first = e->getFirstChild();
00900       if (first) {
00901         if (isNewline(first)) {
00902           // the first child node of this element is a newline 
00903           //   which indicates it is indenting, not inline:
00904           indenting = true;
00905           
00906           // - add a newline (if needed) and indent space before it
00907           dom::DOMNode* p = e->getParentNode();
00908           if (p) {
00909             dom::DOMNode* prev = e->getPreviousSibling();
00910             if (prev)
00911               if (!isNewline(prev))
00912                 p->insertBefore( createText(newline), e );
00913             p->insertBefore( createText(String(indent*indentSpaces, ' ')), e );
00914           }
00915           
00916 
00917 
00918           // - add a newline (if needed) and indent space as the last nodes to
00919           //   indent the close tag.  If there is only one newline child,
00920           //   remove it
00921           dom::DOMNode* last = e->getLastChild();
00922           if (last->isSameNode(first) && isNewline(last))
00923             e->removeChild(last);
00924           else {
00925             // recursive call
00926             xmlFormat(c,indent+1);
00927 
00928             last = e->getLastChild();
00929             if (!isNewline(last))
00930               appendText(e, newline);
00931             appendText(e, String(indent*indentSpaces, ' ') );
00932 
00933           
00934             // - add a newline after (so it is after the closetag)
00935             dom::DOMNode* p = e->getParentNode();
00936             if (p) {
00937               dom::DOMNode* next = e->getNextSibling();
00938               if (next)
00939                 p->insertBefore( createText(newline), next );
00940               else
00941                 appendText(p, newline );
00942             }
00943           }
00944 
00945         }
00946       }
00947       
00948       
00949       // if there is a newline before this element, indent it
00950       dom::DOMNode* prev = e->getPreviousSibling();
00951       if (prev)
00952         if (isNewline(prev)) {
00953           dom::DOMNode* p = e->getParentNode();
00954           if (p)
00955             p->insertBefore( createText(String(indent*indentSpaces, ' ')), e );
00956         }
00957           
00958 
00959 
00960       if (!indenting)
00961         xmlFormat(c,indent);
00962 
00963     } break;
00964 
00965     case dom::DOMNode::TEXT_NODE: {
00966       // if there is a newline before this node, indent it (if it isn't a newline itself)
00967       if (!isNewline(c)) {
00968         dom::DOMText* t = dynamic_cast<dom::DOMText*>(c);
00969         dom::DOMNode* prev = t->getPreviousSibling();
00970         if (prev) {
00971           if (isNewline(prev)) {
00972             dom::DOMNode* p = t->getParentNode();
00973             if (p) 
00974               p->insertBefore( createText(String(indent*indentSpaces, ' ')), t );
00975           }
00976         }
00977         
00978         // if the text contains a newline, split it
00979         // i.e. before: t='aa\nbb\ncc' after: t='aa' t2='\n' t3='bb\ncc'
00980         const XCh* text = t->getData();
00981         SInt newlineIndex = dom::XMLString::indexOf(text,XS(newline)[0]);
00982         if (newlineIndex != -1) {
00983           if (newlineIndex != SInt(t->getLength())-1) {
00984             t->splitText(newlineIndex);
00985             dom::DOMText* newtext = dynamic_cast<dom::DOMText*>(t->getNextSibling());
00986             newtext->deleteData(0,1); // remove the leading newline
00987             // put a newline and indent spaces between them
00988             dom::DOMNode* p = t->getParentNode();
00989             if (p) 
00990               p->insertBefore(createText(newline), newtext );
00991           }
00992           else
00993             t->deleteData(t->getLength()-1,1);
00994         }
00995       }
00996 
00997       xmlFormat(c,indent);
00998     } break;
00999 
01000     // these nodes always occur on a new line
01001     case dom::DOMNode::CDATA_SECTION_NODE: 
01002     case dom::DOMNode::COMMENT_NODE: {
01003 
01004       // add a newline before this node (if necessary) and indent it
01005       dom::DOMNode* prev = c->getPreviousSibling();
01006       dom::DOMNode* p = c->getParentNode();
01007       if (prev) {
01008         if (!isNewline(prev)) {
01009           if (p) p->insertBefore( createText(newline), c);
01010         }
01011 
01012         if (p) 
01013           p->insertBefore( createText(String(indent*indentSpaces, ' ')), c );
01014       }
01015 
01016       // add a newline after too
01017       dom::DOMNode* next = c->getNextSibling();
01018       if (next) {
01019         if (p) p->insertBefore( createText(newline), next );
01020       }
01021       else
01022         p->appendChild( createText(newline) );
01023         
01024       
01025     } break;
01026 
01027     default: ;
01028     }
01029 
01030     c = c->getNextSibling();
01031   } while(c);
01032 
01033 }
01034 
01035 
01036 
01037 
01038 
01039 
01040 void Externalizer::abort(const String& exceptionString)
01041 { 
01042   if (!aborted) {
01043     aborted=true; 
01044     Logln(exceptionString);
01045     throw externalization_error(Exception(exceptionString)); 
01046   }
01047 }

Generated on Thu Jul 29 15:56:06 2004 for OpenSim by doxygen 1.3.6