00001 #include "storage_xml.h"
00002 #include <model.h>
00003 #include <cmath>
00004 #include <cassert>
00005 #include <util/forall.h>
00006
00007 #include <QFile>
00008
00009 #include <iostream>
00010 #include <algorithm>
00011
00012 using std::endl;
00013 using std::cout;
00014
00015 namespace storage
00016 {
00017
00018
00019
00020 VVEStorage_XMLWriter::VVEStorage_XMLWriter()
00021 : doc()
00022 , write_type(true)
00023 {
00024 }
00025
00026 VVEStorage_XMLWriter::~VVEStorage_XMLWriter()
00027 {
00028 }
00029
00030 bool VVEStorage_XMLWriter::setOption(int option, int value)
00031 {
00032 switch(option)
00033 {
00034 case TypeChecking:
00035 switch(value)
00036 {
00037 case TCO_Exact:
00038 case TCO_Strict:
00039 case TCO_Permissive:
00040 case TCO_PermissiveNoWarning:
00041 write_type = true;
00042 return true;
00043 case TCO_NoCheck:
00044 write_type = false;
00045 return true;
00046 default:
00047 return false;
00048 }
00049 break;
00050 default:
00051 return false;
00052 }
00053 }
00054
00055 bool VVEStorage_XMLWriter::serialize(const QString& filename, Model* model)
00056 {
00057 _filename = filename;
00058 doc = QDomDocument("VVEStorage");
00059
00060 last_error = NO__ERROR;
00061 last_error_string = QString();
00062
00063 file_version = model->version();
00064
00065 typed_references_map.clear();
00066 compound_stack.clear();
00067
00068 root = doc.createElement("VVEStorage");
00069 if(!file_version.isEmpty())
00070 root.setAttribute("version", file_version);
00071 doc.appendChild(root);
00072 compound_stack.push_back(root);
00073
00074 file_version = QString("%1 XML").arg(file_version);
00075 version_number = model->versionNumber(file_version);
00076
00077 if(model->serialize(*this))
00078 {
00079 QFile file(filename);
00080 if(!file.open(QIODevice::WriteOnly))
00081 {
00082 setLastError(IO_ERROR, QString("Could not open file '%1' for writing").arg(filename));
00083 _filename = QString();
00084 return false;
00085 }
00086 file.write(doc.toByteArray());
00087 file.close();
00088 _filename = QString();
00089 return true;
00090 }
00091 _filename = QString();
00092 if(!last_error)
00093 {
00094 last_error = UNKNOWN_ERROR;
00095 last_error_string += "\nUnkown error.";
00096 }
00097 return false;
00098 }
00099
00100 QDomElement VVEStorage_XMLWriter::createElement(const QString& name, const QString& type)
00101 {
00102 QDomElement& parent = compound_stack.back();
00103 QDomElement new_element;
00104 if(name.isEmpty())
00105 {
00106 new_element = parent;
00107 }
00108 else
00109 {
00110 new_element = doc.createElement(name);
00111 parent.appendChild(new_element);
00112 }
00113 if(write_type and !type.isEmpty())
00114 new_element.setAttribute("type", type);
00115 return new_element;
00116 }
00117
00118 bool VVEStorage_XMLWriter::startCompound(const QString& name)
00119 {
00120 QDomElement new_compound = createElement(name);
00121 compound_stack.push_back(new_compound);
00122 return true;
00123 }
00124
00125 bool VVEStorage_XMLWriter::endCompound()
00126 {
00127 if(compound_stack.empty())
00128 {
00129 setLastError(NO_FIELD_ERROR, "Trying to end a compound not started");
00130 return false;
00131 }
00132 compound_stack.pop_back();
00133 return true;
00134 }
00135
00136 int VVEStorage_XMLWriter::startReference(const QString& name, const QString& ref_type, reference_t ref)
00137 {
00138 QDomElement place_holder = createElement(name);
00139
00140 ref_map_t& references_map = typed_references_map[ref_type];
00141
00142 ref_map_t::const_iterator found = references_map.find(ref);
00143 if(found == references_map.end())
00144 {
00145 int id = (int)references_map.size();
00146 place_holder.setAttribute("ref_id", id);
00147 place_holder.setAttribute("ref_type", ref_type);
00148 references_map[ref] = id;
00149 compound_stack.push_back(place_holder);
00150 return id;
00151 }
00152 else
00153 {
00154 place_holder.setAttribute("ref_id", found->second);
00155 place_holder.setAttribute("ref_type", ref_type);
00156 compound_stack.push_back(place_holder);
00157 return found->second;
00158 }
00159 }
00160
00161 bool VVEStorage_XMLWriter::endReference()
00162 {
00163 if(compound_stack.empty())
00164 {
00165 setLastError(USER_ERROR, "Trying to close a reference that wasn't opened.");
00166 return false;
00167 }
00168 compound_stack.pop_back();
00169 return true;
00170 }
00171
00172 #define DEFINE_FIELD_FCT(type) \
00173 bool VVEStorage_XMLWriter::field(const QString& name, type& value) \
00174 { \
00175 QDomElement element = createElement(name, TypeTraits<type>::name); \
00176 if(element.isNull()) return false; \
00177 QString value_str; \
00178 convert_type(value, value_str); \
00179 element.setAttribute("value", value_str); \
00180 return true; \
00181 }
00182
00183 FOR_ALL_TYPES(DEFINE_FIELD_FCT)
00184
00185 #undef DEFINE_FIELD_FCT
00186
00187
00188 VVEStorage_XMLReader::VVEStorage_XMLReader()
00189 : doc()
00190 , type_checking(TCO_Permissive)
00191 {
00192 }
00193
00194 VVEStorage_XMLReader::~VVEStorage_XMLReader()
00195 {
00196 }
00197
00198 bool VVEStorage_XMLReader::serialize(const QString& filename, Model* model)
00199 {
00200 _filename = filename;
00201 doc = QDomDocument("VVEStorage");
00202 compound_stack.clear();
00203
00204 last_error = NO__ERROR;
00205 last_error_string = QString();
00206
00207 QFile file(filename);
00208 if(!file.open(QIODevice::ReadOnly))
00209 {
00210 setLastError(IO_ERROR, QString("Could not open file '%1' for reading").arg(filename));
00211 return false;
00212 }
00213 if(!doc.setContent(&file))
00214 {
00215 file.close();
00216 setLastError(BAD_CONTENT, QString("File '%1' : Content is not valid XML.").arg(filename));
00217 return false;
00218 }
00219
00220 root = doc.firstChildElement("VVEStorage");
00221 if(root.isNull())
00222 {
00223 setLastError(BAD_CONTENT, QString("File '%1' has no root element").arg(filename));
00224 return false;
00225 }
00226 file_version = QString("%1 XML").arg(root.attribute("version", ""));
00227 version_number = model->versionNumber(file_version);
00228 compound_stack.push_back(root);
00229 bool result = model->serialize(*this);
00230 _filename = QString();
00231 return result;
00232 }
00233
00234 void VVEStorage_XMLReader::setLastError(int error, const QString& err)
00235 {
00236 last_error = error;
00237 last_error_string = err;
00238 last_error_string += "\nStack of elements:";
00239 QDomElement last;
00240 forall(const QDomElement& el, compound_stack)
00241 {
00242 if(el != last)
00243 last_error_string += QString("\n<%1>").arg(el.tagName());
00244 last = el;
00245 }
00246 }
00247
00248
00249
00250
00251
00252
00253
00254
00255 QDomElement VVEStorage_XMLReader::extractElement(const QString& name)
00256 {
00257 QDomElement& parent = compound_stack.back();
00258 if(name.isEmpty())
00259 return parent;
00260 QDomElement new_element = parent.firstChildElement(name);
00261 if(new_element.isNull())
00262 {
00263 setLastError(NO_FIELD_ERROR, QString("Element named %1 not found.").arg(name));
00264 return QDomElement();
00265 }
00266 parent.removeChild(new_element);
00267 return new_element;
00268 }
00269
00270 bool VVEStorage_XMLReader::setOption(int option, int value)
00271 {
00272 switch(option)
00273 {
00274 case TypeChecking:
00275 switch(value)
00276 {
00277 case TCO_Exact:
00278 case TCO_Strict:
00279 case TCO_Permissive:
00280 case TCO_PermissiveNoWarning:
00281 case TCO_NoCheck:
00282 type_checking = value;
00283 return true;
00284 default:
00285 return false;
00286 }
00287 break;
00288 default:
00289 return false;
00290 }
00291 }
00292
00293 bool VVEStorage_XMLReader::validType(QDomElement element, TYPES type)
00294 {
00295 if(type == T_INVALID)
00296 return true;
00297 QString real_type_name = element.attribute("type");
00298 TYPES real_type = T_STRING;
00299 if(!real_type_name.isEmpty())
00300 real_type = typename_to_id(real_type_name);
00301
00302 switch(type_checking)
00303 {
00304 case TCO_Exact:
00305 return type == real_type;
00306 case TCO_Strict:
00307 {
00308 if(real_type == T_INVALID)
00309 return false;
00310 return isStrictConversion(real_type, type);
00311 }
00312 case TCO_Permissive:
00313 {
00314 if(real_type == T_INVALID)
00315 return false;
00316 if(!isStrictConversion(real_type, type))
00317 {
00318 std::cerr << "**Warning**: trying to convert from " << typeid_to_name(real_type).toStdString() << " to " << typeid_to_name(type).toStdString()
00319 << " for element <" << element.tagName().toStdString() << "> on line " << element.lineNumber() << endl;
00320 }
00321 return true;
00322 }
00323 case TCO_PermissiveNoWarning:
00324 {
00325 if(real_type == T_INVALID)
00326 return false;
00327 return true;
00328 }
00329 case TCO_NoCheck:
00330 return true;
00331 default:
00332 break;
00333 }
00334 return false;
00335 }
00336
00337 template <typename From, typename To>
00338 bool VVEStorage_XMLReader::extract_value_with_error(QDomElement element, To& result)
00339 {
00340 QString attr = element.attribute("value");
00341 From value;
00342 if(!convert_type(attr, value))
00343 {
00344 setError_invalid_value(element.tagName(), type_name(value), attr);
00345 return false;
00346 }
00347 if(!convert_type(value, result))
00348 {
00349 setError_invalid_conversion(element.tagName(), attr, type_name(value), type_name(result));
00350 return false;
00351 }
00352 return true;
00353 }
00354
00355 template <typename T>
00356 bool VVEStorage_XMLReader::get_field(const QString& name, T& result)
00357 {
00358 static const TYPES result_type = type_id<T>();
00359 QDomElement element = extractElement(name);
00360 if(element.isNull())
00361 return false;
00362 QString type = element.attribute("type");
00363 if(type.isNull())
00364 {
00365 if(!element.hasAttribute("value"))
00366 {
00367 setError_novalue(name);
00368 return false;
00369 }
00370 return extract_value_with_error<QString,T>(element, result);
00371 }
00372 if(!validType(element, result_type))
00373 {
00374 setLastError(TYPE_CHECK_ERROR, QString("Error, element '%1' has type '%2' while type '%3' is expected").arg(element.tagName(), type, typeid_to_name(result_type)));
00375 return false;
00376 }
00377 if(!element.hasAttribute("value"))
00378 {
00379 setError_novalue(name);
00380 return false;
00381 }
00382 TYPES type_id = typename_to_id(type);
00383 #define SWITCH_TYPE(type_id,type) case type_id: return extract_value_with_error<type,T>(element, result);
00384 switch(type_id)
00385 {
00386 FOR_ALL_TYPEIDS(SWITCH_TYPE)
00387 case T_INVALID:
00388 default:
00389 setLastError(TYPE_CHECK_ERROR, QString("Error, type '%1' is unknown").arg(type));
00390 return false;
00391 }
00392 #undef SWITCH_TYPE
00393 }
00394
00395 bool VVEStorage_XMLReader::checkNextField(const QString& name)
00396 {
00397 QDomElement last = compound_stack.back();
00398 QDomElement child = last.firstChildElement(name);
00399 if(child.isNull())
00400 return false;
00401 return true;
00402 }
00403
00404 bool VVEStorage_XMLReader::startCompound(const QString& name)
00405 {
00406 QDomElement new_compound = extractElement(name);
00407 if(new_compound.isNull())
00408 {
00409 setLastError(NO_FIELD_ERROR, QString("Compound '%1' not found").arg(name));
00410 return false;
00411 }
00412 compound_stack.push_back(new_compound);
00413 return true;
00414 }
00415
00416 bool VVEStorage_XMLReader::endCompound()
00417 {
00418 if(compound_stack.empty())
00419 {
00420 setLastError(NO_FIELD_ERROR, "Trying to end a compound not started");
00421 return false;
00422 }
00423 compound_stack.pop_back();
00424 return true;
00425 }
00426
00427 int VVEStorage_XMLReader::startReference(const QString& name, const QString& ref_type, reference_t)
00428 {
00429 QDomElement place_holder = extractElement(name);
00430 if(place_holder.isNull())
00431 {
00432 setLastError(NO_FIELD_ERROR, QString("Reference '%1' not found").arg(name));
00433 return -1;
00434 }
00435
00436 QString type = place_holder.attribute("ref_type");
00437 if(type.isEmpty())
00438 {
00439 setLastError(REFERENCE_ERROR, QString("Element '%1' is not a reference but is used as one.").arg(name));
00440 return -1;
00441 }
00442
00443 if(type != ref_type)
00444 {
00445 setLastError(REFERENCE_ERROR, QString("Element '%1' is a reference of type '%2', reference of type '%3' was expected.").arg(name, type, ref_type));
00446 return -1;
00447 }
00448
00449 bool ok;
00450 uint ref = place_holder.attribute("ref_id", "").toUInt(&ok);
00451 if(!ok)
00452 {
00453 setLastError(REFERENCE_ERROR, QString("Reference '%1' has no id").arg(name));
00454 return -1;
00455 }
00456 compound_stack.push_back(place_holder);
00457 return (int)ref;
00458 }
00459
00460 bool VVEStorage_XMLReader::endReference()
00461 {
00462 if(compound_stack.empty())
00463 {
00464 setLastError(USER_ERROR, "Trying to close a reference that wasn't opened.");
00465 return false;
00466 }
00467 compound_stack.pop_back();
00468 return true;
00469 }
00470 void VVEStorage_XMLReader::setError_novalue(const QString& name)
00471 {
00472 setLastError(NO_FIELD_ERROR, QString("Element '%1' has no 'value' field.").arg(name));
00473 }
00474
00475 void VVEStorage_XMLReader::setError_invalid_value(const QString& name, const QString& type, const QString& value)
00476 {
00477 setLastError(TYPE_CONVERSION_ERROR, QString("Element '%1' has value '%2' which is not valid for type '%3'.").arg(name, value, type));
00478 }
00479
00480 void VVEStorage_XMLReader::setError_invalid_conversion(const QString& name, const QString& value, const QString& type_from, const QString& type_to)
00481 {
00482 setLastError(TYPE_CONVERSION_ERROR, QString("Element '%1' has value '%2' that cannot be converted from type '%3' to type '%4'.").arg(name, value, type_from, type_to));
00483 }
00484
00485 #define GET_FIELD_FUNCTION(type) \
00486 bool VVEStorage_XMLReader::field(const QString& name, type& value)\
00487 {\
00488 return get_field(name, value);\
00489 }
00490
00491 FOR_ALL_TYPES(GET_FIELD_FUNCTION)
00492
00493 #undef GET_FIELD_FUNCTION
00494
00495 }
00496