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

base/SimpleXMLSerializer.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002   Copyright (C)1996 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: SimpleXMLSerializer.cpp 1029 2004-02-11 20:45:54Z jungd $
00019   $Revision: 1.6 $
00020   $Date: 2004-02-11 15:45:54 -0500 (Wed, 11 Feb 2004) $
00021   $Author: jungd $
00022  
00023 ****************************************************************************/
00024 
00025 #include <base/SimpleXMLSerializer>
00026 
00027 #include <base/Serializable>
00028 
00029 using base::SimpleXMLSerializer;
00030 using base::Serializer;
00031 using base::Serializable;
00032 using base::Referenced;
00033 using base::VFile;
00034 using base::ref;
00035 
00036 using std::iostream;
00037 using std::istream;
00038 using std::ostream;
00039 
00040 
00041 #ifdef DEBUG
00042 
00043 // comment out to inhibit serialization debugging
00044 //#define SDEBUG
00045 
00046 #endif
00047 
00048 
00049 SimpleXMLSerializer::SimpleXMLSerializer(SerializerType type, ref<VFile> archive)
00050   : Serializer(type,archive),
00051     istream(output?0:&archive->iostream(iostream::in)),
00052     ostream(output?&archive->iostream(iostream::out):0),
00053     indent(0)
00054 {
00055   serializeHeader(archive->name().str());
00056 
00057 #ifdef SDEBUG
00058   Debugln(Ser,"Constructed:" << (isOutput()?String("Output"):String("Input")) );
00059 #endif
00060 }
00061 
00062 SimpleXMLSerializer::SimpleXMLSerializer(SerializerType type, std::ios& stream)
00063   : Serializer(type,stream),
00064     indent(0)
00065 {
00066   Assert(output?instanceof(stream, std::ostream)
00067                :instanceof(stream, std::istream));
00068   if (output) 
00069     ostream = dynamic_cast<std::ostream*>(&stream);
00070   else 
00071     istream = dynamic_cast<std::istream*>(&stream);
00072 
00073   serializeHeader(String("stream"));
00074 
00075 #ifdef SDEBUG
00076   Debugln(Ser,"Constructed:" << (isOutput()?String("Output"):String("Input")) );
00077 #endif
00078 }
00079 
00080 
00081 SimpleXMLSerializer::~SimpleXMLSerializer()
00082 {
00083   if (aborted) {
00084     if (output) flush();
00085     return;
00086   }
00087 
00088   if (output) {
00089     indent--;
00090     (*ostream) << "</serialization>" << std::endl;
00091     flush();
00092   }
00093   else {
00094     readEndTag("serialization");
00095   }
00096 }
00097 
00098 
00099 bool SimpleXMLSerializer::followReferences(bool follow)
00100 {
00101 #ifdef SDEBUG
00102   Debugln(Ser,"Reference following " << (follow?String("enabled"):String("disabled")));
00103 #endif
00104   return Serializer::followReferences(follow);
00105 }
00106 
00107 
00108 Serializer& SimpleXMLSerializer::serialize(char& c, const String& memberName)
00109 {
00110   String tagName(memberName.empty()?"char":memberName);
00111   if (output) {
00112     try {
00113       (*ostream) << String(indent,' ') << "<" << tagName << ">" << c << "</" << tagName << ">" << std::endl;
00114     } catch (std::exception& e) { abort(e.what()); }
00115   }
00116   else { 
00117     TagData tag;
00118     if (readStartTag(tag)) {
00119       if ((tag.name != tagName) && (tag.name != "char"))
00120         abort("expected element "+tagName+" but got "+tag.name);
00121       try {
00122         (*istream) >> c;
00123       } catch (std::exception& e) { abort(e.what()); }
00124       readEndTag(tag.name);
00125     }
00126   }
00127 
00128 #ifdef SDEBUG
00129   //Debugln(Ser,"serialized char:" << c);
00130 #endif
00131   return *this;
00132 }
00133 
00134 Serializer& SimpleXMLSerializer::serialize(Byte& b, const String& memberName)
00135 {
00136   String tagName(memberName.empty()?"Byte":memberName);
00137   if (output) {
00138     try {
00139       (*ostream) << String(indent,' ') << "<" << tagName << ">" << Int(b) << "</" << tagName << ">" << std::endl;
00140     } catch (std::exception& e) { abort(e.what()); }
00141   }
00142   else {
00143     TagData tag;
00144     if (readStartTag(tag)) {
00145       if (tag.name != tagName)
00146         abort("expected element "+tagName+" but got "+tag.name);
00147       Int i;
00148       try {
00149         (*istream) >> i;
00150       } catch (std::exception& e) { abort(e.what()); }
00151       b = Byte(i);
00152       readEndTag(tag.name);
00153     }
00154   }
00155 
00156 #ifdef SDEBUG
00157   Debugln(Ser,"serialized Byte:" << b);
00158 #endif
00159   return *this;
00160 }
00161 
00162 Serializer& SimpleXMLSerializer::serialize(bool& b, const String& memberName)
00163 {
00164   String tagName(memberName.empty()?"bool":memberName);
00165   if (output) {
00166     try {
00167       (*ostream) << std::boolalpha;
00168       (*ostream) << String(indent,' ') << "<" << tagName << ">" << b << "</" << tagName << ">" << std::endl;
00169     } catch (std::exception& e) { abort(e.what()); }
00170   }
00171   else {
00172     TagData tag;
00173     if (readStartTag(tag)) {
00174       if (tag.name != tagName)
00175         abort("expected element "+tagName+" but got "+tag.name);
00176       try {
00177         (*istream) >> std::boolalpha;
00178         (*istream) >> b;
00179       } catch (std::exception& e) { abort(e.what()); }
00180       readEndTag(tag.name);
00181     }
00182   }
00183 
00184 #ifdef SDEBUG
00185   Debugln(Ser,"serialized bool:" << b);
00186 #endif
00187   return *this;
00188 }
00189 
00190 Serializer& SimpleXMLSerializer::serialize(SInt& i, const String& memberName)
00191 {
00192   String tagName(memberName.empty()?"SInt":memberName);
00193   if (output) {
00194     try {
00195       (*ostream) << String(indent,' ') << "<" << tagName << ">" << base::intToString(i) << "</" << tagName << ">" << std::endl;
00196     } catch (std::exception& e) { abort(e.what()); }
00197   }
00198   else {
00199     TagData tag;
00200     if (readStartTag(tag)) {
00201       if (tag.name != tagName)
00202         abort("expected element "+tagName+" but got "+tag.name);
00203       try {
00204         (*istream) >> i;
00205       } catch (std::exception& e) { abort(e.what()); }
00206       readEndTag(tag.name);
00207     }
00208   }
00209 
00210 #ifdef SDEBUG
00211   Debugln(Ser,"serialized SInt:" << i);
00212 #endif
00213   return *this;
00214 }
00215 
00216 Serializer& SimpleXMLSerializer::serialize(Int& i, const String& memberName)
00217 {
00218   String tagName(memberName.empty()?"Int":memberName);
00219   if (output) {
00220     try {
00221       (*ostream) << String(indent,' ') << "<" << tagName << ">" << base::intToString(i) << "</" << tagName << ">" << std::endl;
00222     } catch (std::exception& e) { abort(e.what()); }
00223   }
00224   else {
00225     TagData tag;
00226     if (readStartTag(tag)) {
00227       if (tag.name != tagName)
00228         abort("expected element "+tagName+" but got "+tag.name);
00229       try {
00230         (*istream) >> i;
00231       } catch (std::exception& e) { abort(e.what()); }
00232       readEndTag(tag.name);
00233     }
00234   }
00235 #ifdef SDEBUG
00236   Debugln(Ser,"serialized Int:" << i);
00237 #endif
00238   return *this;
00239 }
00240 
00241 Serializer& SimpleXMLSerializer::serialize(LInt& i, const String& memberName)
00242 {
00243   String tagName(memberName.empty()?"LInt":memberName);
00244   if (output) {
00245     try {
00246       (*ostream) << String(indent,' ') << "<" << tagName << ">" << base::intToString(i) << "</" << tagName << ">" << std::endl;
00247     } catch (std::exception& e) { abort(e.what()); }
00248   }
00249   else {
00250     TagData tag;
00251     if (readStartTag(tag)) {
00252       if (tag.name != tagName)
00253         abort("expected element "+tagName+" but got "+tag.name);
00254       try {
00255         (*istream) >> i;
00256       } catch (std::exception& e) { abort(e.what()); }
00257       readEndTag(tag.name);
00258     }
00259   }
00260 
00261 #ifdef SDEBUG
00262   Debugln(Ser,"serialized LInt:" << i);
00263 #endif
00264   return *this;
00265 }
00266 
00267 Serializer& SimpleXMLSerializer::serialize(String& s, const String& memberName)
00268 {
00269   String tagName(memberName.empty()?"string":memberName);
00270   if (output) {
00271     try {
00272       (*ostream) << String(indent,' ') << "<" << tagName << ">" << s << "</" << tagName << ">" << std::endl;
00273     } catch (std::exception& e) { abort(e.what()); }
00274   }
00275   else {
00276     TagData tag;
00277     if (readStartTag(tag)) {
00278       if (tag.name != tagName)
00279         abort("expected element "+tagName+" but got "+tag.name);
00280       try {
00281         s.clear();
00282         // read chars until '<'
00283         char p = nextChar();
00284         while (p != '<') { s += p; p = nextChar(); }
00285         istream->putback(p);
00286       } catch (std::exception& e) { abort(e.what()); }
00287       readEndTag(tag.name);
00288     }
00289   }
00290 
00291 #ifdef SDEBUG
00292   Debugln(Ser,"serialized String:" << s);
00293 #endif
00294   return *this;
00295 }
00296 
00297 Serializer& SimpleXMLSerializer::serialize(Real& r, const String& memberName)
00298 {
00299   String tagName(memberName.empty()?"real":memberName);
00300   if (output) {
00301     try {
00302       (*ostream) << String(indent,' ') << "<" << tagName << ">" << base::realToString(r) << "</" << tagName << ">" << std::endl;
00303     } catch (std::exception& e) { abort(e.what()); }
00304   }
00305   else {
00306     TagData tag;
00307     if (readStartTag(tag)) {
00308       if (tag.name != tagName)
00309         abort("expected element "+tagName+" but got "+tag.name);
00310       try {
00311         (*istream) >> r;
00312       } catch (std::exception& e) { abort(e.what()); }
00313       readEndTag(tag.name);
00314     }
00315   }
00316 
00317 #ifdef SDEBUG
00318   Debugln(Ser,"serialized Real:" << r);
00319 #endif
00320   return *this;
00321 }
00322 
00323 void SimpleXMLSerializer::flush()
00324 {
00325   if (output) 
00326     (*ostream) << std::flush;
00327 }
00328 
00329 
00330 
00331 
00332 Serializer::TypeModifier SimpleXMLSerializer::preSerializeObject(String& typeName, TypeModifier typeModifier, const String& memberName, bool forceTypeSerialization)
00333 {
00334   String tagName(memberName.empty()?"object":memberName);
00335   String typeString;
00336     
00337   if (output) {
00338     
00339     try {
00340       typeString = base::demangleTypeidName(typeName);
00341     } catch (std::exception& e) {
00342       abort(String("error demangling typeName:")+typeName+" for element:" +tagName+" - "+e.what());
00343     }
00344     
00345     String modifierString;
00346     if (typeModifier == ReferencedObjectType)
00347       modifierString = "ro";
00348     else if (typeModifier == ObjectReferenceType)
00349       modifierString = "r";
00350 
00351     if (!modifierString.empty())
00352       modifierString = String(" ref=\""+modifierString+"\"");
00353 
00354     (*ostream) << String(indent,' ') << "<" << tagName << " type=\"" << typeString << "\"" << modifierString << ">" << std::endl;
00355     indent++;
00356 
00357     return typeModifier;
00358   }
00359   else { // input
00360     TagData tag;
00361 
00362     if (readStartTag(tag)) {
00363 
00364       if (tag.name != tagName)
00365         abort("expected element "+tagName+" but got "+tag.name);
00366 
00367       if (!typeName.empty()) {
00368         try {
00369           typeString = base::demangleTypeidName(typeName);
00370         } catch (std::exception& e) {
00371           abort(String("error demangling typeName:")+typeName+" for element:" +tagName+" - "+e.what());
00372         }
00373 
00374         if (tag.attribute("type") != typeString) 
00375           abort("expected object of type "+typeString+" but got "+tag.attribute("type"));
00376       }
00377       else
00378         typeName = tag.attribute("type");
00379       
00380       String modifierString(tag.attribute("ref"));
00381       TypeModifier readTypeModifier = ObjectType;
00382 
00383       if (modifierString == "r")
00384         readTypeModifier = ObjectReferenceType;
00385       else if (modifierString == "ro")
00386         readTypeModifier = ReferencedObjectType;
00387 
00388       if (typeModifier != UnknownType)
00389         if (typeModifier != readTypeModifier)
00390           abort("got difference object reference type modifier than expected");
00391 
00392       return readTypeModifier;
00393     }
00394     else 
00395       return UnknownType;
00396   }
00397 }
00398 
00399 
00400 void SimpleXMLSerializer::postSerializeObject(const String& memberName)
00401 {
00402   String tagName(memberName.empty()?"object":memberName);
00403 
00404   if (output) {
00405     indent--;
00406     (*ostream) << String(indent,' ') << "</" << tagName << ">" << std::endl;
00407   }
00408   else { // input 
00409     readEndTag(tagName);
00410   }
00411 }
00412 
00413 
00414 void SimpleXMLSerializer::serializeComment(const String& comment)
00415 {
00416   if (output) {
00417     (*ostream) << String(indent,' ') << "<!-- " << comment << " -->" << std::endl;
00418   }
00419 }
00420 
00421 
00422 Serializer& SimpleXMLSerializer::hint(Int h)
00423 {
00424   if (h == Indent) indent++;
00425   else if (h == Unindent) indent--;
00426   return *this;
00427 }
00428 
00429 
00430 bool SimpleXMLSerializer::serializeHeader(const String& target)
00431 {
00432   
00433   if (output) {
00434     try {
00435       (*ostream) << "<?xml version =\"1.0\" encoding=\"UTF-8\" standalone=\"yes\" ?>" << std::endl;
00436       (*ostream) << "<serialization type=\"base::SimpleXMLSerializer\" version=\"1.0\" target=\"" << target << "\">" << std::endl;
00437       indent++;
00438     } catch (std::exception& e) { abort(e.what()); }
00439   }
00440   else { // input
00441     TagData tag;
00442     bool xmlTagOK = readStartTag(tag);
00443     if (xmlTagOK) {
00444       if (tag.name != "?xml") {
00445         xmlTagOK = false;
00446       } 
00447     }
00448     
00449     if (!xmlTagOK)
00450       abort("Bad XML header");
00451     
00452     bool rootElementOK = readStartTag(tag);
00453     if (rootElementOK) {
00454       if (tag.name != "serialization") {
00455         rootElementOK = false;
00456       }
00457       else {
00458         if (tag.attribute("type") != "base::SimpleXMLSerializer")
00459           abort("Serialization format not supported");
00460 
00461         if (tag.attribute("version") != "1.0")
00462           abort("Only serialization format version 1.0 is supported");
00463       }
00464     }
00465 
00466     if (!rootElementOK)
00467       abort("This XML format or version not is supported");
00468 
00469   }
00470 
00471   return true;
00472 }
00473 
00474 
00475 bool SimpleXMLSerializer::readStartTag(TagData& tag)
00476 {
00477   Assert(!output);
00478 
00479   tag.name.clear();
00480   tag.attributeNames.clear();
00481   tag.attributeValues.clear();
00482 
00483   (*istream) >> std::noskipws;
00484 
00485   char p = nextNWSChar();
00486   if (p == '<') {
00487     // check for '!--' comment start
00488     p = nextChar();
00489     tag.name += p;
00490     if (p=='!') {
00491       tag.name += p;
00492       do { p = nextChar(); tag.name += p; } while (p==' ');
00493       if (p=='-') {
00494         tag.name += p;
00495         p = nextChar();
00496         if (p=='-') { // comment start
00497           tag.name.clear();
00498           // skip to end of comment
00499           bool endcomment = false;
00500           do {
00501             p = nextChar();
00502             if (p == '-') {
00503               p = nextChar();
00504               if (p == '-') {
00505                 p = nextChar();
00506                 if (p == '>') {
00507                   endcomment = true;
00508                   // eat whitespace after comment
00509                   p = nextNWSChar();
00510                   if (p!='<') {
00511                     istream->putback(p);
00512                     return false;
00513                   }
00514                 }
00515               }
00516             }
00517           } while (!endcomment);
00518         }
00519       }
00520     } // end check for comment
00521 
00522     // read tag name (ends with space or '>')
00523     p = nextChar();
00524     while ((p != '>') && (p != ' ')) {
00525       tag.name += p;
00526       p = nextChar();
00527     } 
00528 
00529     // white space is allowed after tag name, but before '>', eat it
00530     if (p!='>') {
00531       p = nextNWSChar();
00532       if (p!='>') 
00533         istream->putback(p); // still isn't tag close '>', must be start of attribute name, put it back
00534     }
00535 
00536     if (p!='>') { // tag must have some attributes, read them
00537       bool tagclose=false;
00538       do {
00539         String attribName;
00540         p = nextNWSChar();
00541         while ((p!=' ') && (p!='=')) { attribName += p; p = nextChar(); }
00542         if (p!='=') p = nextNWSChar();
00543         if (p!='=') abort("'=' expected after attribute name:"+attribName+" of element:"+tag.name);
00544         p = nextNWSChar();
00545         if (p!='\"') abort("attribute value must be quoted (in attribute name:"+attribName+" of element:"+tag.name+")");
00546         // read in attribute value
00547         String attribValue;
00548         p = nextChar();
00549         while (p!='\"') { attribValue += p; p = nextChar(); }
00550         tag.attributeNames.push_back(attribName);
00551         tag.attributeValues.push_back(attribValue);
00552 
00553         p = nextNWSChar();
00554 
00555         if ((tag.name[0]=='?') && (p=='?')) 
00556           p = nextChar();
00557 
00558         if (p == '>') tagclose=true;
00559 
00560         if (p == '<') 
00561           abort("unexpected '<' - element "+tag.name+" not terminated?");
00562 
00563         if (!tagclose) istream->putback(p);
00564 
00565       } while (!tagclose);
00566 
00567     }
00568 
00569     return true;
00570   }    
00571 
00572   istream->putback(p);
00573   return false;
00574 
00575 }
00576 
00577 
00578 bool SimpleXMLSerializer::readEndTag(const String& tagName)
00579 {
00580   Assert(!output);
00581 
00582   (*istream) >> std::noskipws;
00583 
00584   String name;
00585   char p = nextNWSChar();
00586   if (p == '<') {
00587     // check for '!--' comment start
00588     p = nextChar();
00589     name += p;
00590     if (p=='!') {
00591       name += p;
00592       do { p = nextChar(); name += p; } while (p==' ');
00593       if (p=='-') {
00594         name += p;
00595         p = nextChar();
00596         if (p=='-') { // comment start
00597           name.clear();
00598           // skip to end of comment
00599           bool endcomment = false;
00600           do {
00601             p = nextChar();
00602             if (p == '-') {
00603               p = nextChar();
00604               if (p == '-') {
00605                 p = nextChar();
00606                 if (p == '>') {
00607                   endcomment = true;
00608                   // eat whitespace after comment
00609                   p = nextNWSChar();
00610                   if (p!='<') {
00611                     istream->putback(p);
00612                     return false;
00613                   }
00614                 }
00615               }
00616             }
00617           } while (!endcomment);
00618         }
00619       }
00620     } // end check for comment
00621     
00622     // read tag name (ends with space or '>')
00623     p = nextChar();
00624     while ((p != '>') && (p != ' ')) {
00625       name += p;
00626       p = nextChar();
00627     } 
00628 
00629 
00630     // white space is allowed after tag name, but before '>', eat it
00631     if (p!='>') 
00632       p = nextNWSChar();
00633     
00634     if (p!='>') // still no '>'?, improperly terminated
00635       abort("expected '>' to close element:<"+name);
00636 
00637     if (!tagName.empty())
00638       if (name != String("/"+tagName))
00639         abort("expected closing element </"+tagName+"> but read <"+name+">");
00640     
00641     return true;
00642   }
00643 
00644   return false;
00645 }
00646 

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