Rev 45 | Blame | Last modification | View Log | RSS feed
/*
* Copyright (C) 2020, 2021 by Andreas Theofilu <andreas@theosys.at>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include <errno.h>
#include <iostream>
#include <fstream>
#include <exception>
#include "tnameformat.h"
#include "treadxml.h"
using std::ifstream;
using std::ofstream;
using std::string;
using std::exception;
TReadXML::TReadXML(const std::string& fname, bool trim)
: mFName(fname)
{
DECL_TRACER("TReadXML::TReadXML(const std::string& fname)");
openFile(trim);
}
TReadXML::~TReadXML()
{
DECL_TRACER("TReadXML::~TReadXML()");
if (mTree)
mxmlDelete(mTree);
}
bool TReadXML::openFile(bool trim)
{
DECL_TRACER("TReadXML::openFile()");
TError::clear();
if (mFName.empty())
{
TError::setErrorMsg("No XML file to read!");
MSG_ERROR(TError::getErrorMsg());
return false;
}
MSG_TRACE("Opening XML file " << mFName << " for reading ...");
try
{
ifstream xml(mFName.c_str());
if (!xml)
{
TError::setErrorMsg("Error opening the file " + mFName);
MSG_ERROR(TError::getErrorMsg());
return false;
}
string buffer;
try
{
xml.seekg(0, xml.end);
size_t length = xml.tellg();
xml.seekg(0, xml.beg);
buffer.resize(length, ' ');
char *begin = &*buffer.begin();
xml.read(begin, length);
}
catch (exception& e)
{
TError::setErrorMsg("Error reading a file: " + string(e.what()));
MSG_ERROR(TError::getErrorMsg());
xml.close();
return false;
}
xml.close();
string cbuf = TNameFormat::cp1250ToUTF8(buffer);
// The used XML parser has a bug. If it is a formatted XML file, then
// it crashes. Therefore the following call removes all formatting
// characters to ensure the XML parser succeeds. To avoid this (maybe)
// time consuming trimming, there is a parameter to control whether
// the trimming should be made or not.
if (trim)
cbuf = TNameFormat::trimXML(cbuf);
mTree = mxmlLoadString(NULL, cbuf.c_str(), MXML_OPAQUE_CALLBACK);
buffer.clear();
if (mTree == NULL)
{
TError::setErrorMsg("Error reading XML file " + mFName);
MSG_ERROR(TError::getErrorMsg());
return false;
}
}
catch (exception& e)
{
TError::setErrorMsg("Fatal error: " + string(e.what()));
MSG_ERROR(TError::getErrorMsg());
return false;
}
return true;
}
std::string & TReadXML::findElement(const std::string& name)
{
DECL_TRACER("TReadXML::findElement(const std::string& name)");
const string none;
return findElement(name, none);
}
std::string & TReadXML::findElement(const std::string& name, const std::string& attr)
{
DECL_TRACER("TReadXML::findElement(const std::string& name, const std::string& attr)");
TError::clear();
if (!mTree)
{
TError::setErrorMsg("No valid XML available!");
MSG_ERROR(TError::getErrorMsg());
mValue.clear();
mValid = false;
return mValue;
}
try
{
if (!attr.empty())
mNode = mxmlFindElement(mTree, mTree, name.c_str(), attr.c_str(), NULL, MXML_DESCEND);
else
mNode = mxmlFindElement(mTree, mTree, name.c_str(), NULL, NULL, MXML_DESCEND);
if (!mNode)
{
if (attr.empty())
{
MSG_WARNING("Element " << name << " not found!");
}
else
{
MSG_WARNING("Element " << name << " with attribute " << attr << " not found!");
}
mValue.clear();
mValid = false;
return mValue;
}
mLastNode = mNode;
extractValue(mNode);
if (!attr.empty())
{
const char *attribute = mxmlElementGetAttr(mNode, attr.c_str());
if (attribute)
mAttribute.assign(attribute);
else
mAttribute.clear();
// MSG_DEBUG("Found attribute " << mAttribute);
}
}
catch (exception& e)
{
TError::setErrorMsg("Fatal error: " + string(e.what()));
MSG_ERROR(TError::getErrorMsg());
mValid = false;
mValue.clear();
return mValue;
}
mValid = true;
return mValue;
}
std::string & TReadXML::findElement(mxml_node_t* node, const std::string& name)
{
DECL_TRACER("TReadXML::findElement(mxml_node_t* node, const std::string& name)");
const string none;
return findElement(node, name, none);
}
std::string & TReadXML::findElement(mxml_node_t* node, const std::string& name, const std::string& attr)
{
DECL_TRACER("TReadXML::findElement(mxml_node_t* node, const std::string& name, const std::string& attr)");
TError::clear();
if (!mTree || !node)
{
TError::setErrorMsg("No valid XML available!");
MSG_ERROR(TError::getErrorMsg());
mValue.clear();
mValid = false;
return mValue;
}
if (!attr.empty())
mNode = mxmlFindElement(node, mTree, name.c_str(), attr.c_str(), NULL, MXML_DESCEND);
else
mNode = mxmlFindElement(node, mTree, name.c_str(), NULL, NULL, MXML_DESCEND);
if (!mNode)
{
if (attr.empty())
{
MSG_WARNING("Element " << name << " not found!");
}
else
{
MSG_WARNING("Element " << name << " with attribute " << attr << " not found!");
}
mValue.clear();
mValid = false;
return mValue;
}
mLastNode = mNode;
extractValue(mNode);
if (!attr.empty())
{
const char *attribute = mxmlElementGetAttr(mNode, attr.c_str());
if (attribute)
mAttribute.assign(attribute);
else
mAttribute.clear();
// MSG_DEBUG("Found attribute " << mAttribute);
}
mValid = true;
return mValue;
}
std::string & TReadXML::findNextElement(const std::string& name)
{
DECL_TRACER("TReadXML::findNextElement(const std::string& name)");
return findElement(mLastNode, name);
}
std::string & TReadXML::findNextElement(const std::string& name, const std::string& attr)
{
DECL_TRACER("TReadXML::findNextElement(const std::string& name, const std::string& attr)");
return findElement(mLastNode, name, attr);
}
std::string & TReadXML::getAttribute(const string& name)
{
DECL_TRACER("TReadXML::getAttribute(const string& name)");
const char *value = mxmlElementGetAttr(mNode, name.c_str());
if (!value)
mAttribute.clear();
else
mAttribute.assign(value);
// MSG_DEBUG("Found attribute " << mAttribute << " from name " << name);
return mAttribute;
}
std::string & TReadXML::getAttribute(const string& name, int index)
{
DECL_TRACER("TReadXML::getAttribute(const string& name)");
const char *pname = name.c_str();
const char *value = mxmlElementGetAttrByIndex(mNode, index, &pname);
if (!value)
mAttribute.clear();
else
mAttribute.assign(value);
// MSG_DEBUG("Found attribute " << mAttribute << " from name " << name << " at index " << index);
return mAttribute;
}
mxml_node_t *TReadXML::getFirstChild()
{
DECL_TRACER("*TReadXML::getFirstChild()");
mxml_node_t *n = mxmlGetFirstChild(mLastNode);
if (n)
{
inode = n;
mElement = mxmlGetElement(n);
}
// if (inode)
// MSG_DEBUG("Found element " << mElement);
return n;
}
mxml_node_t *TReadXML::getNextChild()
{
DECL_TRACER("*TReadXML::getNextChild()");
if (!inode)
return nullptr;
mxml_node_t *n = mxmlGetNextSibling(inode);
if (n)
{
inode = n;
mElement = mxmlGetElement(n);
}
// if (inode)
// MSG_DEBUG("Found element " << mElement);
return n;
}
mxml_node_t *TReadXML::getLastChild()
{
DECL_TRACER("*TReadXML::getLastChild()");
mxml_node_t *n = mxmlGetLastChild(mLastNode);
if (n)
{
inode = n;
mElement = mxmlGetElement(n);
}
// if (inode)
// MSG_DEBUG("Found element " << mElement);
return n;
}
mxml_node_t *TReadXML::getFirstChild(mxml_node_t* node)
{
DECL_TRACER("TReadXML::getFirstChild(mxml_node_t* node)");
if (!node)
return nullptr;
mxml_node_t *n = mxmlGetFirstChild(node);
// if (n)
// MSG_DEBUG("Found element " << mxmlGetElement(n));
return n;
}
mxml_node_t *TReadXML::getNextChild(mxml_node_t* node)
{
DECL_TRACER("TReadXML::getNextChild(mxml_node_t* node)");
if (!node)
return nullptr;
mxml_node_t *n = mxmlGetNextSibling(node);
// if (n)
// MSG_DEBUG("Found element " << mxmlGetElement(n));
return n;
}
mxml_node_t *TReadXML::getLastChild(mxml_node_t* node)
{
DECL_TRACER("TReadXML::getLastChild(mxml_node_t* node)");
mxml_node_t *n = mxmlGetLastChild(node);
if (!node)
return nullptr;
// if (n)
// MSG_DEBUG("Found element " << mxmlGetElement(n));
return n;
}
std::string& TReadXML::getTextFromNode(mxml_node_t* n)
{
DECL_TRACER("TReadXML::getTextFromNode(mxml_node_t* n)");
mValue.clear();
if (!n)
return mValue;
extractValue(n);
// MSG_DEBUG("Value = " << mValue << ", Element = " << mxmlGetElement(n));
return mValue;
}
int TReadXML::getIntFromNode(mxml_node_t* n)
{
DECL_TRACER("TReadXML::getIntFromNode(mxml_node_t* n)");
if (!n)
return 0;
string val = getTextFromNode(n);
if (val.empty())
return 0;
return atoi(val.c_str());
}
double TReadXML::getDoubleFromNode(mxml_node_t* n)
{
DECL_TRACER("TReadXML::getDoubleFromNode(mxml_node_t* n)");
if (!n)
return 0.0;
string val = getTextFromNode(n);
if (val.empty())
return 0.0;
return atof(val.c_str());
}
std::string TReadXML::getAttributeFromNode(mxml_node_t* n, const std::string& attr)
{
DECL_TRACER("TReadXML::getAttributeFromNode(mxml_node_t* n, const std::string& attr)");
const char *a = mxmlElementGetAttr(n, attr.c_str());
if (a)
mAttribute = a;
else
mAttribute.clear();
return mAttribute;
}
std::string& TReadXML::getElementName()
{
DECL_TRACER("TReadXML::getElementName()");
mElement.clear();
if (!mLastNode)
return mElement;
const char *name = mxmlGetElement(mLastNode);
if (name)
mElement = name;
return mElement;
}
std::string& TReadXML::getElementName(mxml_node_t* node)
{
DECL_TRACER("TReadXML::getElementName(mxml_node_t* node)");
mElement.clear();
if (!node)
return mElement;
const char *name = mxmlGetElement(node);
if (name)
mElement = name;
return mElement;
}
std::string TReadXML::_typeName(mxml_type_t t)
{
string ret;
switch (t)
{
case MXML_CUSTOM: ret = "MXML_CUSTOM"; break;
case MXML_ELEMENT: ret = "MXML_ELEMENT"; break;
case MXML_INTEGER: ret = "MXML_INTEGER"; break;
case MXML_OPAQUE: ret = "MXML_OPAQUE"; break;
case MXML_REAL: ret = "MXML_REAL"; break;
case MXML_TEXT: ret = "MXML_TEXT"; break;
case MXML_IGNORE: ret = "MXML_IGNORE"; break;
}
return ret;
}
void TReadXML::extractValue(mxml_node_t *n)
{
DECL_TRACER("TReadXML::extractValue(mxml_node_t *n)");
const char *ename = nullptr, *txt = nullptr;
int val = 0;
double fval = 0.0;
mxml_type_t t = mxmlGetType(n);
if (!n)
return;
mValue.clear();
ename = mxmlGetElement(n);
if (ename)
mElement = ename;
else
mElement.clear();
switch(t)
{
case MXML_CUSTOM: txt = (const char *)mxmlGetCustom(n); break;
case MXML_ELEMENT: txt = mxmlGetOpaque(n); break;
case MXML_INTEGER:
val = mxmlGetInteger(n);
mValue = std::to_string(val);
break;
case MXML_OPAQUE: txt = mxmlGetOpaque(n); break;
case MXML_REAL:
fval = mxmlGetReal(n);
mValue = std::to_string(fval);
break;
case MXML_TEXT: txt = mxmlGetText(n, 0); break;
case MXML_IGNORE: mValue.clear(); break;
}
if (txt)
mValue = txt;
// MSG_DEBUG("Element: " << mElement << ", type: " << _typeName(t) << ", content: " << mValue);
}