lib

KoXmlReader.cpp

00001 /* This file is part of the KDE project
00002    Copyright (C) 2005 Ariya Hidayat <ariya@kde.org>
00003 
00004    This library is free software; you can redistribute it and/or
00005    modify it under the terms of the GNU Library General Public
00006    License as published by the Free Software Foundation; either
00007    version 2 of the License, or (at your option) any later version.
00008 
00009    This library is distributed in the hope that it will be useful,
00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012    Library General Public License for more details.
00013 
00014    You should have received a copy of the GNU Library General Public License
00015    along with this library; see the file COPYING.LIB.  If not, write to
00016    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  * Boston, MA 02110-1301, USA.
00018 */
00019 
00020 /*
00021   This is a memory-efficient DOM implementation for KOffice. See the API 
00022   documentation for details.
00023 
00024   IMPORTANT !
00025 
00026   * When you change this stuff, make sure it DOES NOT BREAK the test suite.
00027     Build tests/koxmlreadertest.cpp and verify it. Many sleepless nights 
00028     have been sacrificed for this piece of code, do not let those precious 
00029     hours wasted!
00030 
00031   * Run testdom.cpp WITH Valgrind's memcheck tool and make sure NO illegal 
00032     memory read/write and any type of leak occurs. If you are not familiar 
00033     with Valgrind then RTFM first and come back again later on.
00034 
00035   * The public API shall remain as compatible as QDom. 
00036 
00037   * All QDom-compatible methods should behave the same. All QDom-compatible
00038     functions should return the same result. In case of doubt, run 
00039     koxmlreadertest.cpp but uncomment KOXML_USE_QDOM in koxmlreader.h 
00040     so that the tests are performed with standard QDom.
00041 
00042   Some differences compared to QDom:
00043 
00044   - DOM tree in KoXmlDocument is read-only, you can not modify it. This is
00045     sufficient for KOffice since the tree is only accessed when loading 
00046     a document to the application. For saving the document to XML file,
00047     use KoXmlWriter.
00048 
00049   - Comment node (like QDomComment) is not implemented as comments are 
00050     simply ignored.
00051 
00052   - DTD, entity and entity reference are not handled. Thus, the associated
00053     nodes (like QDomDocumentType, QDomEntity, QDomEntityReference) are also 
00054     not implemented.
00055 
00056   - Attribute mapping node is not implemented. But of course, functions to 
00057     query attributes of an element are available.
00058     
00059  
00060  */
00061 
00062 #include "KoXmlReader.h"
00063 
00064 #ifndef KOXML_USE_QDOM
00065 
00066 #include <qxml.h>
00067 #include <qdom.h>
00068 
00069 #include <qmap.h>
00070 #include <qcstring.h>
00071 
00072 // double QString, used for hashing against namespace and qualified name pair
00073 class DQString
00074 {
00075 public:
00076   DQString() { }
00077   DQString( const QString& s1, const QString& s2 ){ str1 = s1; str2 = s2; }
00078   DQString( const DQString& b ) { str1 = b.str1; str2 = b.str2; }
00079   DQString& operator=( const DQString& b ){ str1 = b.str1; str2 = b.str2; return *this; }
00080   bool operator==( const DQString& b ) const { return (str1==b.str1) && (str2==b.str2); }
00081   bool operator!=( const DQString& b ) const { return (str1!=b.str1) || (str2!=b.str2); }
00082   bool operator<( const DQString& b ) const 
00083   { return ( str1 < b.str1 ) ? true : ( str1==b.str1 ) ? str2<b.str2 : false; }
00084   QString s1() const { return str1; }
00085   QString s2() const { return str2; }
00086 private:
00087   QString str1, str2;  
00088 };
00089 
00090 // just for completeness, this is the self-test for DQString above
00091 #if 0
00092   DQString b1;
00093   DQString b2;
00094   CHECK( b1==b2, true );
00095   CHECK( b1!=b2, false );
00096 
00097   b1 = DQString( "sweet","princess" );
00098   b2 = DQString( "sweet","princess" );
00099   CHECK( b1==b2, true );
00100   CHECK( b1!=b2, false );
00101 
00102   b1 = DQString( "sweet","princess" );
00103   b2 = DQString( "bad","prince" );
00104   CHECK( b1==b2, false );
00105   CHECK( b1!=b2, true );
00106 #endif
00107 
00108 class KoXmlStream
00109 {
00110 public:
00111   KoXmlStream(){ saveData = true; data.reserve(1024); pos = 0; }
00112   QString stringData() const { return data; }
00113   void setSaveData( bool s ){ saveData = s; }
00114   int at() const { return pos; }
00115   KoXmlStream& operator<<( const QString& str )
00116     { if(saveData) data.append(str); pos+=str.length(); return *this; }
00117   KoXmlStream& appendEscape( const QString& str );
00118 
00119 private:
00120   bool saveData;
00121   QString data;
00122   int pos;
00123 };
00124 
00125 KoXmlStream& KoXmlStream::appendEscape( const QString& str )
00126 {
00127   unsigned len = str.length();
00128 
00129   if( saveData )
00130   {
00131     data.reserve( data.length() + len );
00132     for( unsigned c=0; c<len; c++ )
00133       if( str[c]=='<' ){ data.append( "&lt;"); pos += 4; } else
00134       if( str[c]=='>'){ data.append( "&gt;"); pos+= 4; } else
00135       if( str[c]=='"'){ data.append( "&quot;"); pos += 6; } else
00136       if( str[c]=='&'){ data.append( "&amp;"); pos += 5; } else
00137        { data.append( str[c] ); pos++; } 
00138   }
00139   else
00140   {
00141     pos += len;
00142     for( unsigned c=0; c<len; c++ )
00143       if( str[c]=='<' ) pos += 3; else // "&lt;"
00144       if( str[c]=='>') pos+= 3; else   // "&gt;"
00145       if( str[c]=='"') pos += 5; else  // "&quot;"
00146       if( str[c]=='&') pos += 4;  // "&amp;"
00147   }
00148 
00149   return *this;
00150 }
00151 
00152 class KoXmlNodeData
00153 {
00154 public:
00155 
00156   KoXmlNodeData();
00157   virtual ~KoXmlNodeData();
00158 
00159   // generic properties
00160   KoXmlNode::NodeType nodeType;
00161   QString tagName;
00162   QString namespaceURI;
00163   QString prefix;
00164   QString localName;
00165 
00166   // reference counting
00167   unsigned long count;
00168   void ref() { count++; }
00169   void unref() { --count; if( !count ) delete this; }
00170 
00171   // type information
00172   virtual const char* typeInfo() const { return "Node"; }
00173   QString nodeName() const;
00174 
00175   // for tree and linked-list
00176   KoXmlNodeData* parent;
00177   KoXmlNodeData* prev;
00178   KoXmlNodeData* next;
00179   KoXmlNodeData* first;
00180   KoXmlNodeData* last;
00181 
00182   QString text();
00183 
00184   // node manipulation
00185   void appendChild( KoXmlNodeData* child );
00186   virtual void clear();
00187   KoXmlNodeData* ownerDocument();
00188 
00189   // attributes
00190   void setAttribute( const QString& name, const QString& value );
00191   QString attribute( const QString& name );
00192   bool hasAttribute( const QString& name );
00193   void setAttributeNS( const QString& nsURI, const QString& name, const QString& value );
00194   QString attributeNS( const QString& nsURI, const QString& name );
00195   bool hasAttributeNS( const QString& nsURI, const QString& name );
00196 
00197   // for text and CDATA
00198   QString data() const;
00199   void setData( const QString& data );
00200 
00201   // for document node
00202   QXmlSimpleReader* xmlReader;
00203   QString buffer;
00204   bool setContent( QXmlInputSource* source, QXmlReader* reader, 
00205     QString* errorMsg = 0, int* errorLine = 0, int* errorColumn = 0 );
00206 
00207   // used when doing on-demand (re)parse
00208   bool loaded;
00209   unsigned startPos, endPos;
00210   void loadChildren( int depth=1 );
00211   void unloadChildren();
00212   bool fastLoading;
00213   
00214 private:
00215   QMap<QString,QString> attr;  
00216   QMap<DQString,QString> attrNS;  
00217   QString textData;
00218   friend class KoXmlHandler;
00219 };
00220 
00221 class KoXmlHandler : public QXmlDefaultHandler
00222 {
00223 public:
00224   KoXmlHandler( KoXmlNodeData*, bool processNamespace );
00225   ~KoXmlHandler();
00226 
00227   void setMaxDepth( int d ){ maxDepth = d; }
00228   void setInitialOffset( int ofs ){ parseOffset = ofs; }
00229 
00230   // content handler
00231   bool startDocument();
00232   bool endDocument();
00233 
00234   bool startElement( const QString& nsURI, const QString& localName, 
00235     const QString& qName, const QXmlAttributes& atts );
00236   bool endElement( const QString& nsURI, const QString& localName, 
00237     const QString& qName );
00238 
00239   bool characters( const QString& ch );
00240   bool processingInstruction( const QString& target, const QString& data );
00241   bool skippedEntity( const QString& name );
00242 
00243   // lexical handler
00244   bool startCDATA();
00245   bool endCDATA();
00246   bool startEntity( const QString & );
00247   bool endEntity( const QString & );
00248   bool startDTD( const QString& name, const QString& publicId, 
00249     const QString& systemId );
00250   bool comment( const QString& ch );
00251 
00252   // decl handler
00253   bool externalEntityDecl( const QString &name, const QString &publicId, 
00254     const QString &systemId ) ;
00255 
00256   // DTD handler
00257   bool notationDecl( const QString & name, const QString & publicId, 
00258     const QString & systemId );
00259   bool unparsedEntityDecl( const QString &name, const QString &publicId, 
00260     const QString &systemId, const QString &notationName ) ;
00261 
00262   // error handler
00263   bool fatalError( const QXmlParseException& exception );
00264 
00265   QString errorMsg;
00266   int errorLine;
00267   int errorColumn;
00268 
00269 private:
00270   bool processNamespace;
00271   KoXmlNodeData* rootNode;
00272   KoXmlNodeData* currentNode;
00273   QString entityName;
00274   bool cdata;
00275   int parseOffset;
00276   KoXmlStream bufferStream;
00277   int elementDepth;
00278   int maxDepth;
00279 };
00280 
00281 // ==================================================================
00282 //
00283 //         KoXmlNodeData 
00284 //
00285 // ==================================================================
00286 
00287 KoXmlNodeData::KoXmlNodeData()
00288 {
00289   nodeType = KoXmlNode::NullNode;
00290 
00291   tagName = QString::null;
00292   prefix = QString::null;
00293   localName = QString::null;
00294   namespaceURI = QString::null;
00295   textData = QString::null;
00296 
00297   count = 1;
00298   parent = 0;
00299   prev = next = 0;
00300   first = last = 0;
00301 
00302   xmlReader = 0;
00303   startPos = endPos = 0;
00304 
00305   fastLoading = false;
00306 
00307   // assume true, it will be set to false by XML parser when this node
00308   // apparently has children AND the children are not loaded
00309   loaded = true;
00310 }
00311 
00312 KoXmlNodeData::~KoXmlNodeData()
00313 { 
00314   clear();
00315 }
00316 
00317 void KoXmlNodeData::clear()
00318 {
00319   if( first )
00320   for( KoXmlNodeData* node = first; node ; )
00321   {
00322     KoXmlNodeData* next = node->next;
00323     node->unref();
00324     node = next;
00325   }
00326 
00327   nodeType = KoXmlNode::NullNode;
00328   tagName = QString::null;
00329   prefix = QString::null;
00330   namespaceURI = QString::null;
00331   textData = QString::null;
00332 
00333   attr.clear();
00334   attrNS.clear();
00335 
00336   parent = 0;
00337   prev = next = 0;
00338   first = last = 0;
00339 
00340   delete xmlReader;
00341   xmlReader = 0;
00342   buffer = QString::null;
00343 }
00344 
00345 QString KoXmlNodeData::text()
00346 {
00347   QString t( "" );
00348 
00349   loadChildren();
00350 
00351   KoXmlNodeData* node = first;
00352   while ( node ) 
00353   {
00354     switch( node->nodeType )
00355     {
00356       case KoXmlNode::ElementNode: 
00357         t += node->text(); break;
00358       case KoXmlNode::TextNode:
00359         t += node->data(); break;
00360       case KoXmlNode::CDATASectionNode:
00361         t += node->data(); break;
00362       default: break;
00363     }
00364     node = node->next;
00365   }
00366 
00367   return t;
00368 }
00369 
00370 QString KoXmlNodeData::nodeName() const
00371 {
00372   QString n;
00373 
00374   switch( nodeType )
00375   {
00376     case KoXmlNode::ElementNode: 
00377       n = tagName;
00378       if (!prefix.isEmpty()) n.prepend(":").prepend(prefix); break;
00379     case KoXmlNode::TextNode: return QString("#text");
00380     case KoXmlNode::CDATASectionNode: return QString("#cdata-section");
00381     case KoXmlNode::DocumentNode: return QString("#document");
00382     default: break;
00383   }
00384 
00385   return n;
00386 }
00387 
00388 KoXmlNodeData* KoXmlNodeData::ownerDocument()
00389 {
00390   KoXmlNodeData* owner = this;
00391 
00392   while( owner->parent ) 
00393     owner = owner->parent;
00394 
00395   return (owner->nodeType==KoXmlNode::DocumentNode) ? owner : 0;
00396 }
00397 
00398 void KoXmlNodeData::appendChild( KoXmlNodeData* node )
00399 {
00400   node->parent = this;
00401   if( !last )
00402     first = last = node;
00403   else
00404   {
00405     last->next = node;
00406     node->prev = last;
00407     node->next = 0;
00408     last = node;
00409   }
00410 }
00411 
00412 void KoXmlNodeData::setAttribute( const QString& name, const QString& value )
00413 {
00414   attr[ name ] = value;
00415 }
00416 
00417 QString KoXmlNodeData::attribute( const QString& name )
00418 {
00419   return attr[ name ];
00420 }
00421 
00422 bool KoXmlNodeData::hasAttribute( const QString& name )
00423 {
00424   return attr.contains( name );
00425 }
00426 
00427 void KoXmlNodeData::setAttributeNS( const QString& nsURI, 
00428 const QString& name, const QString& value )
00429 {
00430   QString prefix;
00431   QString localName = name;
00432   int i = name.find( ':' );
00433   if( i != -1 )
00434   {
00435     localName = name.mid( i + 1 );
00436     prefix = name.left( i );
00437   }
00438   
00439   if( prefix.isNull() ) return;
00440   
00441   DQString key( nsURI, localName );
00442   attrNS[ key ] = value;
00443 }
00444 
00445 QString KoXmlNodeData::attributeNS( const QString& nsURI, const QString& name )
00446 {
00447   DQString key( nsURI, name );
00448   return attrNS[ key ];
00449 }
00450 
00451 bool KoXmlNodeData::hasAttributeNS( const QString& nsURI, const QString& name )
00452 {
00453   DQString key( nsURI, name );
00454   return attrNS.contains( key );
00455 }
00456 
00457 QString KoXmlNodeData::data() const
00458 {
00459   return textData;
00460 }
00461 
00462 void KoXmlNodeData::setData( const QString& d )
00463 {
00464   textData = d;
00465 }
00466 
00467 bool KoXmlNodeData::setContent( QXmlInputSource* source, 
00468 QXmlReader* reader, QString* errorMsg, int* errorLine, int* errorColumn )
00469 {
00470   if( nodeType != KoXmlNode::DocumentNode )
00471     return false;
00472 
00473   clear();
00474   nodeType = KoXmlNode::DocumentNode;
00475 
00476   // sanity checks
00477   if( !source ) return false;
00478   if( !reader ) return false;
00479 
00480   // copy the reader for later on-demand loading
00481   // FIXME this is a workaround because no copy is possible with QXmlReader
00482   char* features[] =
00483   { 
00484     "http://xml.org/sax/features/namespaces",
00485     "http://xml.org/sax/features/namespace-prefixes",
00486     "http://trolltech.com/xml/features/report-whitespace-only-CharData",
00487     "http://trolltech.com/xml/features/report-start-end-entity"
00488   };
00489   xmlReader = new QXmlSimpleReader;
00490   for( int fi=0; fi<4; fi++ )
00491     xmlReader->setFeature( features[fi], reader->feature( features[fi] ) );
00492   
00493   bool processNamespace = 
00494     reader->feature( "http://xml.org/sax/features/namespaces" ) && 
00495     !reader->feature( "http://xml.org/sax/features/namespace-prefixes" );
00496 
00497   KoXmlHandler handler( this, processNamespace );
00498   reader->setContentHandler( &handler );
00499   reader->setErrorHandler( &handler );
00500   reader->setLexicalHandler( &handler );
00501   reader->setDeclHandler( &handler );
00502   reader->setDTDHandler( &handler );
00503 
00504   if( !fastLoading )
00505     handler.setMaxDepth( 4 );
00506 
00507   if( !reader->parse( source ) ) 
00508   {
00509     // parsing error has occurred
00510     if( errorMsg ) *errorMsg = handler.errorMsg;
00511     if( errorLine ) *errorLine = handler.errorLine;
00512     if( errorColumn )  *errorColumn = handler.errorColumn;
00513     return false;
00514   }
00515 
00516   return true;
00517 }
00518 
00519 void KoXmlNodeData::loadChildren( int depth )
00520 {
00521   // for more than 1 level, force reloading anyway
00522   if( ( depth== 1 ) && loaded ) return;
00523 
00524   KoXmlNodeData* doc = ownerDocument();
00525   if( !doc ) return;
00526 
00527   // fast loading? then this on-demand loading makes no sense
00528   if( doc->fastLoading ) return;
00529 
00530   unloadChildren();
00531 
00532   bool nsProcess = 
00533     doc->xmlReader->feature( "http://xml.org/sax/features/namespaces" ) && 
00534     !doc->xmlReader->feature( "http://xml.org/sax/features/namespace-prefixes" );
00535 
00536 
00537   // XML snippet for the children, including this element
00538   QString snippet = doc->buffer.mid( startPos, endPos-startPos+1 );
00539 
00540   // now parse all subnodes
00541   KoXmlHandler handler( this, nsProcess );
00542   handler.setMaxDepth( depth );
00543   handler.setInitialOffset( startPos );
00544   doc->xmlReader->setContentHandler( &handler );
00545   doc->xmlReader->setErrorHandler( &handler );
00546   doc->xmlReader->setLexicalHandler( &handler );
00547   doc->xmlReader->setDeclHandler( &handler );
00548   doc->xmlReader->setDTDHandler( &handler );
00549 
00550   QXmlInputSource source;
00551   source.setData( snippet );
00552   if( !doc->xmlReader->parse( source ) ) 
00553   {
00554     // parsing error has occurred, which should not happen
00555     // nothing we can do except...
00556     loaded = false;
00557     qWarning( "On-demand loading triggers parse error!" );
00558   }
00559   else
00560     loaded = true;
00561 }
00562 
00563 void KoXmlNodeData::unloadChildren()
00564 {
00565   if( !loaded ) return;
00566 
00567   if( first )
00568   for( KoXmlNodeData* node = first; node ; )
00569   {
00570     KoXmlNodeData* next = node->next;
00571     node->unloadChildren();
00572     node->unref();
00573     node = next;
00574   }
00575 
00576   loaded = false;
00577   first = last = 0;
00578 }
00579 
00580 
00581 // ==================================================================
00582 //
00583 //         KoXmlHandler 
00584 //
00585 // ==================================================================
00586 
00587 KoXmlHandler::KoXmlHandler( KoXmlNodeData* n, bool ns ): 
00588 QXmlDefaultHandler()
00589 {
00590   processNamespace = ns;
00591 
00592   rootNode = n;
00593   currentNode = n;
00594   cdata = false;
00595   entityName = QString::null;
00596 
00597   errorMsg = QString::null;
00598   errorLine = 0;
00599   errorColumn = 0;
00600 
00601   parseOffset = 0;
00602   elementDepth = -1;
00603   maxDepth = 999;
00604 
00605   bufferStream.setSaveData( rootNode->nodeType == KoXmlNode::DocumentNode );
00606 }
00607 
00608 KoXmlHandler::~KoXmlHandler()
00609 {
00610 }
00611 
00612 bool KoXmlHandler::startDocument()
00613 {
00614   // just for sanity
00615   currentNode = rootNode;  
00616   cdata = false;
00617   entityName = QString::null;
00618   elementDepth = -1;
00619 
00620   return true;
00621 }
00622 
00623 bool KoXmlHandler::endDocument()
00624 {
00625   // just for sanity
00626   if( rootNode->nodeType == KoXmlNode::DocumentNode )
00627     if( currentNode!=rootNode )
00628       return false; 
00629 
00630   if( rootNode->nodeType == KoXmlNode::DocumentNode )
00631     rootNode->buffer = bufferStream.stringData();
00632 
00633   return true;
00634 }
00635 
00636 bool KoXmlHandler::startDTD( const QString& name, const QString& publicId, 
00637 const QString& systemId )
00638 {
00639   Q_UNUSED( name );
00640   Q_UNUSED( publicId );
00641   Q_UNUSED( systemId );
00642 
00643   // we skip DTD
00644   return true;
00645 }
00646 
00647 bool KoXmlHandler::startElement( const QString& nsURI, const QString& localName, 
00648 const QString& name, const QXmlAttributes& atts )
00649 {
00650   Q_UNUSED( localName );
00651 
00652   // sanity check
00653   if( !currentNode ) 
00654     return false;
00655 
00656   // we are going one level deeper
00657   elementDepth++;
00658 
00659   QString nodePrefix, nodeLocalName, nodeTagName;
00660   KoXmlNodeData* element = 0;
00661 
00662   if( processNamespace )
00663   {
00664     // parse, using namespace
00665     nodeTagName = name;
00666     nodeLocalName = name;
00667     nodePrefix = nsURI.isNull() ? QString::null : QString("");
00668     int i = name.find( ':' );
00669     if( i != -1 )
00670     {
00671       nodeTagName = name.mid( i + 1 );
00672       nodeLocalName = nodeTagName;
00673       nodePrefix = name.left( i );
00674     }
00675 
00676     if( elementDepth <= maxDepth )
00677     {
00678       // construct a new element
00679       element = new KoXmlNodeData;
00680       element->nodeType = KoXmlNode::ElementNode;
00681       element->parent = currentNode;
00682       element->namespaceURI = nsURI;
00683       element->prefix = nodePrefix;
00684       element->localName = nodeLocalName;
00685       element->tagName = nodeTagName;
00686 
00687       // Note: endPos will be later fixed in endElement
00688       element->endPos = element->startPos = parseOffset + bufferStream.at();
00689 
00690       // handle the attributes
00691       for( int c=0; c<atts.length(); c++ )
00692       {
00693         QString prefix;
00694         QString qName; // with prefix
00695         QString name;  // without prefix, i.e. local name
00696 
00697         name = qName = atts.qName(c);
00698         int i = qName.find( ':' );
00699         if( i != -1 ) prefix = qName.left( i );
00700         if( i != -1 ) name = qName.mid( i + 1 );
00701         element->setAttributeNS( atts.uri(c), qName, atts.value(c) );
00702         element->setAttribute( name, atts.value(c) );
00703       }
00704     }
00705 
00706     // save in buffer for later on-demand loading
00707     if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
00708     {
00709       bufferStream << "<";
00710       if( !nodePrefix.isEmpty() )
00711         bufferStream << nodePrefix << ":";
00712       bufferStream << localName;
00713       bufferStream << " xmlns";
00714       if( !nodePrefix.isEmpty() ) 
00715         bufferStream << ":" << nodePrefix;
00716       bufferStream << "=\"";
00717       bufferStream.appendEscape( nsURI );
00718       bufferStream << "\"";
00719       for( int c=0; c<atts.length(); c++ )
00720       {
00721         QString prefix;
00722         QString name = atts.qName(c);  // qName contains the prefix
00723         int i = name.find( ':' );
00724         if( i != -1 ) prefix = name.left( i );
00725         if( i != -1 ) name = atts.qName(c).mid( i + 1 );
00726         if( !atts.uri(c).isEmpty() )
00727           bufferStream << " xmlns:" << prefix << "=\"" << atts.uri(c) << "\"";
00728         bufferStream << " ";
00729         if( !prefix.isEmpty() ) bufferStream << prefix << ":";
00730         bufferStream << name << "=\"";
00731         bufferStream.appendEscape( atts.value(c) );
00732         bufferStream << "\"";
00733       }
00734       bufferStream << ">";
00735     }
00736   }
00737   else
00738   {
00739     // parse, without using namespace
00740     nodeTagName = name;
00741 
00742     if( elementDepth <= maxDepth )
00743     {
00744       // construct a new element
00745       element = new KoXmlNodeData;
00746       element->nodeType = KoXmlNode::ElementNode;
00747       element->parent = currentNode;
00748       element->namespaceURI = QString::null;
00749       element->prefix = QString::null;
00750       element->localName = QString::null;
00751       element->tagName = nodeTagName;
00752 
00753       if( rootNode->nodeType == KoXmlNode::DocumentNode ) 
00754         element->fastLoading = rootNode->fastLoading;
00755   
00756       // Note: endPos will be later fixed in endElement
00757       element->endPos = element->startPos = parseOffset + bufferStream.at();
00758 
00759       // handle the attributes
00760       for( int c=0; c<atts.length(); c++ )
00761         element->setAttribute( atts.qName(c), atts.value(c) );
00762     }
00763 
00764     // save in buffer for later on-demand loading
00765     if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
00766     {
00767       bufferStream << "<";
00768       bufferStream << nodeTagName;
00769       for( int c=0; c<atts.length(); c++ )
00770       {
00771         bufferStream << " " << atts.qName(c) << "=\"";
00772         bufferStream.appendEscape( atts.value(c) );
00773         bufferStream << "\""; 
00774       }
00775       bufferStream << ">";
00776     }
00777 
00778   }
00779 
00780   // if we do not parse a complete document, the first element is ignored
00781   // this feature is used in on-demand loading
00782   // e.g. "<ul><li><b>bold items</b></li></ul>", if root node points to 
00783   // <ul> tag, then the first <ul> is skipped so that next <li> element
00784   // becomes the child of it.
00785   if( elementDepth == 0 )
00786     if( rootNode->nodeType != KoXmlNode::DocumentNode )
00787     {
00788       delete element;
00789       return true;
00790     }
00791 
00792   if( element )
00793   {
00794     // add as the child and traverse to it
00795     currentNode->loaded = true;
00796     currentNode->appendChild( element );
00797     currentNode = element;
00798   }
00799   else
00800     currentNode->loaded = false;
00801 
00802   return true;
00803 }
00804 
00805 bool KoXmlHandler::endElement( const QString& nsURI, const QString& localName, 
00806 const QString& qName )
00807 {
00808   Q_UNUSED( nsURI );
00809   Q_UNUSED( localName );
00810   
00811   // sanity check
00812   if( !currentNode ) return false;
00813   if( !currentNode->parent ) return false;
00814 
00815   // see comments in startElement about first element and on-demand loading
00816   if( rootNode->nodeType == KoXmlNode::DocumentNode )
00817     if( currentNode == rootNode ) 
00818       return false;
00819 
00820   // buffer for on-demand loading
00821   if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
00822     bufferStream << "</" << qName << ">";
00823 
00824   // fix up the pointer
00825   currentNode->endPos = parseOffset + bufferStream.at() - 1;
00826 
00827   // go up one level
00828   if( elementDepth <= maxDepth )
00829     currentNode = currentNode->parent;
00830 
00831   // we are going up one level
00832   elementDepth--;
00833 
00834   return true;
00835 }
00836 
00837 bool KoXmlHandler::characters( const QString& str )
00838 {
00839   // sanity check
00840   if( rootNode->nodeType == KoXmlNode::DocumentNode )
00841     if( currentNode == rootNode )
00842       return false;
00843 
00844   // are we inside entity ?
00845   if( !entityName.isEmpty() )
00846   {
00847     // we do not handle entity but need to keep track of it
00848     // because we want to skip it alltogether
00849     return true;
00850   }
00851 
00852   if( cdata )
00853   {
00854     // are we inside CDATA section ?
00855     if( elementDepth <= maxDepth )
00856     {
00857       KoXmlNodeData* cdata = new KoXmlNodeData;
00858       cdata->nodeType = KoXmlNode::CDATASectionNode;
00859       cdata->parent = currentNode;
00860       cdata->setData( str );
00861       currentNode->appendChild( cdata );
00862     }
00863 
00864     if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
00865       bufferStream << "<![CDATA[" << str << "]]>"; // no escape for CDATA
00866   }
00867   else
00868   {
00869     // this must be normal text node
00870     if( elementDepth <= maxDepth )
00871     {
00872       KoXmlNodeData* text = new KoXmlNodeData;
00873       text->nodeType = KoXmlNode::TextNode;
00874       text->parent = currentNode;
00875       text->setData( str );
00876       currentNode->appendChild( text );
00877     }
00878   
00879     // save it for later use  
00880     if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
00881       bufferStream.appendEscape( str );
00882   }
00883 
00884   return true;
00885 }
00886 
00887 bool KoXmlHandler::processingInstruction( const QString& target, 
00888 const QString& data )
00889 {
00890   // sanity check
00891   if( !currentNode ) 
00892     return false;
00893   
00894   KoXmlNodeData* instruction = new KoXmlNodeData;
00895   instruction->nodeType = KoXmlNode::ProcessingInstructionNode;
00896   instruction->parent = currentNode;
00897   instruction->tagName = target;
00898   instruction->setData( data );
00899 
00900   currentNode->appendChild( instruction );
00901 
00902   return true;
00903 }
00904 
00905 bool KoXmlHandler::skippedEntity( const QString& name )
00906 {
00907   Q_UNUSED( name );
00908 
00909   // we skip entity
00910   return true;
00911 }
00912 
00913 bool KoXmlHandler::startCDATA()
00914 {
00915   cdata = true;
00916   return true;
00917 }
00918 
00919 bool KoXmlHandler::endCDATA()
00920 {
00921   cdata = false;
00922   return true;
00923 }
00924 
00925 bool KoXmlHandler::startEntity( const QString& name )
00926 {
00927   entityName = name;
00928   return true;
00929 }
00930 
00931 bool KoXmlHandler::endEntity( const QString& name )
00932 {
00933   Q_UNUSED( name );
00934   entityName = QString::null;
00935   return true;
00936 }
00937 
00938 bool KoXmlHandler::comment( const QString& comment )
00939 {
00940   Q_UNUSED( comment );
00941 
00942   // we skip comment
00943   return true;
00944 }
00945 
00946 bool KoXmlHandler::unparsedEntityDecl( const QString &name, 
00947 const QString &publicId, const QString &systemId, const QString &notationName )
00948 {
00949   Q_UNUSED( name );
00950   Q_UNUSED( publicId );
00951   Q_UNUSED( systemId );
00952   Q_UNUSED( notationName );
00953 
00954   // we skip entity
00955   return true;
00956 }
00957 
00958 bool KoXmlHandler::externalEntityDecl( const QString &name, 
00959 const QString &publicId, const QString &systemId )
00960 {
00961   Q_UNUSED( name );
00962   Q_UNUSED( publicId );
00963   Q_UNUSED( systemId );
00964 
00965   // we skip entity
00966   return true;
00967 }
00968 
00969 bool KoXmlHandler::notationDecl( const QString & name, 
00970 const QString & publicId, const QString & systemId )
00971 {
00972   Q_UNUSED( name );
00973   Q_UNUSED( publicId );
00974   Q_UNUSED( systemId );
00975 
00976   // we skip notation node
00977   return true;
00978 }
00979 
00980 bool KoXmlHandler::fatalError( const QXmlParseException& exception )
00981 {
00982   errorMsg = exception.message();
00983   errorLine =  exception.lineNumber();
00984   errorColumn =  exception.columnNumber();
00985   return QXmlDefaultHandler::fatalError( exception );
00986 }
00987 
00988 // ==================================================================
00989 //
00990 //         KoXmlNode 
00991 //
00992 // ==================================================================
00993 
00994 // Creates a null node
00995 KoXmlNode::KoXmlNode()
00996 {
00997   d = new KoXmlNodeData;
00998 }
00999 
01000 // Destroys this node
01001 KoXmlNode::~KoXmlNode()
01002 {
01003   if( d ) d->unref();
01004 }
01005 
01006 // Creates a copy of another node
01007 KoXmlNode::KoXmlNode( const KoXmlNode& node )
01008 {
01009   d = node.d;
01010   d->ref();
01011 }
01012 
01013 // Creates a node for specific implementation
01014 KoXmlNode::KoXmlNode( KoXmlNodeData* data )
01015 {
01016   d = data;
01017   data->ref();
01018 }
01019 
01020 // Creates a shallow copy of another node
01021 KoXmlNode& KoXmlNode::operator=( const KoXmlNode& node )
01022 {
01023   d->unref();
01024   d = node.d;
01025   d->ref();
01026   return *this;
01027 }
01028 
01029 // Note: two null nodes are always equal
01030 bool KoXmlNode::operator==( const KoXmlNode& node ) const
01031 {
01032   if( isNull() && node.isNull() ) return true;
01033   return( d==node.d );
01034 }
01035 
01036 // Note: two null nodes are always equal
01037 bool KoXmlNode::operator!=( const KoXmlNode& node ) const
01038 {
01039   if( isNull() && !node.isNull() ) return true;
01040   if( !isNull() && node.isNull() ) return true;
01041   if( isNull() && node.isNull() ) return false;
01042   return( d!=node.d );
01043 }
01044 
01045 KoXmlNode::NodeType KoXmlNode::nodeType() const
01046 {
01047   return d->nodeType;
01048 }
01049 
01050 bool KoXmlNode::isElement() const
01051 {
01052   return d->nodeType == ElementNode;
01053 }
01054 
01055 bool KoXmlNode::isText() const
01056 {
01057   return (d->nodeType == TextNode) || isCDATASection();
01058 }
01059 
01060 bool KoXmlNode::isCDATASection() const
01061 {
01062   return d->nodeType == CDATASectionNode;
01063 }
01064 
01065 bool KoXmlNode::isDocument() const
01066 {
01067   return d->nodeType == DocumentNode;
01068 }
01069 
01070 bool KoXmlNode::isNull() const
01071 {
01072   return d->nodeType == NullNode;
01073 }
01074 
01075 void KoXmlNode::clear()
01076 {
01077   d->unref();
01078   d = new KoXmlNodeData;
01079 }
01080 
01081 QString KoXmlNode::nodeName() const
01082 {
01083   return d->nodeName();
01084 }
01085 
01086 QString KoXmlNode::prefix() const
01087 {
01088   return isElement() ? d->prefix : QString::null;
01089 }
01090 
01091 QString KoXmlNode::namespaceURI() const
01092 {
01093   return isElement() ? d->namespaceURI : QString::null;
01094 }
01095 
01096 QString KoXmlNode::localName() const
01097 {
01098   return isElement() ? d->localName : QString::null;
01099 }
01100 
01101 KoXmlDocument KoXmlNode::ownerDocument() const
01102 {
01103   KoXmlNodeData* node = d; 
01104   while( node->parent ) node = node->parent;
01105 
01106   if( node->nodeType != DocumentNode ) return KoXmlDocument();  
01107   return KoXmlDocument( node );
01108 }
01109 
01110 KoXmlNode KoXmlNode::parentNode() const
01111 {
01112   return d->parent ? KoXmlNode( d->parent ) : KoXmlNode();
01113 }
01114 
01115 bool KoXmlNode::hasChildNodes() const
01116 {
01117   d->loadChildren();
01118   return d->first!=0 ;
01119 }
01120 
01121 KoXmlNode KoXmlNode::firstChild() const
01122 {
01123   if( !d->fastLoading ) 
01124     d->loadChildren();
01125   return d->first ? KoXmlNode( d->first ) : KoXmlNode();
01126 }
01127 
01128 KoXmlNode KoXmlNode::lastChild() const
01129 {
01130   if( !d->fastLoading ) 
01131     d->loadChildren();
01132   return d->last ? KoXmlNode( d->last ) : KoXmlNode();
01133 }
01134 
01135 KoXmlNode KoXmlNode::nextSibling() const
01136 {
01137   return d->next ? KoXmlNode( d->next ) : KoXmlNode();
01138 }
01139 
01140 KoXmlNode KoXmlNode::previousSibling() const
01141 {
01142   return d->prev ? KoXmlNode( d->prev ) : KoXmlNode();
01143 }
01144 
01145 KoXmlNode KoXmlNode::namedItem( const QString& name ) const
01146 {
01147   if( !d->fastLoading ) 
01148     d->loadChildren();
01149 
01150   KoXmlNodeData* node = d->first;
01151   while ( node ) 
01152   {
01153     if( node->nodeName() == name )
01154       return KoXmlNode( node );
01155     node = node->next;
01156   }
01157 
01158   // not found
01159   return KoXmlNode();
01160 }
01161 
01162 KoXmlNode KoXmlNode::namedItemNS( const QString& nsURI, const QString& name ) const
01163 {
01164   if( !d->fastLoading ) 
01165     d->loadChildren();
01166 
01167   KoXmlNodeData* node = d->first;
01168   while ( node ) 
01169   {
01170     if( !node->prefix.isNull() )
01171     if( node->namespaceURI == nsURI )
01172     if( node->localName == name )
01173       return KoXmlNode( node );
01174     node = node->next;
01175   }
01176 
01177   // not found
01178   return KoXmlNode();
01179 }
01180 
01181 KoXmlElement KoXmlNode::toElement()
01182 {
01183   return isElement() ? KoXmlElement( d ) : KoXmlElement();
01184 }
01185 
01186 KoXmlText KoXmlNode::toText()
01187 {
01188   return isText() ? KoXmlText( d ) : KoXmlText();
01189 }
01190 
01191 KoXmlCDATASection KoXmlNode::toCDATASection()
01192 {
01193   return isCDATASection() ? KoXmlCDATASection( (KoXmlNodeData*)d ) :
01194     KoXmlCDATASection();
01195 }
01196 
01197 KoXmlDocument KoXmlNode::toDocument()
01198 {
01199   return isDocument() ? KoXmlDocument( d ) : KoXmlDocument();
01200 }
01201 
01202 void KoXmlNode::load( int depth )
01203 {
01204   d->loadChildren( depth );
01205 }
01206 
01207 void KoXmlNode::unload()
01208 {
01209   d->unloadChildren();
01210 }
01211 
01212 // ==================================================================
01213 //
01214 //         KoXmlElement 
01215 //
01216 // ==================================================================
01217 
01218 // Creates an empty element
01219 KoXmlElement::KoXmlElement(): KoXmlNode()
01220 {
01221   d->unref();
01222   d = new KoXmlNodeData;
01223 }
01224 
01225 KoXmlElement::~KoXmlElement()
01226 {
01227   d->unref();
01228   d = 0;
01229 }
01230 
01231 // Creates a shallow copy of another element
01232 KoXmlElement::KoXmlElement( const KoXmlElement& element ): KoXmlNode()
01233 {
01234   d->unref();
01235   d = element.d;
01236   d->ref();
01237 }
01238 
01239 KoXmlElement::KoXmlElement( KoXmlNodeData* data ): KoXmlNode()
01240 {
01241   d->unref();
01242   d = data;
01243   d->ref();
01244 }
01245 
01246 // Copies another element
01247 KoXmlElement& KoXmlElement::operator=( const KoXmlElement& element )
01248 {
01249   KoXmlNode::operator=( element );
01250   return *this;
01251 }
01252 
01253 bool KoXmlElement::operator== ( const KoXmlElement& element ) const
01254 {
01255   if( isNull() || element.isNull() ) return false;
01256   return (d==element.d);
01257 }
01258 
01259 bool KoXmlElement::operator!= ( const KoXmlElement& element ) const
01260 {
01261   if( isNull() && element.isNull() ) return false;
01262   if( isNull() || element.isNull() ) return true;
01263   return (d!=element.d);
01264 }
01265 
01266 QString KoXmlElement::tagName() const
01267 {
01268   return isElement() ? ((KoXmlNodeData*)d)->tagName: QString::null;
01269 }
01270 
01271 QString KoXmlElement::text() const
01272 {
01273   return d->text();
01274 }
01275 
01276 bool KoXmlElement::isElement() const
01277 {
01278   return true;
01279 }
01280 
01281 QString KoXmlElement::attribute( const QString& name ) const
01282 {
01283   return attribute( name, QString::null );
01284 }
01285 
01286 QString KoXmlElement::attribute( const QString& name, 
01287 const QString& defaultValue ) const
01288 {
01289   if( !isElement() )
01290     return defaultValue;
01291     
01292   if( !hasAttribute( name ) )  
01293     return defaultValue;
01294 
01295   return ((KoXmlNodeData*)d)->attribute( name );
01296 }
01297 
01298 QString KoXmlElement::attributeNS( const QString& namespaceURI, 
01299 const QString& localName, const QString& defaultValue ) const
01300 {
01301   if( !isElement() )
01302     return defaultValue;
01303     
01304   if( !hasAttributeNS( namespaceURI,localName ) )  
01305     return defaultValue;
01306 
01307   return ((KoXmlNodeData*)d)->attributeNS( namespaceURI,localName );
01308 }
01309 
01310 bool KoXmlElement::hasAttribute( const QString& name ) const
01311 {
01312   return isElement() ? ((KoXmlNodeData*)d)->hasAttribute( name ) : false;
01313 }
01314 
01315 bool KoXmlElement::hasAttributeNS( const QString& namespaceURI, 
01316 const QString& localName ) const
01317 {
01318   return isElement() ? ((KoXmlNodeData*)d)->hasAttributeNS( 
01319     namespaceURI, localName ) : false;;
01320 }
01321 
01322 // ==================================================================
01323 //
01324 //         KoXmlText
01325 //
01326 // ==================================================================
01327 
01328 KoXmlText::KoXmlText(): KoXmlNode()
01329 {
01330   d->unref();
01331   d = new KoXmlNodeData;
01332   d->nodeType = TextNode;
01333 }
01334 
01335 KoXmlText::~KoXmlText()
01336 {
01337   if( d ) d->unref();
01338   d = 0;
01339 }
01340 
01341 KoXmlText::KoXmlText( const KoXmlText& text ): KoXmlNode()
01342 {
01343   d->unref();
01344   d = (KoXmlNodeData*) text.d;
01345   d->ref();
01346 }
01347 
01348 KoXmlText::KoXmlText( KoXmlNodeData* data ): KoXmlNode()
01349 {
01350   d->unref();
01351   d = data;
01352   d->ref();
01353 }
01354 
01355 bool KoXmlText::isText() const
01356 {
01357   return true;
01358 }
01359 
01360 QString KoXmlText::data() const
01361 {
01362   return ((KoXmlNodeData*)d)->data();
01363 }
01364 
01365 KoXmlText& KoXmlText::operator=( const KoXmlText& element )
01366 {
01367   KoXmlNode::operator=( element );
01368   return *this;
01369 }
01370 
01371 // ==================================================================
01372 //
01373 //         KoXmlCDATASection
01374 //
01375 // ==================================================================
01376 
01377 KoXmlCDATASection::KoXmlCDATASection(): KoXmlText()
01378 {
01379   d->unref();
01380   d = new KoXmlNodeData;
01381   d->nodeType = KoXmlNode::CDATASectionNode;
01382 }
01383 
01384 KoXmlCDATASection::~KoXmlCDATASection()
01385 {
01386   d->unref();
01387   d = 0;
01388 }
01389 
01390 KoXmlCDATASection::KoXmlCDATASection( const KoXmlCDATASection& cdata ):
01391 KoXmlText()
01392 {
01393   d->unref();
01394   d = (KoXmlNodeData*) cdata.d;
01395   d->ref();
01396 }
01397 
01398 KoXmlCDATASection::KoXmlCDATASection( KoXmlNodeData* cdata ):
01399 KoXmlText()
01400 {
01401   d->unref();
01402   d = cdata;
01403   d->ref();
01404 }
01405 
01406 bool KoXmlCDATASection::isCDATASection() const
01407 {
01408   return true;
01409 }
01410 
01411 KoXmlCDATASection& KoXmlCDATASection::operator=( const KoXmlCDATASection& cdata )
01412 {
01413   KoXmlNode::operator=( cdata );
01414   return *this;
01415 }
01416 
01417 // ==================================================================
01418 //
01419 //         KoXmlDocument 
01420 //
01421 // ==================================================================
01422 
01423 KoXmlDocument::KoXmlDocument(): KoXmlNode()
01424 {
01425   d->unref();
01426   d = new KoXmlNodeData;
01427   d->nodeType = KoXmlNode::DocumentNode;
01428 }
01429 
01430 KoXmlDocument::~KoXmlDocument()
01431 {
01432   d->unref();
01433   d = 0;
01434 }
01435 
01436 KoXmlDocument::KoXmlDocument( KoXmlNodeData* data ): KoXmlNode()
01437 {
01438   d->unref();
01439   d = data;
01440   d->ref();
01441 }
01442 
01443 // Creates a copy of another document
01444 KoXmlDocument::KoXmlDocument( const KoXmlDocument& doc ): KoXmlNode()
01445 {
01446   d->unref();
01447   d = doc.d;
01448   d->ref();
01449 }
01450 
01451 // Creates a shallow copy of another document
01452 KoXmlDocument& KoXmlDocument::operator=( const KoXmlDocument& doc )
01453 {
01454   KoXmlNode::operator=( doc );
01455   return *this;
01456 }
01457 
01458 // Checks if this document and doc are equals
01459 bool KoXmlDocument::operator==( const KoXmlDocument& doc ) const
01460 {
01461   return( d==doc.d );
01462 }
01463 
01464 // Checks if this document and doc are not equals
01465 bool KoXmlDocument::operator!=( const KoXmlDocument& doc ) const
01466 {
01467   return( d!=doc.d );
01468 }
01469 
01470 bool KoXmlDocument::isDocument() const
01471 {
01472   return true;
01473 }
01474 
01475 KoXmlElement KoXmlDocument::documentElement() const
01476 {
01477   for( KoXmlNodeData* node=d->first; node; )
01478     if( node->nodeType==KoXmlNode::ElementNode )
01479       return KoXmlElement( node );
01480     else node = node->next;  
01481 
01482   return KoXmlElement();
01483 }
01484 
01485 void KoXmlDocument::setFastLoading( bool f )
01486 {
01487   d->fastLoading = f;
01488 }
01489 
01490 bool KoXmlDocument::fastLoading() const
01491 {
01492   return d->fastLoading;
01493 }
01494 
01495 bool KoXmlDocument::setContent( QXmlInputSource *source, QXmlReader *reader, 
01496     QString* errorMsg, int* errorLine, int* errorColumn )
01497 {
01498   if( d->nodeType != KoXmlNode::DocumentNode ) 
01499     return false;
01500 
01501   return d->setContent( source, reader, errorMsg, errorLine, errorColumn );
01502 }
01503 
01504 // no namespace processing
01505 bool KoXmlDocument::setContent( QIODevice* device, QString* errorMsg,
01506 int* errorLine, int* errorColumn )
01507 {
01508   return setContent( device, false, errorMsg, errorLine, errorColumn );
01509 }
01510 
01511 bool KoXmlDocument::setContent( QIODevice* device, bool namespaceProcessing, 
01512 QString* errorMsg, int* errorLine, int* errorColumn )
01513 {
01514   if( d->nodeType != KoXmlNode::DocumentNode ) 
01515     return false;
01516 
01517   QXmlSimpleReader reader;
01518   reader.setFeature( "http://xml.org/sax/features/namespaces", namespaceProcessing );
01519   reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", !namespaceProcessing );
01520   reader.setFeature( "http://trolltech.com/xml/features/report-whitespace-only-CharData", false );
01521 
01522   // FIXME this hack is apparently private
01523   //reader.setUndefEntityInAttrHack(true);
01524 
01525   QXmlInputSource source( device );
01526   return d->setContent( &source, &reader, errorMsg, errorLine, errorColumn );
01527 }
01528 
01529 #endif
01530 
01531 KoXmlElement KoXml::namedItemNS( const KoXmlNode& node, const char* nsURI, 
01532 const char* localName )
01533 {
01534 #ifdef KOXML_USE_QDOM
01535     // David's solution for namedItemNS, only for QDom stuff
01536     KoXmlNode n = node.firstChild();
01537     for ( ; !n.isNull(); n = n.nextSibling() ) {
01538         if ( n.isElement() && n.localName() == localName && 
01539             n.namespaceURI() == nsURI )
01540                return n.toElement();
01541     }
01542     return KoXmlElement();
01543 #else
01544   return node.namedItemNS( nsURI, localName).toElement();
01545 #endif
01546 }
01547 
01548 void KoXml::load( KoXmlNode& node, int depth )
01549 {
01550 #ifdef KOXML_USE_QDOM
01551   // do nothing, QDom has no on-demand loading
01552   Q_UNUSED( node );
01553   Q_UNUSED( depth );
01554 #else
01555   node.load( depth );
01556 #endif
01557 }
01558 
01559 
01560 void KoXml::unload( KoXmlNode& node )
01561 {
01562 #ifdef KOXML_USE_QDOM
01563   // do nothing, QDom has no on-demand unloading
01564   Q_UNUSED( node );
01565 #else
01566   node.unload();
01567 #endif
01568 }
KDE Home | KDE Accessibility Home | Description of Access Keys