storage_xml.cpp

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   // First, the XML Writer
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   // Second, the XML Reader
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 //  static QString fullElement(QDomElement element)
00249 //  {
00250 //    QDomDocument dd("");
00251 //    dd.appendChild(element);
00252 //    return dd.toString().trimmed();
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 //    bool warning = false;
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 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
Generated on Fri May 31 15:37:52 2013 for VVE by  doxygen 1.6.3