summaryrefslogtreecommitdiffstats
path: root/lib/kofficecore/KoXmlReader.cpp
diff options
context:
space:
mode:
authortpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 01:29:50 +0000
committertpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da>2010-01-20 01:29:50 +0000
commit8362bf63dea22bbf6736609b0f49c152f975eb63 (patch)
tree0eea3928e39e50fae91d4e68b21b1e6cbae25604 /lib/kofficecore/KoXmlReader.cpp
downloadkoffice-8362bf63dea22bbf6736609b0f49c152f975eb63.tar.gz
koffice-8362bf63dea22bbf6736609b0f49c152f975eb63.zip
Added old abandoned KDE3 version of koffice
git-svn-id: svn://anonsvn.kde.org/home/kde/branches/trinity/applications/koffice@1077364 283d02a7-25f6-0310-bc7c-ecb5cbfe19da
Diffstat (limited to 'lib/kofficecore/KoXmlReader.cpp')
-rw-r--r--lib/kofficecore/KoXmlReader.cpp1568
1 files changed, 1568 insertions, 0 deletions
diff --git a/lib/kofficecore/KoXmlReader.cpp b/lib/kofficecore/KoXmlReader.cpp
new file mode 100644
index 00000000..ae1e2ae6
--- /dev/null
+++ b/lib/kofficecore/KoXmlReader.cpp
@@ -0,0 +1,1568 @@
+/* This file is part of the KDE project
+ Copyright (C) 2005 Ariya Hidayat <[email protected]>
+
+ This library is free software; you can redistribute it and/or
+ modify it under the terms of the GNU Library General Public
+ License as published by the Free Software Foundation; either
+ version 2 of the License, or (at your option) any later version.
+
+ This library 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
+ Library General Public License for more details.
+
+ You should have received a copy of the GNU Library General Public License
+ along with this library; see the file COPYING.LIB. If not, write to
+ the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+*/
+
+/*
+ This is a memory-efficient DOM implementation for KOffice. See the API
+ documentation for details.
+
+ IMPORTANT !
+
+ * When you change this stuff, make sure it DOES NOT BREAK the test suite.
+ Build tests/koxmlreadertest.cpp and verify it. Many sleepless nights
+ have been sacrificed for this piece of code, do not let those precious
+ hours wasted!
+
+ * Run testdom.cpp WITH Valgrind's memcheck tool and make sure NO illegal
+ memory read/write and any type of leak occurs. If you are not familiar
+ with Valgrind then RTFM first and come back again later on.
+
+ * The public API shall remain as compatible as QDom.
+
+ * All QDom-compatible methods should behave the same. All QDom-compatible
+ functions should return the same result. In case of doubt, run
+ koxmlreadertest.cpp but uncomment KOXML_USE_QDOM in koxmlreader.h
+ so that the tests are performed with standard QDom.
+
+ Some differences compared to QDom:
+
+ - DOM tree in KoXmlDocument is read-only, you can not modify it. This is
+ sufficient for KOffice since the tree is only accessed when loading
+ a document to the application. For saving the document to XML file,
+ use KoXmlWriter.
+
+ - Comment node (like QDomComment) is not implemented as comments are
+ simply ignored.
+
+ - DTD, entity and entity reference are not handled. Thus, the associated
+ nodes (like QDomDocumentType, QDomEntity, QDomEntityReference) are also
+ not implemented.
+
+ - Attribute mapping node is not implemented. But of course, functions to
+ query attributes of an element are available.
+
+
+ */
+
+#include "KoXmlReader.h"
+
+#ifndef KOXML_USE_QDOM
+
+#include <qxml.h>
+#include <qdom.h>
+
+#include <qmap.h>
+#include <qcstring.h>
+
+// double QString, used for hashing against namespace and qualified name pair
+class DQString
+{
+public:
+ DQString() { }
+ DQString( const QString& s1, const QString& s2 ){ str1 = s1; str2 = s2; }
+ DQString( const DQString& b ) { str1 = b.str1; str2 = b.str2; }
+ DQString& operator=( const DQString& b ){ str1 = b.str1; str2 = b.str2; return *this; }
+ bool operator==( const DQString& b ) const { return (str1==b.str1) && (str2==b.str2); }
+ bool operator!=( const DQString& b ) const { return (str1!=b.str1) || (str2!=b.str2); }
+ bool operator<( const DQString& b ) const
+ { return ( str1 < b.str1 ) ? true : ( str1==b.str1 ) ? str2<b.str2 : false; }
+ QString s1() const { return str1; }
+ QString s2() const { return str2; }
+private:
+ QString str1, str2;
+};
+
+// just for completeness, this is the self-test for DQString above
+#if 0
+ DQString b1;
+ DQString b2;
+ CHECK( b1==b2, true );
+ CHECK( b1!=b2, false );
+
+ b1 = DQString( "sweet","princess" );
+ b2 = DQString( "sweet","princess" );
+ CHECK( b1==b2, true );
+ CHECK( b1!=b2, false );
+
+ b1 = DQString( "sweet","princess" );
+ b2 = DQString( "bad","prince" );
+ CHECK( b1==b2, false );
+ CHECK( b1!=b2, true );
+#endif
+
+class KoXmlStream
+{
+public:
+ KoXmlStream(){ saveData = true; data.reserve(1024); pos = 0; }
+ QString stringData() const { return data; }
+ void setSaveData( bool s ){ saveData = s; }
+ int at() const { return pos; }
+ KoXmlStream& operator<<( const QString& str )
+ { if(saveData) data.append(str); pos+=str.length(); return *this; }
+ KoXmlStream& appendEscape( const QString& str );
+
+private:
+ bool saveData;
+ QString data;
+ int pos;
+};
+
+KoXmlStream& KoXmlStream::appendEscape( const QString& str )
+{
+ unsigned len = str.length();
+
+ if( saveData )
+ {
+ data.reserve( data.length() + len );
+ for( unsigned c=0; c<len; c++ )
+ if( str[c]=='<' ){ data.append( "&lt;"); pos += 4; } else
+ if( str[c]=='>'){ data.append( "&gt;"); pos+= 4; } else
+ if( str[c]=='"'){ data.append( "&quot;"); pos += 6; } else
+ if( str[c]=='&'){ data.append( "&amp;"); pos += 5; } else
+ { data.append( str[c] ); pos++; }
+ }
+ else
+ {
+ pos += len;
+ for( unsigned c=0; c<len; c++ )
+ if( str[c]=='<' ) pos += 3; else // "&lt;"
+ if( str[c]=='>') pos+= 3; else // "&gt;"
+ if( str[c]=='"') pos += 5; else // "&quot;"
+ if( str[c]=='&') pos += 4; // "&amp;"
+ }
+
+ return *this;
+}
+
+class KoXmlNodeData
+{
+public:
+
+ KoXmlNodeData();
+ virtual ~KoXmlNodeData();
+
+ // generic properties
+ KoXmlNode::NodeType nodeType;
+ QString tagName;
+ QString namespaceURI;
+ QString prefix;
+ QString localName;
+
+ // reference counting
+ unsigned long count;
+ void ref() { count++; }
+ void unref() { --count; if( !count ) delete this; }
+
+ // type information
+ virtual const char* typeInfo() const { return "Node"; }
+ QString nodeName() const;
+
+ // for tree and linked-list
+ KoXmlNodeData* parent;
+ KoXmlNodeData* prev;
+ KoXmlNodeData* next;
+ KoXmlNodeData* first;
+ KoXmlNodeData* last;
+
+ QString text();
+
+ // node manipulation
+ void appendChild( KoXmlNodeData* child );
+ virtual void clear();
+ KoXmlNodeData* ownerDocument();
+
+ // attributes
+ void setAttribute( const QString& name, const QString& value );
+ QString attribute( const QString& name );
+ bool hasAttribute( const QString& name );
+ void setAttributeNS( const QString& nsURI, const QString& name, const QString& value );
+ QString attributeNS( const QString& nsURI, const QString& name );
+ bool hasAttributeNS( const QString& nsURI, const QString& name );
+
+ // for text and CDATA
+ QString data() const;
+ void setData( const QString& data );
+
+ // for document node
+ QXmlSimpleReader* xmlReader;
+ QString buffer;
+ bool setContent( QXmlInputSource* source, QXmlReader* reader,
+ QString* errorMsg = 0, int* errorLine = 0, int* errorColumn = 0 );
+
+ // used when doing on-demand (re)parse
+ bool loaded;
+ unsigned startPos, endPos;
+ void loadChildren( int depth=1 );
+ void unloadChildren();
+ bool fastLoading;
+
+private:
+ QMap<QString,QString> attr;
+ QMap<DQString,QString> attrNS;
+ QString textData;
+ friend class KoXmlHandler;
+};
+
+class KoXmlHandler : public QXmlDefaultHandler
+{
+public:
+ KoXmlHandler( KoXmlNodeData*, bool processNamespace );
+ ~KoXmlHandler();
+
+ void setMaxDepth( int d ){ maxDepth = d; }
+ void setInitialOffset( int ofs ){ parseOffset = ofs; }
+
+ // content handler
+ bool startDocument();
+ bool endDocument();
+
+ bool startElement( const QString& nsURI, const QString& localName,
+ const QString& qName, const QXmlAttributes& atts );
+ bool endElement( const QString& nsURI, const QString& localName,
+ const QString& qName );
+
+ bool characters( const QString& ch );
+ bool processingInstruction( const QString& target, const QString& data );
+ bool skippedEntity( const QString& name );
+
+ // lexical handler
+ bool startCDATA();
+ bool endCDATA();
+ bool startEntity( const QString & );
+ bool endEntity( const QString & );
+ bool startDTD( const QString& name, const QString& publicId,
+ const QString& systemId );
+ bool comment( const QString& ch );
+
+ // decl handler
+ bool externalEntityDecl( const QString &name, const QString &publicId,
+ const QString &systemId ) ;
+
+ // DTD handler
+ bool notationDecl( const QString & name, const QString & publicId,
+ const QString & systemId );
+ bool unparsedEntityDecl( const QString &name, const QString &publicId,
+ const QString &systemId, const QString &notationName ) ;
+
+ // error handler
+ bool fatalError( const QXmlParseException& exception );
+
+ QString errorMsg;
+ int errorLine;
+ int errorColumn;
+
+private:
+ bool processNamespace;
+ KoXmlNodeData* rootNode;
+ KoXmlNodeData* currentNode;
+ QString entityName;
+ bool cdata;
+ int parseOffset;
+ KoXmlStream bufferStream;
+ int elementDepth;
+ int maxDepth;
+};
+
+// ==================================================================
+//
+// KoXmlNodeData
+//
+// ==================================================================
+
+KoXmlNodeData::KoXmlNodeData()
+{
+ nodeType = KoXmlNode::NullNode;
+
+ tagName = QString::null;
+ prefix = QString::null;
+ localName = QString::null;
+ namespaceURI = QString::null;
+ textData = QString::null;
+
+ count = 1;
+ parent = 0;
+ prev = next = 0;
+ first = last = 0;
+
+ xmlReader = 0;
+ startPos = endPos = 0;
+
+ fastLoading = false;
+
+ // assume true, it will be set to false by XML parser when this node
+ // apparently has children AND the children are not loaded
+ loaded = true;
+}
+
+KoXmlNodeData::~KoXmlNodeData()
+{
+ clear();
+}
+
+void KoXmlNodeData::clear()
+{
+ if( first )
+ for( KoXmlNodeData* node = first; node ; )
+ {
+ KoXmlNodeData* next = node->next;
+ node->unref();
+ node = next;
+ }
+
+ nodeType = KoXmlNode::NullNode;
+ tagName = QString::null;
+ prefix = QString::null;
+ namespaceURI = QString::null;
+ textData = QString::null;
+
+ attr.clear();
+ attrNS.clear();
+
+ parent = 0;
+ prev = next = 0;
+ first = last = 0;
+
+ delete xmlReader;
+ xmlReader = 0;
+ buffer = QString::null;
+}
+
+QString KoXmlNodeData::text()
+{
+ QString t( "" );
+
+ loadChildren();
+
+ KoXmlNodeData* node = first;
+ while ( node )
+ {
+ switch( node->nodeType )
+ {
+ case KoXmlNode::ElementNode:
+ t += node->text(); break;
+ case KoXmlNode::TextNode:
+ t += node->data(); break;
+ case KoXmlNode::CDATASectionNode:
+ t += node->data(); break;
+ default: break;
+ }
+ node = node->next;
+ }
+
+ return t;
+}
+
+QString KoXmlNodeData::nodeName() const
+{
+ QString n;
+
+ switch( nodeType )
+ {
+ case KoXmlNode::ElementNode:
+ n = tagName;
+ if (!prefix.isEmpty()) n.prepend(":").prepend(prefix); break;
+ case KoXmlNode::TextNode: return QString("#text");
+ case KoXmlNode::CDATASectionNode: return QString("#cdata-section");
+ case KoXmlNode::DocumentNode: return QString("#document");
+ default: break;
+ }
+
+ return n;
+}
+
+KoXmlNodeData* KoXmlNodeData::ownerDocument()
+{
+ KoXmlNodeData* owner = this;
+
+ while( owner->parent )
+ owner = owner->parent;
+
+ return (owner->nodeType==KoXmlNode::DocumentNode) ? owner : 0;
+}
+
+void KoXmlNodeData::appendChild( KoXmlNodeData* node )
+{
+ node->parent = this;
+ if( !last )
+ first = last = node;
+ else
+ {
+ last->next = node;
+ node->prev = last;
+ node->next = 0;
+ last = node;
+ }
+}
+
+void KoXmlNodeData::setAttribute( const QString& name, const QString& value )
+{
+ attr[ name ] = value;
+}
+
+QString KoXmlNodeData::attribute( const QString& name )
+{
+ return attr[ name ];
+}
+
+bool KoXmlNodeData::hasAttribute( const QString& name )
+{
+ return attr.contains( name );
+}
+
+void KoXmlNodeData::setAttributeNS( const QString& nsURI,
+const QString& name, const QString& value )
+{
+ QString prefix;
+ QString localName = name;
+ int i = name.find( ':' );
+ if( i != -1 )
+ {
+ localName = name.mid( i + 1 );
+ prefix = name.left( i );
+ }
+
+ if( prefix.isNull() ) return;
+
+ DQString key( nsURI, localName );
+ attrNS[ key ] = value;
+}
+
+QString KoXmlNodeData::attributeNS( const QString& nsURI, const QString& name )
+{
+ DQString key( nsURI, name );
+ return attrNS[ key ];
+}
+
+bool KoXmlNodeData::hasAttributeNS( const QString& nsURI, const QString& name )
+{
+ DQString key( nsURI, name );
+ return attrNS.contains( key );
+}
+
+QString KoXmlNodeData::data() const
+{
+ return textData;
+}
+
+void KoXmlNodeData::setData( const QString& d )
+{
+ textData = d;
+}
+
+bool KoXmlNodeData::setContent( QXmlInputSource* source,
+QXmlReader* reader, QString* errorMsg, int* errorLine, int* errorColumn )
+{
+ if( nodeType != KoXmlNode::DocumentNode )
+ return false;
+
+ clear();
+ nodeType = KoXmlNode::DocumentNode;
+
+ // sanity checks
+ if( !source ) return false;
+ if( !reader ) return false;
+
+ // copy the reader for later on-demand loading
+ // FIXME this is a workaround because no copy is possible with QXmlReader
+ char* features[] =
+ {
+ "http://xml.org/sax/features/namespaces",
+ "http://xml.org/sax/features/namespace-prefixes",
+ "http://trolltech.com/xml/features/report-whitespace-only-CharData",
+ "http://trolltech.com/xml/features/report-start-end-entity"
+ };
+ xmlReader = new QXmlSimpleReader;
+ for( int fi=0; fi<4; fi++ )
+ xmlReader->setFeature( features[fi], reader->feature( features[fi] ) );
+
+ bool processNamespace =
+ reader->feature( "http://xml.org/sax/features/namespaces" ) &&
+ !reader->feature( "http://xml.org/sax/features/namespace-prefixes" );
+
+ KoXmlHandler handler( this, processNamespace );
+ reader->setContentHandler( &handler );
+ reader->setErrorHandler( &handler );
+ reader->setLexicalHandler( &handler );
+ reader->setDeclHandler( &handler );
+ reader->setDTDHandler( &handler );
+
+ if( !fastLoading )
+ handler.setMaxDepth( 4 );
+
+ if( !reader->parse( source ) )
+ {
+ // parsing error has occurred
+ if( errorMsg ) *errorMsg = handler.errorMsg;
+ if( errorLine ) *errorLine = handler.errorLine;
+ if( errorColumn ) *errorColumn = handler.errorColumn;
+ return false;
+ }
+
+ return true;
+}
+
+void KoXmlNodeData::loadChildren( int depth )
+{
+ // for more than 1 level, force reloading anyway
+ if( ( depth== 1 ) && loaded ) return;
+
+ KoXmlNodeData* doc = ownerDocument();
+ if( !doc ) return;
+
+ // fast loading? then this on-demand loading makes no sense
+ if( doc->fastLoading ) return;
+
+ unloadChildren();
+
+ bool nsProcess =
+ doc->xmlReader->feature( "http://xml.org/sax/features/namespaces" ) &&
+ !doc->xmlReader->feature( "http://xml.org/sax/features/namespace-prefixes" );
+
+
+ // XML snippet for the children, including this element
+ QString snippet = doc->buffer.mid( startPos, endPos-startPos+1 );
+
+ // now parse all subnodes
+ KoXmlHandler handler( this, nsProcess );
+ handler.setMaxDepth( depth );
+ handler.setInitialOffset( startPos );
+ doc->xmlReader->setContentHandler( &handler );
+ doc->xmlReader->setErrorHandler( &handler );
+ doc->xmlReader->setLexicalHandler( &handler );
+ doc->xmlReader->setDeclHandler( &handler );
+ doc->xmlReader->setDTDHandler( &handler );
+
+ QXmlInputSource source;
+ source.setData( snippet );
+ if( !doc->xmlReader->parse( source ) )
+ {
+ // parsing error has occurred, which should not happen
+ // nothing we can do except...
+ loaded = false;
+ qWarning( "On-demand loading triggers parse error!" );
+ }
+ else
+ loaded = true;
+}
+
+void KoXmlNodeData::unloadChildren()
+{
+ if( !loaded ) return;
+
+ if( first )
+ for( KoXmlNodeData* node = first; node ; )
+ {
+ KoXmlNodeData* next = node->next;
+ node->unloadChildren();
+ node->unref();
+ node = next;
+ }
+
+ loaded = false;
+ first = last = 0;
+}
+
+
+// ==================================================================
+//
+// KoXmlHandler
+//
+// ==================================================================
+
+KoXmlHandler::KoXmlHandler( KoXmlNodeData* n, bool ns ):
+QXmlDefaultHandler()
+{
+ processNamespace = ns;
+
+ rootNode = n;
+ currentNode = n;
+ cdata = false;
+ entityName = QString::null;
+
+ errorMsg = QString::null;
+ errorLine = 0;
+ errorColumn = 0;
+
+ parseOffset = 0;
+ elementDepth = -1;
+ maxDepth = 999;
+
+ bufferStream.setSaveData( rootNode->nodeType == KoXmlNode::DocumentNode );
+}
+
+KoXmlHandler::~KoXmlHandler()
+{
+}
+
+bool KoXmlHandler::startDocument()
+{
+ // just for sanity
+ currentNode = rootNode;
+ cdata = false;
+ entityName = QString::null;
+ elementDepth = -1;
+
+ return true;
+}
+
+bool KoXmlHandler::endDocument()
+{
+ // just for sanity
+ if( rootNode->nodeType == KoXmlNode::DocumentNode )
+ if( currentNode!=rootNode )
+ return false;
+
+ if( rootNode->nodeType == KoXmlNode::DocumentNode )
+ rootNode->buffer = bufferStream.stringData();
+
+ return true;
+}
+
+bool KoXmlHandler::startDTD( const QString& name, const QString& publicId,
+const QString& systemId )
+{
+ Q_UNUSED( name );
+ Q_UNUSED( publicId );
+ Q_UNUSED( systemId );
+
+ // we skip DTD
+ return true;
+}
+
+bool KoXmlHandler::startElement( const QString& nsURI, const QString& localName,
+const QString& name, const QXmlAttributes& atts )
+{
+ Q_UNUSED( localName );
+
+ // sanity check
+ if( !currentNode )
+ return false;
+
+ // we are going one level deeper
+ elementDepth++;
+
+ QString nodePrefix, nodeLocalName, nodeTagName;
+ KoXmlNodeData* element = 0;
+
+ if( processNamespace )
+ {
+ // parse, using namespace
+ nodeTagName = name;
+ nodeLocalName = name;
+ nodePrefix = nsURI.isNull() ? QString::null : QString("");
+ int i = name.find( ':' );
+ if( i != -1 )
+ {
+ nodeTagName = name.mid( i + 1 );
+ nodeLocalName = nodeTagName;
+ nodePrefix = name.left( i );
+ }
+
+ if( elementDepth <= maxDepth )
+ {
+ // construct a new element
+ element = new KoXmlNodeData;
+ element->nodeType = KoXmlNode::ElementNode;
+ element->parent = currentNode;
+ element->namespaceURI = nsURI;
+ element->prefix = nodePrefix;
+ element->localName = nodeLocalName;
+ element->tagName = nodeTagName;
+
+ // Note: endPos will be later fixed in endElement
+ element->endPos = element->startPos = parseOffset + bufferStream.at();
+
+ // handle the attributes
+ for( int c=0; c<atts.length(); c++ )
+ {
+ QString prefix;
+ QString qName; // with prefix
+ QString name; // without prefix, i.e. local name
+
+ name = qName = atts.qName(c);
+ int i = qName.find( ':' );
+ if( i != -1 ) prefix = qName.left( i );
+ if( i != -1 ) name = qName.mid( i + 1 );
+ element->setAttributeNS( atts.uri(c), qName, atts.value(c) );
+ element->setAttribute( name, atts.value(c) );
+ }
+ }
+
+ // save in buffer for later on-demand loading
+ if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
+ {
+ bufferStream << "<";
+ if( !nodePrefix.isEmpty() )
+ bufferStream << nodePrefix << ":";
+ bufferStream << localName;
+ bufferStream << " xmlns";
+ if( !nodePrefix.isEmpty() )
+ bufferStream << ":" << nodePrefix;
+ bufferStream << "=\"";
+ bufferStream.appendEscape( nsURI );
+ bufferStream << "\"";
+ for( int c=0; c<atts.length(); c++ )
+ {
+ QString prefix;
+ QString name = atts.qName(c); // qName contains the prefix
+ int i = name.find( ':' );
+ if( i != -1 ) prefix = name.left( i );
+ if( i != -1 ) name = atts.qName(c).mid( i + 1 );
+ if( !atts.uri(c).isEmpty() )
+ bufferStream << " xmlns:" << prefix << "=\"" << atts.uri(c) << "\"";
+ bufferStream << " ";
+ if( !prefix.isEmpty() ) bufferStream << prefix << ":";
+ bufferStream << name << "=\"";
+ bufferStream.appendEscape( atts.value(c) );
+ bufferStream << "\"";
+ }
+ bufferStream << ">";
+ }
+ }
+ else
+ {
+ // parse, without using namespace
+ nodeTagName = name;
+
+ if( elementDepth <= maxDepth )
+ {
+ // construct a new element
+ element = new KoXmlNodeData;
+ element->nodeType = KoXmlNode::ElementNode;
+ element->parent = currentNode;
+ element->namespaceURI = QString::null;
+ element->prefix = QString::null;
+ element->localName = QString::null;
+ element->tagName = nodeTagName;
+
+ if( rootNode->nodeType == KoXmlNode::DocumentNode )
+ element->fastLoading = rootNode->fastLoading;
+
+ // Note: endPos will be later fixed in endElement
+ element->endPos = element->startPos = parseOffset + bufferStream.at();
+
+ // handle the attributes
+ for( int c=0; c<atts.length(); c++ )
+ element->setAttribute( atts.qName(c), atts.value(c) );
+ }
+
+ // save in buffer for later on-demand loading
+ if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
+ {
+ bufferStream << "<";
+ bufferStream << nodeTagName;
+ for( int c=0; c<atts.length(); c++ )
+ {
+ bufferStream << " " << atts.qName(c) << "=\"";
+ bufferStream.appendEscape( atts.value(c) );
+ bufferStream << "\"";
+ }
+ bufferStream << ">";
+ }
+
+ }
+
+ // if we do not parse a complete document, the first element is ignored
+ // this feature is used in on-demand loading
+ // e.g. "<ul><li><b>bold items</b></li></ul>", if root node points to
+ // <ul> tag, then the first <ul> is skipped so that next <li> element
+ // becomes the child of it.
+ if( elementDepth == 0 )
+ if( rootNode->nodeType != KoXmlNode::DocumentNode )
+ {
+ delete element;
+ return true;
+ }
+
+ if( element )
+ {
+ // add as the child and traverse to it
+ currentNode->loaded = true;
+ currentNode->appendChild( element );
+ currentNode = element;
+ }
+ else
+ currentNode->loaded = false;
+
+ return true;
+}
+
+bool KoXmlHandler::endElement( const QString& nsURI, const QString& localName,
+const QString& qName )
+{
+ Q_UNUSED( nsURI );
+ Q_UNUSED( localName );
+
+ // sanity check
+ if( !currentNode ) return false;
+ if( !currentNode->parent ) return false;
+
+ // see comments in startElement about first element and on-demand loading
+ if( rootNode->nodeType == KoXmlNode::DocumentNode )
+ if( currentNode == rootNode )
+ return false;
+
+ // buffer for on-demand loading
+ if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
+ bufferStream << "</" << qName << ">";
+
+ // fix up the pointer
+ currentNode->endPos = parseOffset + bufferStream.at() - 1;
+
+ // go up one level
+ if( elementDepth <= maxDepth )
+ currentNode = currentNode->parent;
+
+ // we are going up one level
+ elementDepth--;
+
+ return true;
+}
+
+bool KoXmlHandler::characters( const QString& str )
+{
+ // sanity check
+ if( rootNode->nodeType == KoXmlNode::DocumentNode )
+ if( currentNode == rootNode )
+ return false;
+
+ // are we inside entity ?
+ if( !entityName.isEmpty() )
+ {
+ // we do not handle entity but need to keep track of it
+ // because we want to skip it alltogether
+ return true;
+ }
+
+ if( cdata )
+ {
+ // are we inside CDATA section ?
+ if( elementDepth <= maxDepth )
+ {
+ KoXmlNodeData* cdata = new KoXmlNodeData;
+ cdata->nodeType = KoXmlNode::CDATASectionNode;
+ cdata->parent = currentNode;
+ cdata->setData( str );
+ currentNode->appendChild( cdata );
+ }
+
+ if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
+ bufferStream << "<![CDATA[" << str << "]]>"; // no escape for CDATA
+ }
+ else
+ {
+ // this must be normal text node
+ if( elementDepth <= maxDepth )
+ {
+ KoXmlNodeData* text = new KoXmlNodeData;
+ text->nodeType = KoXmlNode::TextNode;
+ text->parent = currentNode;
+ text->setData( str );
+ currentNode->appendChild( text );
+ }
+
+ // save it for later use
+ if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
+ bufferStream.appendEscape( str );
+ }
+
+ return true;
+}
+
+bool KoXmlHandler::processingInstruction( const QString& target,
+const QString& data )
+{
+ // sanity check
+ if( !currentNode )
+ return false;
+
+ KoXmlNodeData* instruction = new KoXmlNodeData;
+ instruction->nodeType = KoXmlNode::ProcessingInstructionNode;
+ instruction->parent = currentNode;
+ instruction->tagName = target;
+ instruction->setData( data );
+
+ currentNode->appendChild( instruction );
+
+ return true;
+}
+
+bool KoXmlHandler::skippedEntity( const QString& name )
+{
+ Q_UNUSED( name );
+
+ // we skip entity
+ return true;
+}
+
+bool KoXmlHandler::startCDATA()
+{
+ cdata = true;
+ return true;
+}
+
+bool KoXmlHandler::endCDATA()
+{
+ cdata = false;
+ return true;
+}
+
+bool KoXmlHandler::startEntity( const QString& name )
+{
+ entityName = name;
+ return true;
+}
+
+bool KoXmlHandler::endEntity( const QString& name )
+{
+ Q_UNUSED( name );
+ entityName = QString::null;
+ return true;
+}
+
+bool KoXmlHandler::comment( const QString& comment )
+{
+ Q_UNUSED( comment );
+
+ // we skip comment
+ return true;
+}
+
+bool KoXmlHandler::unparsedEntityDecl( const QString &name,
+const QString &publicId, const QString &systemId, const QString &notationName )
+{
+ Q_UNUSED( name );
+ Q_UNUSED( publicId );
+ Q_UNUSED( systemId );
+ Q_UNUSED( notationName );
+
+ // we skip entity
+ return true;
+}
+
+bool KoXmlHandler::externalEntityDecl( const QString &name,
+const QString &publicId, const QString &systemId )
+{
+ Q_UNUSED( name );
+ Q_UNUSED( publicId );
+ Q_UNUSED( systemId );
+
+ // we skip entity
+ return true;
+}
+
+bool KoXmlHandler::notationDecl( const QString & name,
+const QString & publicId, const QString & systemId )
+{
+ Q_UNUSED( name );
+ Q_UNUSED( publicId );
+ Q_UNUSED( systemId );
+
+ // we skip notation node
+ return true;
+}
+
+bool KoXmlHandler::fatalError( const QXmlParseException& exception )
+{
+ errorMsg = exception.message();
+ errorLine = exception.lineNumber();
+ errorColumn = exception.columnNumber();
+ return QXmlDefaultHandler::fatalError( exception );
+}
+
+// ==================================================================
+//
+// KoXmlNode
+//
+// ==================================================================
+
+// Creates a null node
+KoXmlNode::KoXmlNode()
+{
+ d = new KoXmlNodeData;
+}
+
+// Destroys this node
+KoXmlNode::~KoXmlNode()
+{
+ if( d ) d->unref();
+}
+
+// Creates a copy of another node
+KoXmlNode::KoXmlNode( const KoXmlNode& node )
+{
+ d = node.d;
+ d->ref();
+}
+
+// Creates a node for specific implementation
+KoXmlNode::KoXmlNode( KoXmlNodeData* data )
+{
+ d = data;
+ data->ref();
+}
+
+// Creates a shallow copy of another node
+KoXmlNode& KoXmlNode::operator=( const KoXmlNode& node )
+{
+ d->unref();
+ d = node.d;
+ d->ref();
+ return *this;
+}
+
+// Note: two null nodes are always equal
+bool KoXmlNode::operator==( const KoXmlNode& node ) const
+{
+ if( isNull() && node.isNull() ) return true;
+ return( d==node.d );
+}
+
+// Note: two null nodes are always equal
+bool KoXmlNode::operator!=( const KoXmlNode& node ) const
+{
+ if( isNull() && !node.isNull() ) return true;
+ if( !isNull() && node.isNull() ) return true;
+ if( isNull() && node.isNull() ) return false;
+ return( d!=node.d );
+}
+
+KoXmlNode::NodeType KoXmlNode::nodeType() const
+{
+ return d->nodeType;
+}
+
+bool KoXmlNode::isElement() const
+{
+ return d->nodeType == ElementNode;
+}
+
+bool KoXmlNode::isText() const
+{
+ return (d->nodeType == TextNode) || isCDATASection();
+}
+
+bool KoXmlNode::isCDATASection() const
+{
+ return d->nodeType == CDATASectionNode;
+}
+
+bool KoXmlNode::isDocument() const
+{
+ return d->nodeType == DocumentNode;
+}
+
+bool KoXmlNode::isNull() const
+{
+ return d->nodeType == NullNode;
+}
+
+void KoXmlNode::clear()
+{
+ d->unref();
+ d = new KoXmlNodeData;
+}
+
+QString KoXmlNode::nodeName() const
+{
+ return d->nodeName();
+}
+
+QString KoXmlNode::prefix() const
+{
+ return isElement() ? d->prefix : QString::null;
+}
+
+QString KoXmlNode::namespaceURI() const
+{
+ return isElement() ? d->namespaceURI : QString::null;
+}
+
+QString KoXmlNode::localName() const
+{
+ return isElement() ? d->localName : QString::null;
+}
+
+KoXmlDocument KoXmlNode::ownerDocument() const
+{
+ KoXmlNodeData* node = d;
+ while( node->parent ) node = node->parent;
+
+ if( node->nodeType != DocumentNode ) return KoXmlDocument();
+ return KoXmlDocument( node );
+}
+
+KoXmlNode KoXmlNode::parentNode() const
+{
+ return d->parent ? KoXmlNode( d->parent ) : KoXmlNode();
+}
+
+bool KoXmlNode::hasChildNodes() const
+{
+ d->loadChildren();
+ return d->first!=0 ;
+}
+
+KoXmlNode KoXmlNode::firstChild() const
+{
+ if( !d->fastLoading )
+ d->loadChildren();
+ return d->first ? KoXmlNode( d->first ) : KoXmlNode();
+}
+
+KoXmlNode KoXmlNode::lastChild() const
+{
+ if( !d->fastLoading )
+ d->loadChildren();
+ return d->last ? KoXmlNode( d->last ) : KoXmlNode();
+}
+
+KoXmlNode KoXmlNode::nextSibling() const
+{
+ return d->next ? KoXmlNode( d->next ) : KoXmlNode();
+}
+
+KoXmlNode KoXmlNode::previousSibling() const
+{
+ return d->prev ? KoXmlNode( d->prev ) : KoXmlNode();
+}
+
+KoXmlNode KoXmlNode::namedItem( const QString& name ) const
+{
+ if( !d->fastLoading )
+ d->loadChildren();
+
+ KoXmlNodeData* node = d->first;
+ while ( node )
+ {
+ if( node->nodeName() == name )
+ return KoXmlNode( node );
+ node = node->next;
+ }
+
+ // not found
+ return KoXmlNode();
+}
+
+KoXmlNode KoXmlNode::namedItemNS( const QString& nsURI, const QString& name ) const
+{
+ if( !d->fastLoading )
+ d->loadChildren();
+
+ KoXmlNodeData* node = d->first;
+ while ( node )
+ {
+ if( !node->prefix.isNull() )
+ if( node->namespaceURI == nsURI )
+ if( node->localName == name )
+ return KoXmlNode( node );
+ node = node->next;
+ }
+
+ // not found
+ return KoXmlNode();
+}
+
+KoXmlElement KoXmlNode::toElement()
+{
+ return isElement() ? KoXmlElement( d ) : KoXmlElement();
+}
+
+KoXmlText KoXmlNode::toText()
+{
+ return isText() ? KoXmlText( d ) : KoXmlText();
+}
+
+KoXmlCDATASection KoXmlNode::toCDATASection()
+{
+ return isCDATASection() ? KoXmlCDATASection( (KoXmlNodeData*)d ) :
+ KoXmlCDATASection();
+}
+
+KoXmlDocument KoXmlNode::toDocument()
+{
+ return isDocument() ? KoXmlDocument( d ) : KoXmlDocument();
+}
+
+void KoXmlNode::load( int depth )
+{
+ d->loadChildren( depth );
+}
+
+void KoXmlNode::unload()
+{
+ d->unloadChildren();
+}
+
+// ==================================================================
+//
+// KoXmlElement
+//
+// ==================================================================
+
+// Creates an empty element
+KoXmlElement::KoXmlElement(): KoXmlNode()
+{
+ d->unref();
+ d = new KoXmlNodeData;
+}
+
+KoXmlElement::~KoXmlElement()
+{
+ d->unref();
+ d = 0;
+}
+
+// Creates a shallow copy of another element
+KoXmlElement::KoXmlElement( const KoXmlElement& element ): KoXmlNode()
+{
+ d->unref();
+ d = element.d;
+ d->ref();
+}
+
+KoXmlElement::KoXmlElement( KoXmlNodeData* data ): KoXmlNode()
+{
+ d->unref();
+ d = data;
+ d->ref();
+}
+
+// Copies another element
+KoXmlElement& KoXmlElement::operator=( const KoXmlElement& element )
+{
+ KoXmlNode::operator=( element );
+ return *this;
+}
+
+bool KoXmlElement::operator== ( const KoXmlElement& element ) const
+{
+ if( isNull() || element.isNull() ) return false;
+ return (d==element.d);
+}
+
+bool KoXmlElement::operator!= ( const KoXmlElement& element ) const
+{
+ if( isNull() && element.isNull() ) return false;
+ if( isNull() || element.isNull() ) return true;
+ return (d!=element.d);
+}
+
+QString KoXmlElement::tagName() const
+{
+ return isElement() ? ((KoXmlNodeData*)d)->tagName: QString::null;
+}
+
+QString KoXmlElement::text() const
+{
+ return d->text();
+}
+
+bool KoXmlElement::isElement() const
+{
+ return true;
+}
+
+QString KoXmlElement::attribute( const QString& name ) const
+{
+ return attribute( name, QString::null );
+}
+
+QString KoXmlElement::attribute( const QString& name,
+const QString& defaultValue ) const
+{
+ if( !isElement() )
+ return defaultValue;
+
+ if( !hasAttribute( name ) )
+ return defaultValue;
+
+ return ((KoXmlNodeData*)d)->attribute( name );
+}
+
+QString KoXmlElement::attributeNS( const QString& namespaceURI,
+const QString& localName, const QString& defaultValue ) const
+{
+ if( !isElement() )
+ return defaultValue;
+
+ if( !hasAttributeNS( namespaceURI,localName ) )
+ return defaultValue;
+
+ return ((KoXmlNodeData*)d)->attributeNS( namespaceURI,localName );
+}
+
+bool KoXmlElement::hasAttribute( const QString& name ) const
+{
+ return isElement() ? ((KoXmlNodeData*)d)->hasAttribute( name ) : false;
+}
+
+bool KoXmlElement::hasAttributeNS( const QString& namespaceURI,
+const QString& localName ) const
+{
+ return isElement() ? ((KoXmlNodeData*)d)->hasAttributeNS(
+ namespaceURI, localName ) : false;;
+}
+
+// ==================================================================
+//
+// KoXmlText
+//
+// ==================================================================
+
+KoXmlText::KoXmlText(): KoXmlNode()
+{
+ d->unref();
+ d = new KoXmlNodeData;
+ d->nodeType = TextNode;
+}
+
+KoXmlText::~KoXmlText()
+{
+ if( d ) d->unref();
+ d = 0;
+}
+
+KoXmlText::KoXmlText( const KoXmlText& text ): KoXmlNode()
+{
+ d->unref();
+ d = (KoXmlNodeData*) text.d;
+ d->ref();
+}
+
+KoXmlText::KoXmlText( KoXmlNodeData* data ): KoXmlNode()
+{
+ d->unref();
+ d = data;
+ d->ref();
+}
+
+bool KoXmlText::isText() const
+{
+ return true;
+}
+
+QString KoXmlText::data() const
+{
+ return ((KoXmlNodeData*)d)->data();
+}
+
+KoXmlText& KoXmlText::operator=( const KoXmlText& element )
+{
+ KoXmlNode::operator=( element );
+ return *this;
+}
+
+// ==================================================================
+//
+// KoXmlCDATASection
+//
+// ==================================================================
+
+KoXmlCDATASection::KoXmlCDATASection(): KoXmlText()
+{
+ d->unref();
+ d = new KoXmlNodeData;
+ d->nodeType = KoXmlNode::CDATASectionNode;
+}
+
+KoXmlCDATASection::~KoXmlCDATASection()
+{
+ d->unref();
+ d = 0;
+}
+
+KoXmlCDATASection::KoXmlCDATASection( const KoXmlCDATASection& cdata ):
+KoXmlText()
+{
+ d->unref();
+ d = (KoXmlNodeData*) cdata.d;
+ d->ref();
+}
+
+KoXmlCDATASection::KoXmlCDATASection( KoXmlNodeData* cdata ):
+KoXmlText()
+{
+ d->unref();
+ d = cdata;
+ d->ref();
+}
+
+bool KoXmlCDATASection::isCDATASection() const
+{
+ return true;
+}
+
+KoXmlCDATASection& KoXmlCDATASection::operator=( const KoXmlCDATASection& cdata )
+{
+ KoXmlNode::operator=( cdata );
+ return *this;
+}
+
+// ==================================================================
+//
+// KoXmlDocument
+//
+// ==================================================================
+
+KoXmlDocument::KoXmlDocument(): KoXmlNode()
+{
+ d->unref();
+ d = new KoXmlNodeData;
+ d->nodeType = KoXmlNode::DocumentNode;
+}
+
+KoXmlDocument::~KoXmlDocument()
+{
+ d->unref();
+ d = 0;
+}
+
+KoXmlDocument::KoXmlDocument( KoXmlNodeData* data ): KoXmlNode()
+{
+ d->unref();
+ d = data;
+ d->ref();
+}
+
+// Creates a copy of another document
+KoXmlDocument::KoXmlDocument( const KoXmlDocument& doc ): KoXmlNode()
+{
+ d->unref();
+ d = doc.d;
+ d->ref();
+}
+
+// Creates a shallow copy of another document
+KoXmlDocument& KoXmlDocument::operator=( const KoXmlDocument& doc )
+{
+ KoXmlNode::operator=( doc );
+ return *this;
+}
+
+// Checks if this document and doc are equals
+bool KoXmlDocument::operator==( const KoXmlDocument& doc ) const
+{
+ return( d==doc.d );
+}
+
+// Checks if this document and doc are not equals
+bool KoXmlDocument::operator!=( const KoXmlDocument& doc ) const
+{
+ return( d!=doc.d );
+}
+
+bool KoXmlDocument::isDocument() const
+{
+ return true;
+}
+
+KoXmlElement KoXmlDocument::documentElement() const
+{
+ for( KoXmlNodeData* node=d->first; node; )
+ if( node->nodeType==KoXmlNode::ElementNode )
+ return KoXmlElement( node );
+ else node = node->next;
+
+ return KoXmlElement();
+}
+
+void KoXmlDocument::setFastLoading( bool f )
+{
+ d->fastLoading = f;
+}
+
+bool KoXmlDocument::fastLoading() const
+{
+ return d->fastLoading;
+}
+
+bool KoXmlDocument::setContent( QXmlInputSource *source, QXmlReader *reader,
+ QString* errorMsg, int* errorLine, int* errorColumn )
+{
+ if( d->nodeType != KoXmlNode::DocumentNode )
+ return false;
+
+ return d->setContent( source, reader, errorMsg, errorLine, errorColumn );
+}
+
+// no namespace processing
+bool KoXmlDocument::setContent( QIODevice* device, QString* errorMsg,
+int* errorLine, int* errorColumn )
+{
+ return setContent( device, false, errorMsg, errorLine, errorColumn );
+}
+
+bool KoXmlDocument::setContent( QIODevice* device, bool namespaceProcessing,
+QString* errorMsg, int* errorLine, int* errorColumn )
+{
+ if( d->nodeType != KoXmlNode::DocumentNode )
+ return false;
+
+ QXmlSimpleReader reader;
+ reader.setFeature( "http://xml.org/sax/features/namespaces", namespaceProcessing );
+ reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", !namespaceProcessing );
+ reader.setFeature( "http://trolltech.com/xml/features/report-whitespace-only-CharData", false );
+
+ // FIXME this hack is apparently private
+ //reader.setUndefEntityInAttrHack(true);
+
+ QXmlInputSource source( device );
+ return d->setContent( &source, &reader, errorMsg, errorLine, errorColumn );
+}
+
+#endif
+
+KoXmlElement KoXml::namedItemNS( const KoXmlNode& node, const char* nsURI,
+const char* localName )
+{
+#ifdef KOXML_USE_QDOM
+ // David's solution for namedItemNS, only for QDom stuff
+ KoXmlNode n = node.firstChild();
+ for ( ; !n.isNull(); n = n.nextSibling() ) {
+ if ( n.isElement() && n.localName() == localName &&
+ n.namespaceURI() == nsURI )
+ return n.toElement();
+ }
+ return KoXmlElement();
+#else
+ return node.namedItemNS( nsURI, localName).toElement();
+#endif
+}
+
+void KoXml::load( KoXmlNode& node, int depth )
+{
+#ifdef KOXML_USE_QDOM
+ // do nothing, QDom has no on-demand loading
+ Q_UNUSED( node );
+ Q_UNUSED( depth );
+#else
+ node.load( depth );
+#endif
+}
+
+
+void KoXml::unload( KoXmlNode& node )
+{
+#ifdef KOXML_USE_QDOM
+ // do nothing, QDom has no on-demand unloading
+ Q_UNUSED( node );
+#else
+ node.unload();
+#endif
+}