summaryrefslogtreecommitdiff
path: root/shared/xml_base.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'shared/xml_base.cpp')
-rw-r--r--shared/xml_base.cpp400
1 files changed, 400 insertions, 0 deletions
diff --git a/shared/xml_base.cpp b/shared/xml_base.cpp
new file mode 100644
index 00000000..3213786d
--- /dev/null
+++ b/shared/xml_base.cpp
@@ -0,0 +1,400 @@
+// **************************************************************************
+// * 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 "xml_base.h"
+#include <wx/intl.h>
+#include "file_io.h"
+#include "string_conv.h"
+#include "system_constants.h"
+#include <boost/scoped_array.hpp>
+#include "file_handling.h"
+
+using namespace ffs3;
+
+
+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";
+}
+
+
+namespace
+{
+//convert (0xD, 0xA) and (0xD) to 0xA: just like in TiXmlDocument::LoadFile(); not sure if actually needed
+void normalize(std::vector<char>& stream)
+{
+ std::vector<char> tmp;
+ for (std::vector<char>::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 loadRawXmlDocument(const wxString& filename, TiXmlDocument& document) //throw XmlError()
+{
+ using xmlAccess::XmlError;
+
+ const size_t BUFFER_SIZE = 2 * 1024 * 1024; //maximum size of a valid FreeFileSync XML file!
+
+ std::vector<char> inputBuffer;
+ inputBuffer.resize(BUFFER_SIZE);
+
+ try
+ {
+ FileInput inputFile(wxToZ(filename)); //throw FileError();
+ const size_t bytesRead = inputFile.read(&inputBuffer[0], inputBuffer.size()); //throw FileError()
+
+ if (bytesRead == 0 || bytesRead >= inputBuffer.size()) //treat XML files larger than 2 MB as erroneous: loading larger files just wastes CPU + memory
+ throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\""));
+
+ inputBuffer.resize(bytesRead + 1);
+ inputBuffer[bytesRead] = 0; //set null-termination!!!!
+ }
+ catch (const FileError& error) //more detailed error messages than with wxWidgets
+ {
+ throw XmlError(error.msg());
+ }
+
+ //convert (0xD, 0xA) and (0xD) to (0xA): just like in TiXmlDocument::LoadFile(); not sure if actually needed
+ normalize(inputBuffer);
+
+ document.Parse(&inputBuffer[0], 0, TIXML_DEFAULT_ENCODING); //respect null-termination!
+
+ TiXmlElement* root = document.RootElement();
+
+ if (root && (root->ValueStr() == std::string("FreeFileSync"))) //check for FFS configuration xml
+ return; //finally... success!
+
+ throw XmlError(wxString(_("Error parsing configuration file:")) + wxT("\n\"") + filename + wxT("\""));
+}
+}
+
+
+xmlAccess::XmlType xmlAccess::getXmlType(const wxString& filename) //throw()
+{
+ try
+ {
+ TiXmlDocument doc;
+ ::loadRawXmlDocument(filename, doc); //throw XmlError()
+
+ TiXmlElement* root = doc.RootElement();
+ if (root)
+ {
+ const char* cfgType = root->Attribute("XmlType");
+ if (cfgType)
+ {
+ 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;
+ }
+ }
+ }
+ catch (const XmlError&) {}
+
+ return XML_OTHER;
+}
+
+
+void xmlAccess::loadXmlDocument(const wxString& filename, const xmlAccess::XmlType type, TiXmlDocument& document) //throw XmlError()
+{
+ ::loadRawXmlDocument(filename, document); //throw XmlError()
+
+ TiXmlElement* root = document.RootElement();
+ if (root)
+ {
+ 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)
+{
+ 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);
+}
+
+
+namespace
+{
+bool saveNecessary(const Zstring& filename, const std::string& dataToWrite) //throw()
+{
+ try
+ {
+ if (ffs3::getFilesize(filename) != static_cast<unsigned long>(dataToWrite.size())) //throw FileError();
+ return true;
+
+ boost::scoped_array<char> inputBuffer(new char[dataToWrite.size() + 1]); //+ 1 in order to test for end of file!
+
+ FileInput inputFile(filename); //throw FileError();
+
+ const size_t bytesRead = inputFile.read(inputBuffer.get(), dataToWrite.size() + 1); //throw FileError()
+ if (bytesRead != dataToWrite.size()) //implicit test for eof!
+ return true;
+
+ return ::memcmp(inputBuffer.get(), dataToWrite.c_str(), dataToWrite.size()) != 0;
+ }
+ catch (const FileError&)
+ {
+ return true;
+ }
+}
+}
+
+
+void xmlAccess::saveXmlDocument(const wxString& filename, const TiXmlDocument& document) //throw (XmlError)
+{
+ TiXmlBase::SetCondenseWhiteSpace(false); //do not condense whitespace characters
+
+ //convert XML into continuous byte sequence
+ TiXmlPrinter printer;
+ printer.SetLineBreak(wxString(common::LINE_BREAK).ToUTF8());
+ document.Accept(&printer);
+ const std::string buffer = printer.Str();
+
+ if (saveNecessary(wxToZ(filename), buffer)) //only write file if different data will be written
+ {
+ 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.msg());
+ }
+ }
+}
+//################################################################################################################
+
+
+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, 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<wxString>& 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, 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 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<wxString>& value, TiXmlElement* parent)
+{
+ for (std::vector<wxString>::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 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::errorsOccurred() 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<wxString>::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;
+}
+
bgstack15