diff options
Diffstat (limited to 'src/modules/objects/class_xmlreader.cpp')
-rw-r--r-- | src/modules/objects/class_xmlreader.cpp | 343 |
1 files changed, 343 insertions, 0 deletions
diff --git a/src/modules/objects/class_xmlreader.cpp b/src/modules/objects/class_xmlreader.cpp new file mode 100644 index 00000000..f4bccc8f --- /dev/null +++ b/src/modules/objects/class_xmlreader.cpp @@ -0,0 +1,343 @@ +//============================================================================= +// +// File : class_xmlreader.cpp +// Created on Tue 27 Dec 2005 00:14:09 by Szymon Stefanek +// +// This file is part of the KVIrc IRC Client distribution +// Copyright (C) 2005 Szymon Stefanek <pragma at kvirc dot net> +// +// 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 2 +// of the License, or (at your opinion) 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 "class_xmlreader.h" + +#include "kvi_locale.h" + +#include "kvi_kvs_variantlist.h" +#include "kvi_kvs_hash.h" + +#include "kvi_qstring.h" + + +/* + @doc: xmlreader + @keyterms: + xml + @title: + xmlreader class + @type: + class + @short: + A simple xml document parser + @inherits: + [class]object[/class] + @description: + This class implements a really simple xml document parser. + You will usually derive your own class from this one and reimplement + some of the events that it triggers. + You will typically reimplement [classfnc:xmlparser]onElementStart[/classfnc]() + and [classfnc:xmlparser]onElementEnd[/classfnc]() that will be called + during the execution of [classfnc:xmlparser]$parse[/classfnc]() in an order + reflecting the order of elements in the parsed document. + @functions: + !fn: <boolean> $parse(<xml_data:string>) + Call this function to parse a string that contains an XML document. + A typical call for this method will look like: + [example] + %x = [fnc]$new[/fnc](xmlparser) + %x->$parse([fnc]$file.read[/fnc]("/home/somefile.xml")) + [/example] + During the call the <xml_data> string will be parsed and the + relevant on* events (see below) will be triggered. + $parse will return $true when the parsing terminates succesfully + or $false if it aborts for some reason (unrecoverable error + in the document, user abort etc...). + If this function return $false then you can call $lastError() to + obtain a descriptive error message. + + !fn: <string> $lastError() + Returns the last error occured inside the parser. + You will typically call this function when $parse() above returns $false. + + !fn: <boolean> $onDocumentStart() + This function is called when the document parsing starts. + You can reimplement it in order to handle this notification. + You should return $true if you want document parsing to continue + and $false if you want it to be aborted. + The default implementation does nothing besides returning $true. + + !fn: <boolean> $onDocumentEnd() + This function is called when the document parsing terminates succesfully. + You can reimplement it in order to handle this notification. + You should return $true if you want document parsing to continue + and $false if you want it to be aborted. + The default implementation does nothing besides returning $true. + + !fn: <boolean> $onElementStart(<qualified_name:string>,<attributes:hash>,<namespace:string>,<local_name:string>) + This function is called when an element opening tag is encountered. + The <qualified_name> of the tag is passed as the first parameter. + The <attributes> are passed in the form of a hash with attribute + values indexed by their names. + When the <qualified_name> contains a namespace then it is also reported + in the splitted <namespace> <local_name> pair. + You should return $true if you want document parsing to continue + and $false if you want it to be aborted. + The default implementation does nothing besides returning $true. + + !fn: <boolean> $onElementEnd(<qualified_name:string>,<namespace:string>,<local_name:string>) + This function is called when an element closing tag is encountered. + The <qualified_name> of the tag is passed as the first parameter. + When the <qualified_name> contains a namespace then it is also reported + in the splitted <namespace> <local_name> pair. + You should return $true if you want document parsing to continue + and $false if you want it to be aborted. + The default implementation does nothing besides returning $true. + + !fn: <boolean> $onText($0 = <text:string>) + This function is called when a chunk of text is encountered inside the document. + The parsed <text> chunk is passed as the first parameter. + You should return $true if you want document parsing to continue + and $false if you want it to be aborted. + The default implementation does nothing besides returning $true. + + !fn: <boolean> $onWarning(<message:string>) + This function is called when the parser generates a recoverable error. + The error <message> is passed as the first parameter. + You should return $true if you want document parsing to continue + and $false if you want it to be aborted. + The default implementation does nothing besides returning $true. + + !fn: <boolean> $onError(<message:string>) + This function is called when the parser generates an unrecoverable error. + The error <message> is passed as the first parameter. + The document parsing can't continue. + The default implementation does nothing besides returning $true. +*/ + + +#ifndef QT_NO_XML + +#include <qxml.h> + +class KviXmlHandler : public QXmlDefaultHandler +{ +protected: + KviKvsObject_xmlreader * m_pReader; + QString m_szErrorString; +public: + KviXmlHandler(KviKvsObject_xmlreader * pReader) + { + m_pReader = pReader; + } + ~KviXmlHandler() + { + } +private: + bool kvsCodeFailure() + { + m_szErrorString = __tr2qs("Error in KVS class implementation: processing aborted"); + return false; + } + bool kvsCodeAbort() + { + m_szErrorString = __tr2qs("Processing aborted"); + return false; + } + void decodeException(QString &szMsg,bool bError,const QXmlParseException &exception) + { + if(bError) + KviQString::sprintf(szMsg,__tr2qs("Error near line %d, column %d"),exception.lineNumber(),exception.columnNumber()); + else + KviQString::sprintf(szMsg,__tr2qs("Warning near line %d, column %d"),exception.lineNumber(),exception.columnNumber()); + szMsg += ": "; + szMsg += exception.message(); + } + bool handleKvsCallReturnValue(KviKvsVariant * pRetVal) + { + if(!pRetVal->asBoolean())return kvsCodeAbort(); + return true; + } +public: + virtual bool startDocument() + { + KviKvsVariant ret; + if(!m_pReader->callFunction(m_pReader,"onDocumentStart",&ret)) + return kvsCodeFailure(); + return handleKvsCallReturnValue(&ret); + } + + virtual bool endDocument() + { + KviKvsVariant ret; + if(!m_pReader->callFunction(m_pReader,"onDocumentEnd",&ret)) + return kvsCodeFailure(); + return handleKvsCallReturnValue(&ret); + } + + virtual bool startElement(const QString &szNamespaceUri,const QString &szLocalName,const QString &szQualifiedName,const QXmlAttributes &attrs) + { + KviKvsVariant ret; + KviKvsVariantList par; + par.setAutoDelete(true); + par.append(new KviKvsVariant(szQualifiedName)); + KviKvsHash * pHash = new KviKvsHash(); + par.append(new KviKvsVariant(pHash)); + par.append(new KviKvsVariant(szNamespaceUri)); + par.append(new KviKvsVariant(szLocalName)); + int c = attrs.count(); + for(int i=0;i<c;i++) + pHash->set(attrs.qName(i),new KviKvsVariant(attrs.value(i))); + if(!m_pReader->callFunction(m_pReader,"onElementStart",&ret,&par)) + return kvsCodeFailure(); + return handleKvsCallReturnValue(&ret); + } + + virtual bool endElement(const QString &szNamespaceUri,const QString &szLocalName,const QString &szQualifiedName) + { + KviKvsVariant ret; + KviKvsVariantList par; + par.setAutoDelete(true); + par.append(new KviKvsVariant(szQualifiedName)); + par.append(new KviKvsVariant(szNamespaceUri)); + par.append(new KviKvsVariant(szLocalName)); + if(!m_pReader->callFunction(m_pReader,"onElementEnd",&ret,&par)) + return kvsCodeFailure(); + return handleKvsCallReturnValue(&ret); + } + + virtual bool characters(const QString &szChars) + { + KviKvsVariant ret; + KviKvsVariantList par; + par.setAutoDelete(true); + par.append(new KviKvsVariant(szChars)); + if(!m_pReader->callFunction(m_pReader,"onText",&ret,&par)) + return kvsCodeFailure(); + return handleKvsCallReturnValue(&ret); + } + + virtual bool warning(const QXmlParseException &exception) + { + // recoverable + QString szMsg; + decodeException(szMsg,false,exception); + + KviKvsVariant ret; + KviKvsVariantList par; + par.setAutoDelete(true); + par.append(new KviKvsVariant(szMsg)); + if(!m_pReader->callFunction(m_pReader,"onWarning",&ret,&par)) + return kvsCodeFailure(); + return handleKvsCallReturnValue(&ret); + } + + virtual bool error(const QXmlParseException &exception) + { + // recoverable + QString szMsg; + decodeException(szMsg,false,exception); + + KviKvsVariant ret; + KviKvsVariantList par; + par.setAutoDelete(true); + par.append(new KviKvsVariant(szMsg)); + if(!m_pReader->callFunction(m_pReader,"onWarning",&ret,&par)) + return kvsCodeFailure(); + return handleKvsCallReturnValue(&ret); + } + + virtual bool fatalError(const QXmlParseException &exception) + { + QString szMsg; + decodeException(szMsg,true,exception); + m_pReader->fatalError(szMsg); + return true; + } + + virtual QString errorString() + { + return m_szErrorString; + } +}; + +#endif // !QT_NO_XML + + +KVSO_BEGIN_REGISTERCLASS(KviKvsObject_xmlreader,"xmlreader","object") + KVSO_REGISTER_HANDLER(KviKvsObject_xmlreader,"lastError",function_lastError) + KVSO_REGISTER_HANDLER(KviKvsObject_xmlreader,"parse",function_parse) + + KVSO_REGISTER_STANDARD_TRUERETURN_HANDLER(KviKvsObject_xmlreader,"onDocumentStart") + KVSO_REGISTER_STANDARD_TRUERETURN_HANDLER(KviKvsObject_xmlreader,"onDocumentEnd") + KVSO_REGISTER_STANDARD_TRUERETURN_HANDLER(KviKvsObject_xmlreader,"onElementStart") + KVSO_REGISTER_STANDARD_TRUERETURN_HANDLER(KviKvsObject_xmlreader,"onElementEnd") + KVSO_REGISTER_STANDARD_TRUERETURN_HANDLER(KviKvsObject_xmlreader,"onText") + KVSO_REGISTER_STANDARD_TRUERETURN_HANDLER(KviKvsObject_xmlreader,"onWarning") + KVSO_REGISTER_STANDARD_TRUERETURN_HANDLER(KviKvsObject_xmlreader,"onError") +KVSO_END_REGISTERCLASS(KviKvsObject_xmlreader) + +KVSO_BEGIN_CONSTRUCTOR(KviKvsObject_xmlreader,KviKvsObject) +KVSO_END_CONSTRUCTOR(KviKvsObject_xmlreader) + +KVSO_BEGIN_DESTRUCTOR(KviKvsObject_xmlreader) +KVSO_END_DESTRUCTOR(KviKvsObject_xmlreader) + +void KviKvsObject_xmlreader::fatalError(const QString &szError) +{ + m_szLastError = szError; + + KviKvsVariantList vArgs; + vArgs.append(new KviKvsVariant(m_szLastError)); + callFunction(this,"onError",&vArgs); +} + +bool KviKvsObject_xmlreader::function_parse(KviKvsObjectFunctionCall *c) +{ + QString szString; + KVSO_PARAMETERS_BEGIN(c) + KVSO_PARAMETER("string",KVS_PT_STRING,0,szString) + KVSO_PARAMETERS_END(c) + +#ifdef QT_NO_XML + fatalError(__tr2qs("XML support not available in the Qt library")); + c->returnValue()->setBoolean(false); +#else + m_szLastError = ""; + KviXmlHandler handler(this); + QXmlInputSource source; + // We have a problem here.. most kvirc functions already interpret the data + // read from files. We should have binary data handling features to get this to work correctly. + // The following snippet of code tries to provide a best-effort workaround. + KviQCString utf8data = KviQString::toUtf8(szString); + QByteArray data = utf8data; + data.truncate(utf8data.length()); // don't include the null terminator in data + source.setData(data); + //debug("PARSING(%s) LEN(%d)",szString.utf8().data(),szString.utf8().length()); + QXmlSimpleReader reader; + reader.setContentHandler(&handler); + reader.setErrorHandler(&handler); + c->returnValue()->setBoolean(reader.parse(source)); +#endif + return true; +} + +bool KviKvsObject_xmlreader::function_lastError(KviKvsObjectFunctionCall *c) +{ + c->returnValue()->setString(m_szLastError); + return true; +} + +#include "m_class_xmlreader.moc" |