//============================================================================= // // 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 // // 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: $parse() 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 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: $lastError() Returns the last error occured inside the parser. You will typically call this function when $parse() above returns $false. !fn: $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: $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: $onElementStart(,,,) This function is called when an element opening tag is encountered. The of the tag is passed as the first parameter. The are passed in the form of a hash with attribute values indexed by their names. When the contains a namespace then it is also reported in the splitted 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: $onElementEnd(,,) This function is called when an element closing tag is encountered. The of the tag is passed as the first parameter. When the contains a namespace then it is also reported in the splitted 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: $onText($0 = ) This function is called when a chunk of text is encountered inside the document. The parsed 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: $onWarning() This function is called when the parser generates a recoverable error. The error 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: $onError() This function is called when the parser generates an unrecoverable error. The error is passed as the first parameter. The document parsing can't continue. The default implementation does nothing besides returning $true. */ #ifndef TQT_NO_XML #include class KviXmlHandler : public TQXmlDefaultHandler { protected: KviKvsObject_xmlreader * m_pReader; TQString 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(TQString &szMsg,bool bError,const TQXmlParseException &exception) { if(bError) KviTQString::sprintf(szMsg,__tr2qs("Error near line %d, column %d"),exception.lineNumber(),exception.columnNumber()); else KviTQString::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 TQString &szNamespaceUri,const TQString &szLocalName,const TQString &szQualifiedName,const TQXmlAttributes &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;iset(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 TQString &szNamespaceUri,const TQString &szLocalName,const TQString &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 TQString &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 TQXmlParseException &exception) { // recoverable TQString 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 TQXmlParseException &exception) { // recoverable TQString 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 TQXmlParseException &exception) { TQString szMsg; decodeException(szMsg,true,exception); m_pReader->fatalError(szMsg); return true; } virtual TQString errorString() { return m_szErrorString; } }; #endif // !TQT_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 TQString &szError) { m_szLastError = szError; KviKvsVariantList vArgs; vArgs.append(new KviKvsVariant(m_szLastError)); callFunction(this,"onError",&vArgs); } bool KviKvsObject_xmlreader::function_parse(KviKvsObjectFunctionCall *c) { TQString szString; KVSO_PARAMETERS_BEGIN(c) KVSO_PARAMETER("string",KVS_PT_STRING,0,szString) KVSO_PARAMETERS_END(c) #ifdef TQT_NO_XML fatalError(__tr2qs("XML support not available in the TQt library")); c->returnValue()->setBoolean(false); #else m_szLastError = ""; KviXmlHandler handler(this); TQXmlInputSource 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. KviTQCString utf8data = KviTQString::toUtf8(szString); TQByteArray data = utf8data; data.truncate(utf8data.length()); // don't include the null terminator in data source.setData(data); //tqDebug("PARSING(%s) LEN(%d)",szString.utf8().data(),szString.utf8().length()); TQXmlSimpleReader 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"