00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016 #ifndef PERSISTXML_H
00017 #define PERSISTXML_H
00018
00019 #ifndef USE_PCH
00020 #include <string>
00021 #include <fstream>
00022 #include <stdexcept>
00023 #include <vector>
00024 #include <list>
00025 #include <stack>
00026 #include <sstream>
00027 #include <libxml/parser.h>
00028 #include <boost/shared_ptr.hpp>
00029 #include <boost/optional/optional.hpp>
00030 #include "debug.h"
00031 #endif
00032
00033 #include "exceptions.h"
00034
00035
00036
00037
00038
00039
00040 class xml_invalid_label : public virtual std::invalid_argument {
00041 public:
00042
00043 xml_invalid_label(std::string const& label) : std::invalid_argument("Invalid XML label: \"" + label + "\".") { }
00044
00045 virtual ~xml_invalid_label() throw() { }
00046 };
00047
00048
00049
00050
00051 class xml_parse_error : public virtual std::runtime_error {
00052 public:
00053
00054 xml_parse_error(std::string const& message) : std::runtime_error("XML parse error: " + message) { }
00055
00056 virtual ~xml_parse_error() throw() { }
00057 };
00058
00059
00060 extern bool valid_label(std::string const& label);
00061
00062
00063
00064 class Indentation {
00065 private:
00066 int M_spaces;
00067 public:
00068
00069 Indentation(void) : M_spaces(0) { }
00070
00071 Indentation(Indentation const& indent) : M_spaces(indent.M_spaces) { }
00072
00073 Indentation& operator++(void) { M_spaces += 2; return *this; }
00074
00075 Indentation operator++(int) { Indentation tmp(*this); M_spaces += 2; return tmp; }
00076
00077 Indentation& operator--(void) { M_spaces -= 2; return *this; }
00078
00079 Indentation operator--(int) { Indentation tmp(*this); M_spaces -= 2; return tmp; }
00080
00081 friend std::ostream& operator<<(std::ostream& os, Indentation const& indentation)
00082 {
00083 for (int i = 0; i < indentation.M_spaces; ++i)
00084 os << ' ';
00085 return os;
00086 }
00087 };
00088
00089
00090
00091
00092
00093
00094
00095 class PersistXML {
00096 public:
00097
00098 enum openmode_type {
00099 not_open,
00100 store,
00101 load
00102 };
00103
00104 protected:
00105 openmode_type M_openmode;
00106 std::string M_archivename;
00107 std::fstream M_archive;
00108 Indentation M_indent;
00109 xmlDocPtr M_doc;
00110 xmlNodePtr M_cur;
00111 std::stack<std::vector<xmlNodePtr> > M_parent_nodes;
00112
00113 public:
00114
00115 PersistXML(void);
00116
00117 ~PersistXML();
00118
00119 void open(std::string filename, openmode_type mode);
00120
00121 void close(void);
00122
00123 openmode_type openmode(void) const { return M_openmode; }
00124
00125 private:
00126 void parse_archive(void);
00127 template<typename T>
00128 void write_escaped(T const& builtin);
00129 template<typename T>
00130 void read_data(std::stringstream& ss, T& builtin);
00131
00132 public:
00133 template<typename T>
00134 void serialize(std::string const& label, T& object);
00135 template<typename T>
00136 void serialize(std::string const& label, boost::shared_ptr<T>& ptr);
00137 template<typename T>
00138 void serialize(std::string const& label, std::vector<T>& vec);
00139 template<typename T>
00140 void serialize(std::string const& label, std::list<T>& list);
00141 template<typename KEY, typename INFO, typename COMPARE>
00142 void serialize(std::string const& label, std::map<KEY, INFO, COMPARE>& map);
00143 template<typename T>
00144 void serialize(std::string const& label, boost::optional<T>& opt);
00145 #if 0
00146 template<typename CONTAINER>
00147 void serialize(std::string const& label, CONTAINER& object,
00148 typename CONTAINER::iterator start, typename CONTAINER::iterator end);
00149 #endif
00150 template<typename T>
00151 void serialize_builtin(std::string const& label, T& builtin);
00152
00153 friend class XMLLabel;
00154 friend std::ostream& operator<<(std::ostream& os, openmode_type openmode);
00155 };
00156
00157
00158
00159
00160
00161 class XMLLabel {
00162 private:
00163 PersistXML& M_xml;
00164 std::string M_label;
00165 public:
00166
00167 XMLLabel(PersistXML& xml, std::string const& label) :
00168 M_xml(xml), M_label(label) { M_xml.M_archive << M_xml.M_indent++ << '<' << M_label << ">\n"; }
00169
00170 ~XMLLabel() { M_xml.M_archive << --M_xml.M_indent << "</" << M_label << ">\n"; }
00171 };
00172
00173
00174 template<typename T>
00175 void PersistXML::serialize(std::string const& label, T& object)
00176 {
00177 if (M_openmode == store)
00178 {
00179 Dout(dc::persist, "PersistXML::serialize(\"" << label << "\", (" <<
00180 type_info_of<T>().demangled_name() << ") {" << object << "}) is called.");
00181 XMLLabel xml_label(*this, label);
00182 object.serialize(*this);
00183 }
00184 else if (M_openmode == load)
00185 {
00186 Dout(dc::persist, "PersistXML::serialize(\"" << label << "\", (" <<
00187 type_info_of<T>().demangled_name() << ") @" << (void*)&object << ") is called.");
00188 while (M_cur)
00189 {
00190 Dout(dc::persist, "M_cur is \"" << M_cur->name << "\".");
00191 if (!xmlStrcmp(M_cur->name, (xmlChar const*) label.c_str()))
00192 {
00193 xmlNodePtr cur = M_cur;
00194 M_cur = cur->children;
00195 object.serialize(*this);
00196 M_cur = cur->next;
00197 return;
00198 }
00199 M_cur = M_cur->next;
00200 }
00201 THROW_EXCEPTION(xml_parse_error("Cannot find requested element \"" + label + "\" in current node."), "");
00202 }
00203 }
00204
00205
00206 template<typename T>
00207 void PersistXML::serialize(std::string const& label, boost::shared_ptr<T>& ptr)
00208 {
00209 if (!valid_label(label))
00210 THROW_EXCEPTION(xml_invalid_label(label), "");
00211 if (M_openmode == store)
00212 {
00213 Dout(dc::persist, "PersistXML::serialize(\"" << label << "\", (boost::shared_ptr<" <<
00214 type_info_of<T>().demangled_name() << ">)) is called.");
00215 XMLLabel xml_label(*this, label);
00216 ptr->serialize(*this);
00217 }
00218 else if (M_openmode == load)
00219 {
00220 Dout(dc::persist, "PersistXML::serialize(\"" << label << "\", (boost::shared_ptr<" <<
00221 type_info_of<T>().demangled_name() << ">) @" << (void*)&ptr << ") is called.");
00222 while (M_cur)
00223 {
00224 Dout(dc::persist, "M_cur is \"" << M_cur->name << "\".");
00225 if (!xmlStrcmp(M_cur->name, (xmlChar const*) label.c_str()))
00226 {
00227 xmlNodePtr cur = M_cur;
00228 M_cur = cur->children;
00229 ptr.reset(new T);
00230 AllocTag(ptr.get(), "Allocated for a boost::shared_ptr<" << libcwd::type_info_of<T>().demangled_name() << "> by PersistXML");
00231 ptr->serialize(*this);
00232 M_cur = cur->next;
00233 return;
00234 }
00235 M_cur = M_cur->next;
00236 }
00237 THROW_EXCEPTION(xml_parse_error("Cannot find requested element \"" + label + "\" in current node."), "");
00238 }
00239 }
00240
00241
00242 template<typename T>
00243 void PersistXML::serialize(std::string const& label, std::vector<T>& vec)
00244 {
00245 if (!valid_label(label))
00246 THROW_EXCEPTION(xml_invalid_label(label), "");
00247 if (M_openmode == store)
00248 {
00249 Dout(dc::persist, "PersistXML::serialize(\"" << label << "\", (std::vector<" <<
00250 type_info_of<T>().demangled_name() << ">)) is called.");
00251 XMLLabel xml_label(*this, label);
00252 for (typename std::vector<T>::iterator iter = vec.begin(); iter != vec.end(); ++iter)
00253 this->serialize("item", *iter);
00254 }
00255 else if (M_openmode == load)
00256 {
00257 Dout(dc::persist, "PersistXML::serialize(\"" << label << "\", (std::vector<" <<
00258 type_info_of<T>().demangled_name() << ">) @" << (void*)&vec << ") is called.");
00259 while (M_cur)
00260 {
00261 Dout(dc::persist, "M_cur is \"" << M_cur->name << "\".");
00262 if (!xmlStrcmp(M_cur->name, (xmlChar const*) label.c_str()))
00263 {
00264 if (M_cur->children)
00265 {
00266 xmlNodePtr cur = M_cur;
00267 M_cur = cur->children;
00268 while (M_cur)
00269 {
00270 if (!xmlStrcmp(M_cur->name, (xmlChar const*) "item"))
00271 {
00272 vec.push_back(T());
00273 this->serialize("item", vec.back());
00274 }
00275 M_cur = M_cur->next;
00276 }
00277 M_cur = cur->next;
00278 }
00279 return;
00280 }
00281 M_cur = M_cur->next;
00282 }
00283 if (!M_cur)
00284 THROW_EXCEPTION(xml_parse_error("Cannot find element \"" + label + "\"."), "");
00285 }
00286 }
00287
00288
00289 template<typename T>
00290 void PersistXML::serialize(std::string const& label, std::list<T>& list)
00291 {
00292 if (!valid_label(label))
00293 THROW_EXCEPTION(xml_invalid_label(label), "");
00294 if (M_openmode == store)
00295 {
00296 Dout(dc::persist, "PersistXML::serialize(\"" << label << "\", (std::list<" <<
00297 type_info_of<T>().demangled_name() << ">)) is called.");
00298 XMLLabel xml_label(*this, label);
00299 for (typename std::list<T>::iterator iter = list.begin(); iter != list.end(); ++iter)
00300 this->serialize("item", *iter);
00301 }
00302 else if (M_openmode == load)
00303 {
00304 Dout(dc::persist, "PersistXML::serialize(\"" << label << "\", (std::list<" <<
00305 type_info_of<T>().demangled_name() << ">) @" << (void*)&list << ") is called.");
00306 while (M_cur)
00307 {
00308 Dout(dc::persist, "M_cur is \"" << M_cur->name << "\".");
00309 if (!xmlStrcmp(M_cur->name, (xmlChar const*) label.c_str()))
00310 {
00311 if (M_cur->children)
00312 {
00313 xmlNodePtr cur = M_cur;
00314 M_cur = cur->children;
00315 while (M_cur)
00316 {
00317 if (!xmlStrcmp(M_cur->name, (xmlChar const*) "item"))
00318 {
00319 list.push_back(T());
00320 this->serialize("item", list.back());
00321 }
00322 M_cur = M_cur->next;
00323 }
00324 M_cur = cur->next;
00325 }
00326 return;
00327 }
00328 M_cur = M_cur->next;
00329 }
00330 if (!M_cur)
00331 THROW_EXCEPTION(xml_parse_error("Cannot find element \"" + label + "\"."), "");
00332 }
00333 }
00334
00335
00336 template<typename KEY, typename INFO, typename COMPARE>
00337 void PersistXML::serialize(std::string const& label, std::map<KEY, INFO, COMPARE>& map)
00338 {
00339 typedef std::map<KEY, INFO, COMPARE> container_type;
00340 if (!valid_label(label))
00341 THROW_EXCEPTION(xml_invalid_label(label), "");
00342 if (M_openmode == store)
00343 {
00344 Dout(dc::persist, "PersistXML::serialize(\"" << label << "\", (std::map<" <<
00345 type_info_of<KEY>().demangled_name() << ", " <<
00346 type_info_of<INFO>().demangled_name() << ">)) is called.");
00347 XMLLabel xml_label(*this, label);
00348 for (typename container_type::iterator iter = map.begin(); iter != map.end(); ++iter)
00349 {
00350 this->serialize("key", const_cast<KEY&>(iter->first));
00351 this->serialize("info", iter->second);
00352 }
00353 }
00354 else if (M_openmode == load)
00355 {
00356 Dout(dc::persist, "PersistXML::serialize(\"" << label << "\", (std::map<" <<
00357 type_info_of<KEY>().demangled_name() << ", " <<
00358 type_info_of<INFO>().demangled_name() << ">) @" << (void*)&map << ") is called.");
00359 while (M_cur)
00360 {
00361 Dout(dc::persist, "M_cur is \"" << M_cur->name << "\".");
00362 if (!xmlStrcmp(M_cur->name, (xmlChar const*) label.c_str()))
00363 {
00364 if (M_cur->children)
00365 {
00366 xmlNodePtr cur = M_cur;
00367 M_cur = cur->children;
00368 while (M_cur)
00369 {
00370 Dout(dc::persist, "M_cur is \"" << M_cur->name << "\".");
00371 if (!xmlStrcmp(M_cur->name, (xmlChar const*) "key"))
00372 {
00373 KEY key;
00374 this->serialize("key", key);
00375 std::pair<typename container_type::iterator, bool> result =
00376 map.insert(typename container_type::value_type(key, INFO()));
00377 if (!result.second)
00378 THROW_EXCEPTION(xml_parse_error("Duplicated key for map."), "");
00379 M_cur = M_cur->next;
00380 while (M_cur)
00381 {
00382 Dout(dc::persist, "M_cur is \"" << M_cur->name << "\".");
00383 if (!xmlStrcmp(M_cur->name, (xmlChar const*) "key"))
00384 {
00385 M_cur = NULL;
00386 break;
00387 }
00388 if (!xmlStrcmp(M_cur->name, (xmlChar const*) "info"))
00389 {
00390 this->serialize("info", result.first->second);
00391 Dout(dc::persist, "Inserted in map: " << *result.first);
00392 break;
00393 }
00394 M_cur = M_cur->next;
00395 }
00396 if (!M_cur)
00397 THROW_EXCEPTION(xml_parse_error("Missing element \"info\"."), "");
00398 }
00399 M_cur = M_cur->next;
00400 }
00401 }
00402 return;
00403 }
00404 M_cur = M_cur->next;
00405 }
00406 if (!M_cur)
00407 THROW_EXCEPTION(xml_parse_error("Cannot find element \"" + label + "\"."), "");
00408 }
00409 }
00410
00411
00412 template<typename T>
00413 void PersistXML::serialize(std::string const& label, boost::optional<T>& opt)
00414 {
00415 if (M_openmode == store)
00416 {
00417 Dout(dc::persist, "PersistXML::serialize(\"" << label << "\", (boost::optional<" <<
00418 type_info_of<T>().demangled_name() << ">)) is called.");
00419 if (opt)
00420 this->serialize(label, *opt);
00421 else
00422 {
00423 std::string unknown("unknown");
00424 this->serialize(label, unknown);
00425 }
00426 }
00427 else if (M_openmode == load)
00428 {
00429 Dout(dc::persist, "PersistXML::serialize(\"" << label << "\", (boost::optional<" <<
00430 type_info_of<T>().demangled_name() << ">) @" << (void*)&opt << ") is called.");
00431 bool unknown = false;
00432
00433 while (M_cur)
00434 {
00435 Dout(dc::persist, "M_cur is \"" << M_cur->name << "\".");
00436 if (!xmlStrcmp(M_cur->name, (xmlChar const*) label.c_str()))
00437 {
00438 unknown = (M_cur->children->type == XML_TEXT_NODE &&
00439 !strcmp((char*)M_cur->children->name, "text") &&
00440 !strcmp((char*)M_cur->children->content, "unknown"));
00441 break;
00442 }
00443 M_cur = M_cur->next;
00444 }
00445 if (!M_cur)
00446 THROW_EXCEPTION(xml_parse_error("Cannot find element \"" + label + "\"."), "");
00447
00448 if (unknown)
00449 {
00450 std::string unknown;
00451 this->serialize(label, unknown);
00452 ASSERT(unknown == "unknown");
00453 opt = boost::none;
00454 }
00455 else
00456 {
00457 T value;
00458 this->serialize(label, value);
00459 opt = value;
00460 }
00461 }
00462 }
00463
00464
00465
00466
00467 #define SERIALIZE_BUILTINTYPE(type) \
00468 template<> inline void PersistXML::serialize(std::string const& label, type& builtin) \
00469 { this->serialize_builtin(label, builtin); }
00470
00471 SERIALIZE_BUILTINTYPE(std::string);
00472 SERIALIZE_BUILTINTYPE(signed char);
00473 SERIALIZE_BUILTINTYPE(unsigned char);
00474 SERIALIZE_BUILTINTYPE(short);
00475 SERIALIZE_BUILTINTYPE(unsigned short);
00476 SERIALIZE_BUILTINTYPE(int);
00477 SERIALIZE_BUILTINTYPE(unsigned int);
00478 SERIALIZE_BUILTINTYPE(long);
00479 SERIALIZE_BUILTINTYPE(unsigned long);
00480 SERIALIZE_BUILTINTYPE(float);
00481 SERIALIZE_BUILTINTYPE(double);
00482 SERIALIZE_BUILTINTYPE(bool);
00483
00484
00485 template<typename T>
00486 inline void PersistXML::write_escaped(T const& builtin) { M_archive << builtin; }
00487
00488 template<>
00489 void PersistXML::write_escaped(char const& c);
00490
00491 template<>
00492 inline void PersistXML::write_escaped(unsigned char const& c) { write_escaped(reinterpret_cast<char const&>(c)); }
00493
00494 template<>
00495 void PersistXML::write_escaped(std::string const& s);
00496
00497
00498 template<typename T>
00499 inline void PersistXML::read_data(std::stringstream& ss, T& builtin) { ss >> builtin; }
00500
00501 template<>
00502 inline void PersistXML::read_data(std::stringstream& ss, std::string& s) { s = ss.str(); }
00503
00504
00505 template<typename T>
00506 void PersistXML::serialize_builtin(std::string const& label, T& builtin)
00507 {
00508 if (M_openmode == store)
00509 {
00510 Dout(dc::persist, "PersistXML::serialize_builtin<" <<
00511 type_info_of<T>().demangled_name() << ">(\"" << label << "\", " << builtin << ") is called.");
00512 M_archive << M_indent << '<' << label << '>';
00513 write_escaped(builtin);
00514 M_archive << "</" << label << ">\n";
00515 }
00516 else if (M_openmode == load)
00517 {
00518 Dout(dc::persist, "PersistXML::serialize_builtin<" <<
00519 type_info_of<T>().demangled_name() << ">(\"" << label << "\", @" << (void*)&builtin << ") is called.");
00520 while (M_cur)
00521 {
00522 Dout(dc::persist, "M_cur is \"" << M_cur->name << "\".");
00523 if (!xmlStrcmp(M_cur->name, (xmlChar const*) label.c_str()))
00524 {
00525 if (M_cur->children)
00526 {
00527 std::stringstream is;
00528 is << M_cur->children->content;
00529 read_data(is, builtin);
00530 }
00531 else
00532 builtin = T(); // Should only be used for std::string.
00533 return;
00534 }
00535 M_cur = M_cur->next;
00536 }
00537 if (!M_cur)
00538 THROW_EXCEPTION(xml_parse_error("Cannot find element \"" + label + "\"."), "");
00539 }
00540 }
00541
00542 #endif // PERSISTXML_H