// ************************************************************************** // * This file is part of the FreeFileSync project. It is distributed under * // * GNU General Public License: http://www.gnu.org/licenses/gpl.html * // * Copyright (C) 2008-2010 ZenJu (zhnmju123 AT gmx.de) * // ************************************************************************** // #include "xmlBase.h" #include "globalFunctions.h" #include #include #include "fileIO.h" #include "stringConv.h" #include "systemConstants.h" #include #include using namespace FreeFileSync; std::string getTypeName(xmlAccess::XmlType type) { switch (type) { case xmlAccess::XML_GUI_CONFIG: return "GUI"; case xmlAccess::XML_BATCH_CONFIG: return "BATCH"; case xmlAccess::XML_GLOBAL_SETTINGS: return "GLOBAL"; case xmlAccess::XML_REAL_CONFIG: return "REAL"; case xmlAccess::XML_OTHER: break; } assert(false); return "OTHER"; } xmlAccess::XmlType xmlAccess::getXmlType(const wxString& filename) //throw() { #ifndef __WXDEBUG__ wxLogNull noWxLogs; //hide wxWidgets log messages in release build #endif if (!wxFileExists(filename)) return XML_OTHER; //workaround to get a FILE* from a unicode filename wxFFile configFile(filename, wxT("rb")); if (!configFile.IsOpened()) return XML_OTHER; FILE* inputFile = configFile.fp(); TiXmlDocument doc; try { if (!doc.LoadFile(inputFile)) //fails if inputFile is no proper XML return XML_OTHER; } catch (const std::exception&) { //unfortunately TiXml isn't very smart and tries to allocate space for the complete file: length_error exception is thrown for large files! return XML_OTHER; } TiXmlElement* root = doc.RootElement(); if (!root || (root->ValueStr() != std::string("FreeFileSync"))) //check for FFS configuration xml return XML_OTHER; const char* cfgType = root->Attribute("XmlType"); if (!cfgType) return XML_OTHER; const std::string type(cfgType); if (type == getTypeName(XML_GUI_CONFIG)) return XML_GUI_CONFIG; else if (type == getTypeName(XML_BATCH_CONFIG)) return XML_BATCH_CONFIG; else if (type == getTypeName(XML_GLOBAL_SETTINGS)) return XML_GLOBAL_SETTINGS; else if (type == getTypeName(XML_REAL_CONFIG)) return XML_REAL_CONFIG; return XML_OTHER; } namespace { //convert (0xD, 0xA) and (0xD) to 0xA: just like in TiXmlDocument::LoadFile(); not sure if actually needed void normalize(std::vector& stream) { std::vector tmp; for (std::vector::const_iterator i = stream.begin(); i != stream.end(); ++i) switch (*i) { case 0xD: tmp.push_back(0xA); if (i + 1 != stream.end() && *(i + 1) == 0xA) ++i; break; default: tmp.push_back(*i); } stream.swap(tmp); } } void xmlAccess::loadXmlDocument(const wxString& filename, const xmlAccess::XmlType type, TiXmlDocument& document) //throw FileError() { std::vector inputStream; try { const size_t BUFFER_SIZE = 512 * 1024; static boost::scoped_array buffer(new unsigned char[BUFFER_SIZE]); FileInput inputFile(wxToZ(filename)); //throw FileError(); do { const size_t bytesRead = inputFile.read(buffer.get(), BUFFER_SIZE); //throw FileError() inputStream.insert(inputStream.end(), buffer.get(), buffer.get() + bytesRead); } while (!inputFile.eof()); } catch (const FileError& error) //more detailed error messages than with wxWidgets { throw XmlError(error.show()); } if (!inputStream.empty()) { inputStream.push_back(0); //convert (0xD, 0xA) and (0xD) to (0xA): just like in TiXmlDocument::LoadFile(); not sure if actually needed ::normalize(inputStream); TiXmlBase::SetCondenseWhiteSpace(false); //do not condense whitespace characters document.Parse(&inputStream[0], 0, TIXML_DEFAULT_ENCODING); TiXmlElement* root = document.RootElement(); if (root && (root->ValueStr() == std::string("FreeFileSync"))) //check for FFS configuration xml { const char* cfgType = root->Attribute("XmlType"); if (cfgType && std::string(cfgType) == getTypeName(type)) return; //finally... success! } } throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\"")); } void xmlAccess::getDefaultXmlDocument(const XmlType type, TiXmlDocument& document) { TiXmlBase::SetCondenseWhiteSpace(false); //do not condense whitespace characters TiXmlDeclaration* decl = new TiXmlDeclaration("1.0", "UTF-8", ""); //delete won't be necessary later; ownership passed to TiXmlDocument! document.LinkEndChild(decl); TiXmlElement* root = new TiXmlElement("FreeFileSync"); root->SetAttribute("XmlType", getTypeName(type)); //xml configuration type document.LinkEndChild(root); } void xmlAccess::saveXmlDocument(const wxString& filename, const TiXmlDocument& document) //throw (XmlError) { //convert XML into continuous byte sequence TiXmlPrinter printer; printer.SetLineBreak(wxString(globalFunctions::LINE_BREAK).ToUTF8()); document.Accept(&printer); const std::string buffer = printer.Str(); try { FileOutput outputFile(wxToZ(filename)); //throw FileError() outputFile.write(buffer.c_str(), buffer.length()); // } catch (const FileError& error) //more detailed error messages than with wxWidgets { throw XmlError(error.show()); } } //################################################################################################################ bool xmlAccess::readXmlElement(const std::string& name, const TiXmlElement* parent, std::string& output) { if (parent) { const TiXmlElement* child = parent->FirstChildElement(name); if (child) { const char* text = child->GetText(); if (text) //may be NULL!! output = text; else //NULL means empty string output.clear(); return true; } } return false; } bool xmlAccess::readXmlElement(const std::string& name, const TiXmlElement* parent, wxString& output) { std::string tempString; if (!readXmlElement(name, parent, tempString)) return false; output = wxString::FromUTF8(tempString.c_str()); return true; } bool xmlAccess::readXmlElement(const std::string& name, const TiXmlElement* parent, int& output) { std::string temp; if (!readXmlElement(name, parent, temp)) return false; output = globalFunctions::stringToInt(temp); return true; } bool xmlAccess::readXmlElement(const std::string& name, const TiXmlElement* parent, unsigned int& output) { int dummy = 0; if (!xmlAccess::readXmlElement(name, parent, dummy)) return false; output = static_cast(dummy); return true; } bool xmlAccess::readXmlElement(const std::string& name, const TiXmlElement* parent, long& output) { std::string temp; if (readXmlElement(name, parent, temp)) { output = globalFunctions::stringToLong(temp); return true; } else return false; } bool xmlAccess::readXmlElement(const std::string& name, const TiXmlElement* parent, bool& output) { std::string dummy; if (readXmlElement(name, parent, dummy)) { output = dummy == "true"; return true; } else return false; } bool xmlAccess::readXmlElement(const std::string& name, const TiXmlElement* parent, std::vector& output) { if (parent) { output.clear(); //load elements const TiXmlElement* element = parent->FirstChildElement(name); while (element) { const char* text = element->GetText(); if (text) //may be NULL!! output.push_back(wxString::FromUTF8(text)); else output.push_back(wxEmptyString); element = element->NextSiblingElement(); } return true; } return false; } bool xmlAccess::readXmlAttribute(const std::string& name, const TiXmlElement* node, std::string& output) { if (node) { const std::string* attrib = node->Attribute(name); if (attrib) //may be NULL! { output = *attrib; return true; } } return false; } bool xmlAccess::readXmlAttribute(const std::string& name, const TiXmlElement* node, wxString& output) { std::string tempString; if (!readXmlAttribute(name, node, tempString)) return false; output = wxString::FromUTF8(tempString.c_str()); return true; } bool xmlAccess::readXmlAttribute(const std::string& name, const TiXmlElement* node, int& output) { std::string dummy; if (readXmlAttribute(name, node, dummy)) { output = globalFunctions::stringToInt(dummy); return true; } else return false; } bool xmlAccess::readXmlAttribute(const std::string& name, const TiXmlElement* node, unsigned int& output) { std::string dummy; if (readXmlAttribute(name, node, dummy)) { output = globalFunctions::stringToInt(dummy); return true; } else return false; } bool xmlAccess::readXmlAttribute(const std::string& name, const TiXmlElement* node, bool& output) { std::string dummy; if (readXmlAttribute(name, node, dummy)) { output = dummy == "true"; return true; } else return false; } //################################################################################################################ void xmlAccess::addXmlElement(const std::string& name, const std::string& value, TiXmlElement* parent) { if (parent) { TiXmlElement* subElement = new TiXmlElement(name); parent->LinkEndChild(subElement); subElement->LinkEndChild(new TiXmlText(value)); } } void xmlAccess::addXmlElement(const std::string& name, const wxString& value, TiXmlElement* parent) { addXmlElement(name, std::string(value.ToUTF8()), parent); } void xmlAccess::addXmlElement(const std::string& name, const int value, TiXmlElement* parent) { addXmlElement(name, globalFunctions::numberToString(value), parent); } void xmlAccess::addXmlElement(const std::string& name, const unsigned int value, TiXmlElement* parent) { addXmlElement(name, static_cast(value), parent); } void xmlAccess::addXmlElement(const std::string& name, const long value, TiXmlElement* parent) { addXmlElement(name, globalFunctions::numberToString(value), parent); } void xmlAccess::addXmlElement(const std::string& name, const bool value, TiXmlElement* parent) { if (value) addXmlElement(name, std::string("true"), parent); else addXmlElement(name, std::string("false"), parent); } void xmlAccess::addXmlElement(const std::string& name, const std::vector& value, TiXmlElement* parent) { for (std::vector::const_iterator i = value.begin(); i != value.end(); ++i) addXmlElement(name, std::string(i->ToUTF8()), parent); } void xmlAccess::addXmlAttribute(const std::string& name, const std::string& value, TiXmlElement* node) { if (node) node->SetAttribute(name, value); } void xmlAccess::addXmlAttribute(const std::string& name, const wxString& value, TiXmlElement* node) { addXmlAttribute(name, std::string(value.ToUTF8()), node); } void xmlAccess::addXmlAttribute(const std::string& name, const int value, TiXmlElement* node) { addXmlAttribute(name, globalFunctions::numberToString(value), node); } void xmlAccess::addXmlAttribute(const std::string& name, const unsigned int value, TiXmlElement* node) { addXmlAttribute(name, globalFunctions::numberToString(value), node); } void xmlAccess::addXmlAttribute(const std::string& name, const bool value, TiXmlElement* node) { if (value) addXmlAttribute(name, std::string("true"), node); else addXmlAttribute(name, std::string("false"), node); } using xmlAccess::XmlParser; void XmlParser::logError(const std::string& nodeName) { failedNodes.push_back(wxString::FromUTF8(nodeName.c_str())); } bool XmlParser::errorsOccured() const { return !failedNodes.empty(); } const wxString XmlParser::getErrorMessageFormatted() const { wxString errorMessage = wxString(_("Could not read values for the following XML nodes:")) + wxT("\n"); for (std::vector::const_iterator i = failedNodes.begin(); i != failedNodes.end(); ++i) errorMessage += wxString(wxT("<")) + *i + wxT(">") + ((i - failedNodes.begin() + 1) % 3 == 0 ? wxT("\n") : wxT(" ")); return errorMessage; }