diff options
author | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
---|---|---|
committer | tpearson <tpearson@283d02a7-25f6-0310-bc7c-ecb5cbfe19da> | 2010-01-20 01:29:50 +0000 |
commit | 8362bf63dea22bbf6736609b0f49c152f975eb63 (patch) | |
tree | 0eea3928e39e50fae91d4e68b21b1e6cbae25604 /lib/kofficecore/KoXmlReader.cpp | |
download | koffice-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.cpp | 1568 |
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( "<"); pos += 4; } else + if( str[c]=='>'){ data.append( ">"); pos+= 4; } else + if( str[c]=='"'){ data.append( """); pos += 6; } else + if( str[c]=='&'){ data.append( "&"); pos += 5; } else + { data.append( str[c] ); pos++; } + } + else + { + pos += len; + for( unsigned c=0; c<len; c++ ) + if( str[c]=='<' ) pos += 3; else // "<" + if( str[c]=='>') pos+= 3; else // ">" + if( str[c]=='"') pos += 5; else // """ + if( str[c]=='&') pos += 4; // "&" + } + + 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 ¬ationName ) ; + + // 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 ¬ationName ) +{ + 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 +} |