/* This file is part of the KDE project Copyright (C) 2005 Ariya Hidayat 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 TQDom. * All TQDom-compatible methods should behave the same. All TQDom-compatible functions should return the same result. In case of doubt, run koxmlreadertest.cpp but uncomment KOXML_USE_TQDOM in koxmlreader.h so that the tests are performed with standard TQDom. Some differences compared to TQDom: - 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 TQDomComment) is not implemented as comments are simply ignored. - DTD, entity and entity reference are not handled. Thus, the associated nodes (like TQDomDocumentType, TQDomEntity, TQDomEntityReference) 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_TQDOM #include #include #include #include // double TQString, used for hashing against namespace and qualified name pair class DTQString { public: DTQString() { } DTQString( const TQString& s1, const TQString& s2 ){ str1 = s1; str2 = s2; } DTQString( const DTQString& b ) { str1 = b.str1; str2 = b.str2; } DTQString& operator=( const DTQString& b ){ str1 = b.str1; str2 = b.str2; return *this; } bool operator==( const DTQString& b ) const { return (str1==b.str1) && (str2==b.str2); } bool operator!=( const DTQString& b ) const { return (str1!=b.str1) || (str2!=b.str2); } bool operator<( const DTQString& b ) const { return ( str1 < b.str1 ) ? true : ( str1==b.str1 ) ? str2'){ 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') 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; TQString tagName; TQString namespaceURI; TQString prefix; TQString 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"; } TQString nodeName() const; // for tree and linked-list KoXmlNodeData* parent; KoXmlNodeData* prev; KoXmlNodeData* next; KoXmlNodeData* first; KoXmlNodeData* last; TQString text(); // node manipulation void appendChild( KoXmlNodeData* child ); virtual void clear(); KoXmlNodeData* ownerDocument(); // attributes void setAttribute( const TQString& name, const TQString& value ); TQString attribute( const TQString& name ); bool hasAttribute( const TQString& name ); void setAttributeNS( const TQString& nsURI, const TQString& name, const TQString& value ); TQString attributeNS( const TQString& nsURI, const TQString& name ); bool hasAttributeNS( const TQString& nsURI, const TQString& name ); // for text and CDATA TQString data() const; void setData( const TQString& data ); // for document node TQXmlSimpleReader* xmlReader; TQString buffer; bool setContent( TQXmlInputSource* source, TQXmlReader* reader, TQString* 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: TQMap attr; TQMap attrNS; TQString textData; friend class KoXmlHandler; }; class KoXmlHandler : public TQXmlDefaultHandler { 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 TQString& nsURI, const TQString& localName, const TQString& qName, const TQXmlAttributes& atts ); bool endElement( const TQString& nsURI, const TQString& localName, const TQString& qName ); bool characters( const TQString& ch ); bool processingInstruction( const TQString& target, const TQString& data ); bool skippedEntity( const TQString& name ); // lexical handler bool startCDATA(); bool endCDATA(); bool startEntity( const TQString & ); bool endEntity( const TQString & ); bool startDTD( const TQString& name, const TQString& publicId, const TQString& systemId ); bool comment( const TQString& ch ); // decl handler bool externalEntityDecl( const TQString &name, const TQString &publicId, const TQString &systemId ) ; // DTD handler bool notationDecl( const TQString & name, const TQString & publicId, const TQString & systemId ); bool unparsedEntityDecl( const TQString &name, const TQString &publicId, const TQString &systemId, const TQString ¬ationName ) ; // error handler bool fatalError( const TQXmlParseException& exception ); TQString errorMsg; int errorLine; int errorColumn; private: bool processNamespace; KoXmlNodeData* rootNode; KoXmlNodeData* currentNode; TQString entityName; bool cdata; int parseOffset; KoXmlStream bufferStream; int elementDepth; int maxDepth; }; // ================================================================== // // KoXmlNodeData // // ================================================================== KoXmlNodeData::KoXmlNodeData() { nodeType = KoXmlNode::NullNode; tagName = TQString(); prefix = TQString(); localName = TQString(); namespaceURI = TQString(); textData = TQString(); 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 = TQString(); prefix = TQString(); namespaceURI = TQString(); textData = TQString(); attr.clear(); attrNS.clear(); parent = 0; prev = next = 0; first = last = 0; delete xmlReader; xmlReader = 0; buffer = TQString(); } TQString KoXmlNodeData::text() { TQString 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; } TQString KoXmlNodeData::nodeName() const { TQString n; switch( nodeType ) { case KoXmlNode::ElementNode: n = tagName; if (!prefix.isEmpty()) n.prepend(":").prepend(prefix); break; case KoXmlNode::TextNode: return TQString("#text"); case KoXmlNode::CDATASectionNode: return TQString("#cdata-section"); case KoXmlNode::DocumentNode: return TQString("#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 TQString& name, const TQString& value ) { attr[ name ] = value; } TQString KoXmlNodeData::attribute( const TQString& name ) { return attr[ name ]; } bool KoXmlNodeData::hasAttribute( const TQString& name ) { return attr.contains( name ); } void KoXmlNodeData::setAttributeNS( const TQString& nsURI, const TQString& name, const TQString& value ) { TQString prefix; TQString localName = name; int i = name.find( ':' ); if( i != -1 ) { localName = name.mid( i + 1 ); prefix = name.left( i ); } if( prefix.isNull() ) return; DTQString key( nsURI, localName ); attrNS[ key ] = value; } TQString KoXmlNodeData::attributeNS( const TQString& nsURI, const TQString& name ) { DTQString key( nsURI, name ); return attrNS[ key ]; } bool KoXmlNodeData::hasAttributeNS( const TQString& nsURI, const TQString& name ) { DTQString key( nsURI, name ); return attrNS.contains( key ); } TQString KoXmlNodeData::data() const { return textData; } void KoXmlNodeData::setData( const TQString& d ) { textData = d; } bool KoXmlNodeData::setContent( TQXmlInputSource* source, TQXmlReader* reader, TQString* 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 TQXmlReader 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 TQXmlSimpleReader; 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 TQString 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 ); TQXmlInputSource source; source.setData( snippet ); if( !doc->xmlReader->parse( source ) ) { // parsing error has occurred, which should not happen // nothing we can do except... loaded = false; tqWarning( "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 ): TQXmlDefaultHandler() { processNamespace = ns; rootNode = n; currentNode = n; cdata = false; entityName = TQString(); errorMsg = TQString(); 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 = TQString(); 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 TQString& name, const TQString& publicId, const TQString& systemId ) { Q_UNUSED( name ); Q_UNUSED( publicId ); Q_UNUSED( systemId ); // we skip DTD return true; } bool KoXmlHandler::startElement( const TQString& nsURI, const TQString& localName, const TQString& name, const TQXmlAttributes& atts ) { Q_UNUSED( localName ); // sanity check if( !currentNode ) return false; // we are going one level deeper elementDepth++; TQString nodePrefix, nodeLocalName, nodeTagName; KoXmlNodeData* element = 0; if( processNamespace ) { // parse, using namespace nodeTagName = name; nodeLocalName = name; nodePrefix = nsURI.isNull() ? TQString() : TQString(""); 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; csetAttributeNS( 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"; } } 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 = TQString(); element->prefix = TQString(); element->localName = TQString(); 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; csetAttribute( 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"; } } // if we do not parse a complete document, the first element is ignored // this feature is used in on-demand loading // e.g. "
  • bold items
", if root node points to //
    tag, then the first
      is skipped so that next
    • 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 TQString& nsURI, const TQString& localName, const TQString& 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 << ""; // 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 TQString& 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 << ""; // 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 TQString& target, const TQString& 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 TQString& 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 TQString& name ) { entityName = name; return true; } bool KoXmlHandler::endEntity( const TQString& name ) { Q_UNUSED( name ); entityName = TQString(); return true; } bool KoXmlHandler::comment( const TQString& comment ) { Q_UNUSED( comment ); // we skip comment return true; } bool KoXmlHandler::unparsedEntityDecl( const TQString &name, const TQString &publicId, const TQString &systemId, const TQString ¬ationName ) { Q_UNUSED( name ); Q_UNUSED( publicId ); Q_UNUSED( systemId ); Q_UNUSED( notationName ); // we skip entity return true; } bool KoXmlHandler::externalEntityDecl( const TQString &name, const TQString &publicId, const TQString &systemId ) { Q_UNUSED( name ); Q_UNUSED( publicId ); Q_UNUSED( systemId ); // we skip entity return true; } bool KoXmlHandler::notationDecl( const TQString & name, const TQString & publicId, const TQString & systemId ) { Q_UNUSED( name ); Q_UNUSED( publicId ); Q_UNUSED( systemId ); // we skip notation node return true; } bool KoXmlHandler::fatalError( const TQXmlParseException& exception ) { errorMsg = exception.message(); errorLine = exception.lineNumber(); errorColumn = exception.columnNumber(); return TQXmlDefaultHandler::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; } TQString KoXmlNode::nodeName() const { return d->nodeName(); } TQString KoXmlNode::prefix() const { return isElement() ? d->prefix : TQString(); } TQString KoXmlNode::namespaceURI() const { return isElement() ? d->namespaceURI : TQString(); } TQString KoXmlNode::localName() const { return isElement() ? d->localName : TQString(); } 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 TQString& 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 TQString& nsURI, const TQString& 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); } TQString KoXmlElement::tagName() const { return isElement() ? ((KoXmlNodeData*)d)->tagName: TQString(); } TQString KoXmlElement::text() const { return d->text(); } bool KoXmlElement::isElement() const { return true; } TQString KoXmlElement::attribute( const TQString& name ) const { return attribute( name, TQString() ); } TQString KoXmlElement::attribute( const TQString& name, const TQString& defaultValue ) const { if( !isElement() ) return defaultValue; if( !hasAttribute( name ) ) return defaultValue; return ((KoXmlNodeData*)d)->attribute( name ); } TQString KoXmlElement::attributeNS( const TQString& namespaceURI, const TQString& localName, const TQString& defaultValue ) const { if( !isElement() ) return defaultValue; if( !hasAttributeNS( namespaceURI,localName ) ) return defaultValue; return ((KoXmlNodeData*)d)->attributeNS( namespaceURI,localName ); } bool KoXmlElement::hasAttribute( const TQString& name ) const { return isElement() ? ((KoXmlNodeData*)d)->hasAttribute( name ) : false; } bool KoXmlElement::hasAttributeNS( const TQString& namespaceURI, const TQString& 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; } TQString 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( TQXmlInputSource *source, TQXmlReader *reader, TQString* 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( TQIODevice* device, TQString* errorMsg, int* errorLine, int* errorColumn ) { return setContent( device, false, errorMsg, errorLine, errorColumn ); } bool KoXmlDocument::setContent( TQIODevice* device, bool namespaceProcessing, TQString* errorMsg, int* errorLine, int* errorColumn ) { if( d->nodeType != KoXmlNode::DocumentNode ) return false; TQXmlSimpleReader 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); TQXmlInputSource 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_TQDOM // David's solution for namedItemNS, only for TQDom 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_TQDOM // do nothing, TQDom has no on-demand loading Q_UNUSED( node ); Q_UNUSED( depth ); #else node.load( depth ); #endif } void KoXml::unload( KoXmlNode& node ) { #ifdef KOXML_USE_TQDOM // do nothing, TQDom has no on-demand unloading Q_UNUSED( node ); #else node.unload(); #endif }