F:/KPlato/koffice/libs/store/KoXmlReader.cpp

Aller à la documentation de ce fichier.
00001 /* This file is part of the KDE project
00002    Copyright (C) 2005-2006 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 koxmlreadertest.cpp WITH Valgrind 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 #include <QTextCodec>
00065 #include <QTextDecoder>
00066 
00067 #ifndef KOXML_USE_QDOM
00068 
00069 #include <qxml.h>
00070 #include <qdom.h>
00071 
00072 #include <QBuffer>
00073 #include <QHash>
00074 #include <QPair>
00075 #include <QStringList>
00076 #include <QVector>
00077 
00078 /*
00079  Use more compact representation of in-memory nodes.
00080  
00081  Advantages: faster iteration, can facilitate real-time compression.
00082  Disadvantages: still buggy, eat much more memory
00083 */
00084 #define KOXML_COMPACT
00085 
00086 
00087 typedef QPair<QString,QString> KoXmlStringPair;
00088 
00089 // this simplistic hash is rather fast-and-furious. it works because
00090 // likely there is very few namespaced attributes per element
00091 inline uint qHash( const KoXmlStringPair &p )
00092 {
00093   return qHash(p.first[0].unicode()) ^ 0x1477;
00094   
00095   // in case of doubt, use this:
00096   // return qHash(p.first)^qHash(p.second);
00097 }
00098 
00099 // ==================================================================
00100 //
00101 //         KoXmlPackedItem
00102 //
00103 // ==================================================================
00104 
00105 // 24 bytes on most system
00106 class KoXmlPackedItem
00107 {
00108 public:
00109   bool attr:1;
00110   KoXmlNode::NodeType type:3;
00111   
00112 #ifdef KOXML_COMPACT
00113   quint32 childStart:28;
00114 #else
00115   unsigned depth:28; 
00116 #endif
00117   
00118   unsigned nameIndex;
00119   unsigned nsURIIndex;
00120   QString value;
00121   
00122   // it is important NOT to have a copy constructor, so that growth is optimal
00123   // see http://doc.trolltech.com/4.2/containers.html#growth-strategies
00124 #if 0
00125   KoXmlPackedItem(): attr(false), type(KoXmlNode::NullNode), depth(0) {}
00126 #endif
00127 };
00128 
00129 // ==================================================================
00130 //
00131 //         KoXmlPackedDocument 
00132 //
00133 // ==================================================================
00134 
00135 typedef QVector<KoXmlPackedItem> KoXmlPackedGroup;
00136 
00137 // growth strategy: increase every (1 << GROUP_GROW_SHIFT) items
00138 #define GROUP_GROW_SHIFT 3
00139 #define GROUP_GROW_SIZE (1 << GROUP_GROW_SHIFT)
00140 
00141 class KoXmlPackedDocument
00142 {
00143 public:
00144   bool processNamespace;
00145 #ifdef KOXML_COMPACT
00146   // map given depth to the list of items
00147   QHash<int, KoXmlPackedGroup> groups; 
00148 #else
00149   QVector<KoXmlPackedItem> items;
00150 #endif
00151   
00152   QStringList stringList;
00153 
00154 private:
00155   QHash<QString, unsigned> stringHash;
00156   
00157   unsigned cacheString( const QString& str )
00158   {
00159     if( str.isEmpty() )
00160       return 0;
00161       
00162     const unsigned& ii = stringHash[str];
00163     if(ii > 0)
00164       return ii;
00165     
00166     // not yet declared, so we add it
00167     unsigned i = stringList.count();
00168     stringList.append( str );
00169     stringHash.insert( str, i );
00170   
00171     return i;
00172   }
00173   
00174   QHash<QString, unsigned> valueHash;
00175   QStringList valueList;
00176   
00177   QString cacheValue( const QString& value )
00178   {
00179     if( value.isEmpty() )
00180       return 0;
00181       
00182     const unsigned& ii = valueHash[value];
00183     if(ii > 0)
00184       return valueList[ii];
00185     
00186     // not yet declared, so we add it
00187     unsigned i = valueList.count();
00188     valueList.append( value );
00189     valueHash.insert( value, i );
00190   
00191     return valueList[i];
00192   }
00193   
00194 #ifdef KOXML_COMPACT
00195 public:
00196   KoXmlPackedItem& itemAt( unsigned depth, unsigned index )
00197   {
00198     KoXmlPackedGroup& group = groups[depth];
00199     return group[index];
00200   }
00201     
00202   unsigned itemCount( unsigned depth )
00203   {
00204     const KoXmlPackedGroup& group = groups[depth];
00205     return group.count();
00206   }
00207     
00208     /*
00209        NOTE:
00210           Function clear, newItem, addElement, addAttribute, addText, 
00211           addCData, addProcessing are all related. These are all necessary
00212           for stateful manipulation of the document. See also the calls
00213           to these function from KoXmlHandler.
00214           
00215           The state itself is defined by the member variables 
00216           currentDepth and the groups (see above).
00217      */  
00218      
00219   unsigned currentDepth;
00220     
00221   KoXmlPackedItem& newItem( unsigned depth )
00222   {
00223     KoXmlPackedGroup& group = groups[depth];
00224     
00225     // reserve up front
00226     if( (groups.size() % GROUP_GROW_SIZE) == 0 )
00227       group.reserve( GROUP_GROW_SIZE * (1+(groups.size() >> GROUP_GROW_SHIFT)) );
00228     group.resize( group.count()+1 );
00229     
00230     // this is necessary, because intentionally we don't want to have 
00231     // a constructor for KoXmlPackedItem
00232     KoXmlPackedItem& item = group[group.count()-1];
00233     item.attr = false;
00234     item.type = KoXmlNode::NullNode;
00235     item.nameIndex = 0;
00236     item.nsURIIndex = 0;
00237     item.childStart = itemCount( depth + 1);
00238     
00239     return item;
00240   }
00241     
00242   void clear()
00243   {
00244     currentDepth = 0;
00245     stringHash.clear();
00246     stringList.clear();
00247     valueHash.clear();
00248     valueList.clear();
00249     groups.clear();
00250     
00251     // reserve index #0
00252     cacheString(QString());
00253     
00254     // first node is root
00255     KoXmlPackedItem& rootItem = newItem(0);
00256     rootItem.type = KoXmlNode::DocumentNode;
00257   }
00258     
00259   void finish()
00260   {
00261     // won't be needed anymore
00262     stringHash.clear();
00263     valueHash.clear();
00264     valueList.clear();
00265     
00266     // optimize, see documentation on QVector::squeeze
00267     for(int d = 0; d < groups.count(); d++)
00268     {
00269       KoXmlPackedGroup& group = groups[d];
00270       group.squeeze();
00271     }  
00272   }
00273     
00274   // in case namespace processing, 'name' contains the prefix already
00275   void addElement( const QString& name, const QString& nsURI )
00276   {
00277     KoXmlPackedItem& item = newItem(currentDepth+1); 
00278     item.type = KoXmlNode::ElementNode;
00279     item.nameIndex = cacheString(name);
00280     item.nsURIIndex = cacheString(nsURI);
00281 
00282     currentDepth++;
00283   }
00284     
00285   void closeElement()
00286   {
00287     currentDepth--;
00288   }
00289     
00290   void addAttribute( const QString& name, const QString& nsURI, const QString& value )
00291   {
00292     KoXmlPackedItem& item = newItem(currentDepth+1); 
00293     item.attr = true;
00294     item.nameIndex = cacheString(name);
00295     item.nsURIIndex = cacheString(nsURI);
00296     //item.value = cacheValue( value );
00297     item.value = value;
00298   }
00299 
00300   void addText( const QString& text )
00301   {
00302     KoXmlPackedItem& item = newItem(currentDepth+1); 
00303     item.type = KoXmlNode::TextNode;
00304     item.value = text;
00305   }
00306     
00307   void addCData( const QString& text )
00308   {
00309     KoXmlPackedItem& item = newItem(currentDepth+1); 
00310     item.type = KoXmlNode::CDATASectionNode;
00311     item.value = text;
00312   }
00313     
00314   void addProcessingInstruction()
00315   {
00316     KoXmlPackedItem& item = newItem(currentDepth+1); 
00317     item.type = KoXmlNode::ProcessingInstructionNode;
00318   }  
00319       
00320 public:
00321   KoXmlPackedDocument(): processNamespace(false), currentDepth(0)
00322   {
00323     clear();
00324   }
00325   
00326 #else  
00327   
00328 private:  
00329   unsigned elementDepth;
00330 
00331 public:
00332   
00333   KoXmlPackedItem& newItem()
00334   {
00335     unsigned count = items.count() + 512;
00336     count = 1024 * (count >> 10);
00337     items.reserve(count);
00338     
00339     items.resize( items.count() + 1 );
00340     
00341     // this is necessary, because intentionally we don't want to have 
00342     // a constructor for KoXmlPackedItem
00343     KoXmlPackedItem& item = items[items.count()-1];
00344     item.attr = false;
00345     item.type = KoXmlNode::NullNode;
00346     item.nameIndex = 0;
00347     item.nsURIIndex = 0;
00348     item.depth = 0;
00349     
00350     return item;
00351   }
00352         
00353   void addElement( const QString& name, const QString& nsURI )
00354   {
00355     // we are going one level deeper
00356     elementDepth++;
00357       
00358     KoXmlPackedItem& item = newItem();
00359     
00360     item.attr = false;
00361     item.type = KoXmlNode::ElementNode;
00362     item.depth = elementDepth;
00363     item.nameIndex = cacheString( name );
00364     item.nsURIIndex = cacheString( nsURI );
00365   }
00366         
00367   void closeElement()
00368   {
00369     // we are going up one level
00370     elementDepth--;
00371   }
00372         
00373   void addAttribute( const QString& name, const QString& nsURI, const QString& value )
00374   {
00375     KoXmlPackedItem& item = newItem();
00376   
00377     item.attr = true;
00378     item.type = KoXmlNode::NullNode;
00379     item.depth = elementDepth;
00380     item.nameIndex = cacheString( name );
00381     item.nsURIIndex = cacheString( nsURI );
00382     //item.value = cacheValue( value );
00383     item.value = value;
00384   }
00385 
00386   void addText( const QString& str )
00387   {
00388     KoXmlPackedItem& item = newItem();
00389 
00390     item.attr = false;
00391     item.type = KoXmlNode::TextNode;
00392     item.depth = elementDepth + 1;
00393     item.nameIndex = 0;
00394     item.nsURIIndex = 0;
00395     item.value = str;
00396   }
00397         
00398   void addCData( const QString& str )
00399   {
00400     KoXmlPackedItem& item = newItem();
00401 
00402     item.attr = false;
00403     item.type = KoXmlNode::CDATASectionNode;
00404     item.depth = elementDepth + 1;
00405     item.nameIndex = 0;
00406     item.nsURIIndex = 0;
00407     item.value = str;
00408   }
00409         
00410   void addProcessingInstruction()
00411   {
00412     KoXmlPackedItem& item = newItem();
00413   
00414     item.attr = false;
00415     item.type = KoXmlNode::ProcessingInstructionNode;
00416     item.depth = elementDepth + 1;
00417     item.nameIndex = 0;
00418     item.nsURIIndex = 0;
00419     item.value.clear();
00420   }  
00421           
00422   void clear()
00423   {
00424     stringList.clear();
00425     stringHash.clear();
00426     valueHash.clear();
00427     valueList.clear();
00428     items.clear();
00429     elementDepth = 0;
00430     
00431     // reserve index #0
00432     cacheString( "." );
00433     
00434     KoXmlPackedItem& rootItem = newItem();
00435     rootItem.attr = false;
00436     rootItem.type = KoXmlNode::DocumentNode;
00437     rootItem.depth = 0;
00438     rootItem.nsURIIndex = 0;
00439     rootItem.nameIndex = 0;
00440   }  
00441         
00442   void finish()
00443   {
00444     stringHash.clear();
00445     valueList.clear();
00446     valueHash.clear();
00447     items.squeeze();
00448   }  
00449         
00450   KoXmlPackedDocument(): processNamespace(false), elementDepth(0)
00451   {
00452   }
00453   
00454 #endif
00455 
00456 };
00457 
00458 // ==================================================================
00459 //
00460 //         KoXmlHandler 
00461 //
00462 // ==================================================================
00463 
00464 class KoXmlHandler : public QXmlDefaultHandler
00465 {
00466 public:
00467   KoXmlHandler( KoXmlPackedDocument* doc );
00468   ~KoXmlHandler();
00469 
00470   // content handler
00471   bool startDocument();
00472   bool endDocument();
00473 
00474   bool startElement( const QString& nsURI, const QString& localName, 
00475     const QString& qName, const QXmlAttributes& atts );
00476   bool endElement( const QString& nsURI, const QString& localName, 
00477     const QString& qName );
00478 
00479   bool characters( const QString& ch );
00480   bool processingInstruction( const QString& target, const QString& data );
00481   bool skippedEntity( const QString& name );
00482 
00483   // lexical handler
00484   bool startCDATA();
00485   bool endCDATA();
00486   bool startEntity( const QString & );
00487   bool endEntity( const QString & );
00488   bool startDTD( const QString& name, const QString& publicId, 
00489     const QString& systemId );
00490   bool comment( const QString& ch );
00491 
00492   // decl handler
00493   bool externalEntityDecl( const QString &name, const QString &publicId, 
00494     const QString &systemId ) ;
00495 
00496   // DTD handler
00497   bool notationDecl( const QString & name, const QString & publicId, 
00498     const QString & systemId );
00499   bool unparsedEntityDecl( const QString &name, const QString &publicId, 
00500     const QString &systemId, const QString &notationName ) ;
00501 
00502   // error handler
00503   bool fatalError( const QXmlParseException& exception );
00504 
00505   QString errorMsg;
00506   int errorLine;
00507   int errorColumn;
00508 
00509 private:
00510   bool processNamespace;
00511   QString entityName;
00512   bool cdata;
00513 
00514   KoXmlPackedDocument* document;
00515 };
00516 
00517 
00518 KoXmlHandler::KoXmlHandler( KoXmlPackedDocument* doc ): 
00519 QXmlDefaultHandler()
00520 {
00521   document = doc;
00522   processNamespace = doc->processNamespace;
00523 
00524   cdata = false;
00525   entityName = QString();
00526 
00527   errorMsg = QString();
00528   errorLine = 0;
00529   errorColumn = 0;
00530 }
00531 
00532 KoXmlHandler::~KoXmlHandler()
00533 {
00534 }
00535 
00536 
00537 bool KoXmlHandler::startDocument()
00538 {
00539   // just for sanity
00540   cdata = false;
00541   entityName = QString();
00542 
00543   errorMsg = QString();
00544   errorLine = 0;
00545   errorColumn = 0;
00546 
00547   document->clear();
00548   return true;
00549 }
00550 
00551 bool KoXmlHandler::endDocument()
00552 {
00553   document->finish();
00554   return true;
00555 }
00556 
00557 bool KoXmlHandler::startDTD( const QString& name, const QString& publicId, 
00558 const QString& systemId )
00559 {
00560   Q_UNUSED( name );
00561   Q_UNUSED( publicId );
00562   Q_UNUSED( systemId );
00563 
00564   // we skip DTD
00565   return true;
00566 }
00567 
00568 bool KoXmlHandler::startElement( const QString& nsURI, const QString& localName, 
00569 const QString& name, const QXmlAttributes& atts )
00570 {
00571   Q_UNUSED( localName );
00572   
00573   document->addElement( name, nsURI );
00574 
00575   // add all attributes
00576   for( int c=0; c<atts.length(); c++ )
00577     document->addAttribute( atts.qName(c), atts.uri(c), atts.value(c) );
00578   
00579   return true;
00580 }
00581 
00582 bool KoXmlHandler::endElement( const QString& nsURI, const QString& localName, 
00583 const QString& qName )
00584 {
00585   Q_UNUSED( nsURI );
00586   Q_UNUSED( localName );
00587   Q_UNUSED( qName );
00588   
00589   document->closeElement();
00590   
00591   return true;
00592 }
00593 
00594 bool KoXmlHandler::characters( const QString& str )
00595 {
00596   // are we inside entity ?
00597   if( !entityName.isEmpty() )
00598   {
00599     // we do not handle entity but need to keep track of it
00600     // because we want to skip it alltogether
00601     return true;
00602   }
00603 
00604   // add a new text or CDATA 
00605   if( cdata )
00606     document->addCData( str );
00607   else
00608     document->addText( str );  
00609 
00610   return true;
00611 }
00612 
00613 bool KoXmlHandler::processingInstruction( const QString& target, 
00614 const QString& data )
00615 {
00616   Q_UNUSED( target );
00617   Q_UNUSED( data );
00618 
00619   document->addProcessingInstruction();
00620 
00621   return true;
00622 }
00623 
00624 bool KoXmlHandler::skippedEntity( const QString& name )
00625 {
00626   Q_UNUSED( name );
00627 
00628   // we skip entity
00629   return true;
00630 }
00631 
00632 bool KoXmlHandler::startCDATA()
00633 {
00634   cdata = true;
00635   return true;
00636 }
00637 
00638 bool KoXmlHandler::endCDATA()
00639 {
00640   cdata = false;
00641   return true;
00642 }
00643 
00644 bool KoXmlHandler::startEntity( const QString& name )
00645 {
00646   entityName = name;
00647   return true;
00648 }
00649 
00650 bool KoXmlHandler::endEntity( const QString& name )
00651 {
00652   Q_UNUSED( name );
00653   entityName.clear();
00654   return true;
00655 }
00656 
00657 bool KoXmlHandler::comment( const QString& comment )
00658 {
00659   Q_UNUSED( comment );
00660 
00661   // we skip comment
00662   return true;
00663 }
00664 
00665 bool KoXmlHandler::unparsedEntityDecl( const QString &name, 
00666 const QString &publicId, const QString &systemId, const QString &notationName )
00667 {
00668   Q_UNUSED( name );
00669   Q_UNUSED( publicId );
00670   Q_UNUSED( systemId );
00671   Q_UNUSED( notationName );
00672 
00673   // we skip entity
00674   return true;
00675 }
00676 
00677 bool KoXmlHandler::externalEntityDecl( const QString &name, 
00678 const QString &publicId, const QString &systemId )
00679 {
00680   Q_UNUSED( name );
00681   Q_UNUSED( publicId );
00682   Q_UNUSED( systemId );
00683 
00684   // we skip entity
00685   return true;
00686 }
00687 
00688 bool KoXmlHandler::notationDecl( const QString & name, 
00689 const QString & publicId, const QString & systemId )
00690 {
00691   Q_UNUSED( name );
00692   Q_UNUSED( publicId );
00693   Q_UNUSED( systemId );
00694 
00695   // we skip notation node
00696   return true;
00697 }
00698 
00699 bool KoXmlHandler::fatalError( const QXmlParseException& exception )
00700 {
00701   errorMsg = exception.message();
00702   errorLine =  exception.lineNumber();
00703   errorColumn =  exception.columnNumber();
00704   return QXmlDefaultHandler::fatalError( exception );
00705 }
00706 
00707 
00708 // ==================================================================
00709 //
00710 //         KoXmlNodeData 
00711 //
00712 // ==================================================================
00713 
00714 class KoXmlNodeData
00715 {
00716 public:
00717 
00718   KoXmlNodeData();
00719   ~KoXmlNodeData();
00720 
00721 #ifdef KOXML_COMPACT
00722   unsigned nodeDepth;
00723 #endif
00724 
00725   // generic properties
00726   KoXmlNode::NodeType nodeType;
00727   QString tagName;
00728   QString namespaceURI;
00729   QString prefix;
00730   QString localName;
00731 
00732   // reference counting
00733   unsigned long count;
00734   void ref() { count++; }
00735   void unref() { if( this == &null) return; if( !--count ) delete this; }
00736 
00737   // type information
00738   bool emptyDocument;
00739   QString nodeName() const;
00740 
00741   // for tree and linked-list
00742   KoXmlNodeData* parent;
00743   KoXmlNodeData* prev;
00744   KoXmlNodeData* next;
00745   KoXmlNodeData* first;
00746   KoXmlNodeData* last;
00747 
00748   QString text();
00749 
00750   // node manipulation
00751   void appendChild( KoXmlNodeData* child );
00752   void clear();
00753 
00754   // attributes
00755   inline void setAttribute( const QString& name, const QString& value );
00756   inline QString attribute( const QString& name, const QString& def ) const;
00757   inline bool hasAttribute( const QString& name ) const;
00758   inline void setAttributeNS( const QString& nsURI, const QString& name, const QString& value );
00759   inline QString attributeNS( const QString& nsURI, const QString& name, const QString& def ) const;
00760   inline bool hasAttributeNS( const QString& nsURI, const QString& name ) const;
00761   inline void clearAttributes();
00762 
00763   // for text and CDATA
00764   QString data() const;
00765   void setData( const QString& data );
00766 
00767   // reference from within the packed doc
00768   KoXmlPackedDocument* packedDoc;
00769   unsigned long nodeIndex;
00770 
00771   // for document node
00772   bool setContent( QXmlInputSource* source, QXmlReader* reader, 
00773     QString* errorMsg = 0, int* errorLine = 0, int* errorColumn = 0 );
00774   bool setContent( QXmlInputSource* source, bool namespaceProcessing, 
00775     QString* errorMsg = 0, int* errorLine = 0, int* errorColumn = 0 );
00776 
00777   // used when doing on-demand (re)parse
00778   bool loaded;
00779   void loadChildren( int depth=1 );
00780   void unloadChildren();
00781   
00782   void dump();
00783   
00784   static KoXmlNodeData null;
00785   
00786   // compatibility
00787   QDomNode asQDomNode( QDomDocument ownerDoc ) const;
00788 
00789 private:
00790   QHash<QString,QString> attr;  
00791   QHash<KoXmlStringPair,QString> attrNS;
00792   QString textData;
00793   friend class KoXmlHandler;
00794   friend class KoXmlElement;
00795 };
00796 
00797 KoXmlNodeData KoXmlNodeData::null;
00798 
00799 KoXmlNodeData::KoXmlNodeData()
00800 {
00801 #ifdef KOXML_COMPACT
00802   nodeDepth = 0;
00803 #endif
00804   nodeType = KoXmlNode::NullNode;
00805 
00806   tagName = QString();
00807   prefix = QString();
00808   localName = QString();
00809   namespaceURI = QString();
00810   textData = QString();
00811 
00812   count = 1;
00813   parent = 0;
00814   prev = next = 0;
00815   first = last = 0;
00816 
00817   packedDoc = 0;
00818   nodeIndex = 0;
00819   
00820   emptyDocument = true;
00821   
00822   loaded = false;
00823 }
00824 
00825 KoXmlNodeData::~KoXmlNodeData()
00826 { 
00827   clear();
00828 }
00829 
00830 void KoXmlNodeData::clear()
00831 {
00832   if( first )
00833   for( KoXmlNodeData* node = first; node ; )
00834   {
00835     KoXmlNodeData* next = node->next;
00836     node->unref();
00837     node = next;
00838   }
00839 
00840   // only document can delete these
00841   // normal nodes don't "own" them
00842   if(nodeType == KoXmlNode::DocumentNode)
00843     delete packedDoc;
00844 
00845   nodeType = KoXmlNode::NullNode;
00846   tagName.clear();
00847   prefix.clear();
00848   namespaceURI.clear();
00849   textData.clear();
00850   packedDoc = 0;
00851 
00852   attr.clear();
00853   attrNS.clear();
00854 
00855   parent = 0;
00856   prev = next = 0;
00857   first = last = 0;
00858   
00859   loaded = false;
00860 }
00861 
00862 QString KoXmlNodeData::text()
00863 {
00864   QString t;
00865 
00866   loadChildren();
00867 
00868   KoXmlNodeData* node = first;
00869   while ( node ) 
00870   {
00871     switch( node->nodeType )
00872     {
00873       case KoXmlNode::ElementNode: 
00874         t += node->text(); break;
00875       case KoXmlNode::TextNode:
00876         t += node->data(); break;
00877       case KoXmlNode::CDATASectionNode:
00878         t += node->data(); break;
00879       default: break;
00880     }
00881     node = node->next;
00882   }
00883 
00884   return t;
00885 }
00886 
00887 QString KoXmlNodeData::nodeName() const
00888 {
00889   QString n;
00890 
00891   switch( nodeType )
00892   {
00893     case KoXmlNode::ElementNode: 
00894       n = tagName;
00895       if (!prefix.isEmpty()) n.prepend(':').prepend(prefix); break;
00896     case KoXmlNode::TextNode: return QLatin1String("#text");
00897     case KoXmlNode::CDATASectionNode: return QLatin1String("#cdata-section");
00898     case KoXmlNode::DocumentNode: return QLatin1String("#document");
00899     default: break;
00900   }
00901 
00902   return n;
00903 }
00904 
00905 void KoXmlNodeData::appendChild( KoXmlNodeData* node )
00906 {
00907   node->parent = this;
00908   if( !last )
00909     first = last = node;
00910   else
00911   {
00912     last->next = node;
00913     node->prev = last;
00914     node->next = 0;
00915     last = node;
00916   }
00917 }
00918 
00919 void KoXmlNodeData::setAttribute( const QString& name, const QString& value )
00920 {
00921   attr[ name ] = value;
00922 }
00923 
00924 QString KoXmlNodeData::attribute( const QString& name, const QString& def ) const
00925 {
00926   if( attr.contains(name) )
00927     return attr[ name ];
00928   else
00929     return def;  
00930 }
00931 
00932 bool KoXmlNodeData::hasAttribute( const QString& name ) const
00933 {
00934   return attr.contains( name );
00935 }
00936 
00937 void KoXmlNodeData::setAttributeNS( const QString& nsURI, 
00938 const QString& name, const QString& value )
00939 {
00940   QString prefix;
00941   QString localName = name;
00942   int i = name.indexOf( ':' );
00943   if( i != -1 )
00944   {
00945     localName = name.mid( i + 1 );
00946     prefix = name.left( i );
00947   }
00948   
00949   if( prefix.isNull() ) return;
00950   
00951   KoXmlStringPair key( nsURI, localName );
00952   attrNS[ key ] = value;
00953 }
00954 
00955 QString KoXmlNodeData::attributeNS( const QString& nsURI, const QString& name, 
00956 const QString& def ) const
00957 {
00958   KoXmlStringPair key( nsURI, name );
00959   if( attrNS.contains( key ) )
00960     return attrNS[ key ];
00961   else
00962     return def;  
00963 }
00964 
00965 bool KoXmlNodeData::hasAttributeNS( const QString& nsURI, const QString& name ) const
00966 {
00967   KoXmlStringPair key( nsURI, name );
00968   return attrNS.contains( key );
00969 }
00970 
00971 void KoXmlNodeData::clearAttributes()
00972 {
00973   attr.clear();
00974   attrNS.clear();
00975 }
00976 
00977 QString KoXmlNodeData::data() const
00978 {
00979   return textData;
00980 }
00981 
00982 void KoXmlNodeData::setData( const QString& d )
00983 {
00984   textData = d;
00985 }
00986 
00987 bool KoXmlNodeData::setContent( QXmlInputSource* source, bool namespaceProcessing, 
00988 QString* errorMsg, int* errorLine, int* errorColumn )
00989 {
00990   QXmlSimpleReader reader;
00991   reader.setFeature(QLatin1String("http://xml.org/sax/features/namespaces"), namespaceProcessing);
00992   reader.setFeature(QLatin1String("http://xml.org/sax/features/namespace-prefixes"), !namespaceProcessing);
00993   reader.setFeature(QLatin1String("http://trolltech.com/xml/features/report-whitespace-only-CharData"), false);
00994 
00995   return setContent(source, &reader, errorMsg, errorLine, errorColumn);
00996 }
00997 
00998 bool KoXmlNodeData::setContent( QXmlInputSource* source, 
00999 QXmlReader* reader, QString* errorMsg, int* errorLine, int* errorColumn )
01000 {
01001   if( nodeType != KoXmlNode::DocumentNode )
01002     return false;
01003     
01004   clear();
01005   nodeType = KoXmlNode::DocumentNode;
01006 
01007   // sanity checks
01008   if( !source ) return false;
01009   if( !reader ) return false;
01010 
01011   delete packedDoc;
01012   packedDoc = new KoXmlPackedDocument;
01013   packedDoc->processNamespace = false;
01014 
01015   packedDoc->processNamespace = 
01016     reader->feature( "http://xml.org/sax/features/namespaces" ) && 
01017     !reader->feature( "http://xml.org/sax/features/namespace-prefixes" );
01018 
01019   KoXmlHandler handler( packedDoc );
01020   reader->setContentHandler( &handler );
01021   reader->setErrorHandler( &handler );
01022   reader->setLexicalHandler( &handler );
01023   reader->setDeclHandler( &handler );
01024   reader->setDTDHandler( &handler );
01025   
01026   bool result = reader->parse(source);
01027   if( !result ) 
01028   {
01029     // parsing error has occurred
01030     if( errorMsg ) *errorMsg = handler.errorMsg;
01031     if( errorLine ) *errorLine = handler.errorLine;
01032     if( errorColumn )  *errorColumn = handler.errorColumn;
01033     return false;
01034   }
01035   
01036   
01037   // initially load 
01038   loadChildren();
01039 
01040   return true;
01041 }
01042 
01043 #ifdef KOXML_COMPACT
01044 
01045 void KoXmlNodeData::loadChildren( int depth )
01046 {
01047   // sanity check
01048   if( !packedDoc ) return;
01049 
01050   // already loaded ?
01051   if( loaded && (depth<=1) ) return;
01052   
01053   // in case depth is different
01054   unloadChildren();
01055   
01056   
01057   KoXmlNodeData* lastDat = 0;
01058   const KoXmlPackedItem& self = packedDoc->itemAt( nodeDepth, nodeIndex );
01059   
01060   unsigned childStop = 0;
01061   if( nodeIndex == packedDoc->itemCount(nodeDepth)-1 )
01062     childStop = packedDoc->itemCount(nodeDepth+1);
01063   else  
01064   {
01065     const KoXmlPackedItem& next = packedDoc->itemAt( nodeDepth, nodeIndex+1 );
01066     childStop = next.childStart;
01067   }  
01068 
01069   for(unsigned i = self.childStart; i < childStop; i++ )
01070   {
01071     const KoXmlPackedItem& item = packedDoc->itemAt( nodeDepth + 1, i );
01072     bool textItem = (item.type==KoXmlNode::TextNode);
01073     textItem |= (item.type==KoXmlNode::CDATASectionNode);
01074     
01075     // attribute belongs to this node
01076     if(item.attr)
01077     {
01078       QString name = packedDoc->stringList[item.nameIndex];
01079       QString nsURI = packedDoc->stringList[item.nsURIIndex];
01080       QString value = item.value;
01081       
01082       QString prefix;
01083       
01084       QString qName; // with prefix
01085       QString localName;  // without prefix, i.e. local name
01086 
01087       localName = qName = name;
01088       int i = qName.indexOf( ':' );
01089       if( i != -1 ) prefix = qName.left( i );
01090       if( i != -1 ) localName = qName.mid( i + 1 );
01091 
01092       if(packedDoc->processNamespace)
01093       {
01094         setAttributeNS(nsURI, qName, value);
01095         setAttribute(localName, value);  
01096       }
01097       else
01098         setAttribute( qName, value );
01099     }
01100     else
01101     {
01102       QString name = packedDoc->stringList[item.nameIndex];
01103       QString nsURI = packedDoc->stringList[item.nsURIIndex];
01104       QString value = item.value;
01105     
01106       QString nodeName = name;
01107       QString localName;
01108       QString prefix;
01109       
01110       if(packedDoc->processNamespace)
01111       {
01112         localName = name;
01113         int di = name.indexOf( ':' );
01114         if( di != -1 )
01115         {
01116           localName = name.mid( di + 1 );
01117           prefix = name.left( di );
01118         }
01119         nodeName = localName;
01120       }
01121 
01122       // make a node out of this item
01123       KoXmlNodeData* dat = new KoXmlNodeData;
01124       dat->nodeIndex = i;
01125       dat->packedDoc = packedDoc;
01126       dat->nodeDepth = nodeDepth + 1;
01127       dat->nodeType = item.type;
01128       dat->tagName = nodeName;
01129       dat->localName = localName;
01130       dat->prefix = prefix;
01131       dat->namespaceURI = nsURI;
01132       dat->count = 1;
01133       dat->parent = this;
01134       dat->prev = lastDat;
01135       dat->next = 0;
01136       dat->first = 0;
01137       dat->last = 0;
01138       dat->loaded = false;        
01139       dat->textData = (textItem) ? value : QString();
01140         
01141       // adjust our linked-list
01142       first = (first) ? first : dat;
01143       last = dat;
01144       if(lastDat)
01145         lastDat->next = dat;
01146       lastDat = dat;
01147       
01148       // recursive
01149       if(depth > 1)
01150         dat->loadChildren(depth - 1);
01151     }
01152   }
01153   
01154   loaded = true;
01155 }
01156 
01157 #else
01158 
01159 void KoXmlNodeData::loadChildren( int depth )
01160 {
01161   // sanity check
01162   if( !packedDoc ) return;
01163 
01164   // already loaded ?
01165   if( loaded && (depth<=1) ) return;
01166   
01167   // cause we don't know how deep this node's children already loaded are
01168   unloadChildren();
01169   
01170   KoXmlNodeData* lastDat = 0;
01171   int nodeDepth = packedDoc->items[nodeIndex].depth;
01172 
01173   for(int i = nodeIndex + 1; i < packedDoc->items.count(); i++ )
01174   {
01175     KoXmlPackedItem& item = packedDoc->items[i];
01176     bool textItem = (item.type==KoXmlNode::TextNode);
01177     textItem |= (item.type==KoXmlNode::CDATASectionNode);
01178     
01179     // element already outside our depth
01180     if(!item.attr && (item.type == KoXmlNode::ElementNode))
01181     if(item.depth <= (unsigned)nodeDepth)
01182       break;
01183     
01184     // attribute belongs to this node
01185     if(item.attr && (item.depth == (unsigned)nodeDepth))
01186     {
01187       QString name = packedDoc->stringList[item.nameIndex];
01188       QString nsURI = packedDoc->stringList[item.nsURIIndex];
01189       QString value = item.value;
01190       
01191       QString prefix;
01192       
01193       QString qName; // with prefix
01194       QString localName;  // without prefix, i.e. local name
01195 
01196       localName = qName = name;
01197       int i = qName.indexOf( ':' );
01198       if( i != -1 ) prefix = qName.left( i );
01199       if( i != -1 ) localName = qName.mid( i + 1 );
01200 
01201       if(packedDoc->processNamespace)
01202       {
01203         setAttributeNS(nsURI, qName, value);
01204         setAttribute(localName, value);  
01205       }
01206       else
01207         setAttribute( name, value );
01208     }
01209     
01210       // the child node
01211     if(!item.attr)
01212     {
01213       bool instruction = (item.type == KoXmlNode::ProcessingInstructionNode);
01214       bool ok = (textItem||instruction)  ? (item.depth==(unsigned)nodeDepth) : (item.depth==(unsigned)nodeDepth+1);
01215       
01216       ok = (item.depth==(unsigned)nodeDepth+1);
01217         
01218       if(ok)
01219       {
01220         QString name = packedDoc->stringList[item.nameIndex];
01221         QString nsURI = packedDoc->stringList[item.nsURIIndex];
01222         QString value = item.value;
01223       
01224         QString nodeName = name;
01225         QString localName;
01226         QString prefix;
01227         
01228         if(packedDoc->processNamespace)
01229         {
01230           localName = name;
01231           int di = name.indexOf( ':' );
01232           if( di != -1 )
01233           {
01234             localName = name.mid( di + 1 );
01235             prefix = name.left( di );
01236           }
01237           nodeName = localName;
01238         }
01239 
01240         // make a node out of this item
01241         KoXmlNodeData* dat = new KoXmlNodeData;
01242         dat->nodeIndex = i;
01243         dat->packedDoc = packedDoc;
01244         dat->nodeType = item.type;
01245         dat->tagName = nodeName;
01246         dat->localName = localName;
01247         dat->prefix = prefix;
01248         dat->namespaceURI = nsURI;
01249         dat->count = 1;
01250         dat->parent = this;
01251         dat->prev = lastDat;
01252         dat->next = 0;
01253         dat->first = 0;
01254         dat->last = 0;
01255         dat->loaded = false;        
01256         dat->textData = (textItem) ? value : QString();
01257         
01258         // adjust our linked-list
01259         first = (first) ? first : dat;
01260         last = dat;
01261         if(lastDat)
01262           lastDat->next = dat;
01263         lastDat = dat;
01264         
01265         // recursive
01266         if(depth > 1)
01267           dat->loadChildren(depth - 1);
01268       }
01269   }
01270   }
01271   
01272   loaded = true;
01273 }
01274 #endif
01275 
01276 void KoXmlNodeData::unloadChildren()
01277 {
01278   // sanity check
01279   if( !packedDoc ) return;
01280 
01281   if( !loaded ) return;
01282   
01283   if( first )
01284   for( KoXmlNodeData* node = first; node ; )
01285   {
01286     KoXmlNodeData* next = node->next;
01287     node->unloadChildren();
01288     node->unref();
01289     node = next;
01290   }
01291 
01292   clearAttributes();
01293   loaded = false;
01294   first = last = 0;
01295 }
01296 
01297 #ifdef KOXML_COMPACT
01298 
01299 
01300 static QDomNode itemAsQDomNode( QDomDocument ownerDoc, KoXmlPackedDocument* packedDoc,
01301 unsigned nodeDepth, unsigned nodeIndex )
01302 {
01303   // sanity check
01304   if( !packedDoc )
01305     return QDomNode();
01306     
01307   const KoXmlPackedItem& self = packedDoc->itemAt( nodeDepth, nodeIndex );  
01308   
01309   unsigned childStop = 0;
01310   if( nodeIndex == packedDoc->itemCount(nodeDepth)-1 )
01311     childStop = packedDoc->itemCount(nodeDepth+1);
01312   else  
01313   {
01314     const KoXmlPackedItem& next = packedDoc->itemAt( nodeDepth, nodeIndex+1 );
01315     childStop = next.childStart;
01316   }  
01317   
01318   // nothing to do here
01319   if( self.type == KoXmlNode::NullNode )
01320     return QDomNode();
01321 
01322   // create the element properly
01323   if( self.type == KoXmlNode::ElementNode )
01324   {
01325     QDomElement element;
01326     
01327     QString name = packedDoc->stringList[self.nameIndex];
01328     QString nsURI = packedDoc->stringList[self.nsURIIndex];
01329     
01330     if( packedDoc->processNamespace )
01331       element = ownerDoc.createElementNS( nsURI, name );
01332     else
01333       element = ownerDoc.createElement( name );
01334 
01335     // check all subnodes for attributes
01336     for(unsigned i = self.childStart; i < childStop; i++ )
01337     {
01338       const KoXmlPackedItem& item = packedDoc->itemAt( nodeDepth+1, i );
01339       bool textItem = (item.type==KoXmlNode::TextNode);
01340       textItem |= (item.type==KoXmlNode::CDATASectionNode);
01341     
01342       // attribute belongs to this node
01343       if(item.attr)
01344       {
01345         QString name = packedDoc->stringList[item.nameIndex];
01346         QString nsURI = packedDoc->stringList[item.nsURIIndex];
01347         QString value = item.value;
01348         
01349         QString prefix;
01350         
01351         QString qName; // with prefix
01352         QString localName;  // without prefix, i.e. local name
01353   
01354         localName = qName = name;
01355         int i = qName.indexOf( ':' );
01356         if( i != -1 ) prefix = qName.left( i );
01357         if( i != -1 ) localName = qName.mid( i + 1 );
01358   
01359         if(packedDoc->processNamespace)
01360         {
01361           element.setAttributeNS(nsURI, qName, value);
01362           element.setAttribute(localName, value);  
01363         }
01364         else
01365           element.setAttribute( name, value );
01366       }
01367       else
01368       { 
01369         // add it recursively
01370         QDomNode childNode = itemAsQDomNode( ownerDoc, packedDoc, nodeDepth+1, i );
01371         element.appendChild( childNode );
01372       }  
01373     }
01374       
01375     return element;
01376   }
01377   
01378   // create the text node
01379   if( self.type == KoXmlNode::TextNode )
01380   {
01381     QString text = self.value;
01382     
01383     // FIXME: choose CDATA when the value contains special characters
01384     QDomText textNode = ownerDoc.createTextNode( text );
01385     return textNode;
01386   }
01387           
01388   // nothing matches? strange...    
01389   return QDomNode();
01390 }
01391 
01392 QDomNode KoXmlNodeData::asQDomNode( QDomDocument ownerDoc ) const
01393 {
01394   return itemAsQDomNode( ownerDoc, packedDoc, nodeDepth, nodeIndex );
01395 }
01396 
01397 #else
01398 
01399 static QDomNode itemAsQDomNode( QDomDocument ownerDoc, KoXmlPackedDocument* packedDoc,
01400 unsigned nodeIndex )
01401 {
01402   // sanity check
01403   if( !packedDoc )
01404     return QDomNode();
01405     
01406   KoXmlPackedItem& item = packedDoc->items[nodeIndex];  
01407     
01408   // nothing to do here
01409   if( item.type == KoXmlNode::NullNode )
01410     return QDomNode();
01411 
01412   // create the element properly
01413   if( item.type == KoXmlNode::ElementNode )
01414   {
01415     QDomElement element;
01416     
01417     QString name = packedDoc->stringList[item.nameIndex];
01418     QString nsURI = packedDoc->stringList[item.nsURIIndex];
01419     
01420     if( packedDoc->processNamespace )
01421       element = ownerDoc.createElementNS( nsURI, name );
01422     else
01423       element = ownerDoc.createElement( name );
01424 
01425     // check all subnodes for attributes
01426     int nodeDepth = item.depth;
01427     for(int i = nodeIndex + 1; i < packedDoc->items.count(); i++ )
01428     {
01429       KoXmlPackedItem& item = packedDoc->items[i];
01430       bool textItem = (item.type==KoXmlNode::TextNode);
01431       textItem |= (item.type==KoXmlNode::CDATASectionNode);
01432     
01433       // element already outside our depth
01434       if(!item.attr && (item.type == KoXmlNode::ElementNode))
01435       if(item.depth <= (unsigned)nodeDepth)
01436         break;
01437     
01438       // attribute belongs to this node
01439       if(item.attr && (item.depth == (unsigned)nodeDepth))
01440       {
01441         QString name = packedDoc->stringList[item.nameIndex];
01442         QString nsURI = packedDoc->stringList[item.nsURIIndex];
01443         QString value = item.value;
01444         QString prefix;
01445       
01446         QString qName; // with prefix
01447         QString localName;  // without prefix, i.e. local name
01448 
01449         localName = qName = name;
01450         int i = qName.indexOf( ':' );
01451         if( i != -1 ) prefix = qName.left( i );
01452         if( i != -1 ) localName = qName.mid( i + 1 );
01453 
01454         if(packedDoc->processNamespace)
01455         {
01456           element.setAttributeNS(nsURI, qName, value);
01457           element.setAttribute(localName, value);  
01458         }
01459         else
01460           element.setAttribute( name, value );
01461       }
01462       
01463       // direct child of this node
01464       if( !item.attr && (item.depth == (unsigned)nodeDepth+1) )
01465       { 
01466         // add it recursively
01467         QDomNode childNode = itemAsQDomNode( ownerDoc, packedDoc, i );
01468         element.appendChild( childNode );
01469       }  
01470     }
01471       
01472     return element;
01473   }
01474   
01475   // create the text node
01476   if( item.type == KoXmlNode::TextNode )
01477   {
01478     QString text = item.value;
01479     // FIXME: choose CDATA when the value contains special characters
01480     QDomText textNode = ownerDoc.createTextNode( text );
01481     return textNode;
01482   }
01483           
01484   // nothing matches? strange...    
01485   return QDomNode();
01486 }
01487 
01488 QDomNode KoXmlNodeData::asQDomNode( QDomDocument ownerDoc ) const
01489 {
01490   return itemAsQDomNode( ownerDoc, packedDoc, nodeIndex );
01491 }
01492 
01493 #endif
01494 
01495 void KoXmlNodeData::dump()
01496 {
01497   printf("NodeData %p\n", this);
01498 
01499   printf("  nodeIndex: %d\n", (int)nodeIndex);  
01500   printf("  packedDoc: %p\n", packedDoc);
01501   
01502   printf("  nodeType : %d\n", (int)nodeType);
01503   printf("  tagName: %s\n", qPrintable( tagName ) );
01504   printf("  namespaceURI: %s\n", qPrintable( namespaceURI ) );
01505   printf("  prefix: %s\n", qPrintable( prefix ) );
01506   printf("  localName: %s\n", qPrintable( localName ) );
01507   
01508   printf("  parent : %p\n", parent);
01509   printf("  prev : %p\n", prev);
01510   printf("  next : %p\n", next);
01511   printf("  first : %p\n", first);
01512   printf("  last : %p\n", last);
01513   
01514   printf("  count: %ld\n", count);
01515   
01516   if(loaded)
01517   printf("  loaded: TRUE\n");
01518   else
01519   printf("  loaded: FALSE\n");
01520 }
01521 
01522 // ==================================================================
01523 //
01524 //         KoXmlNode 
01525 //
01526 // ==================================================================
01527 
01528 // Creates a null node
01529 KoXmlNode::KoXmlNode()
01530 {
01531   d = &KoXmlNodeData::null;
01532 }
01533 
01534 // Destroys this node
01535 KoXmlNode::~KoXmlNode()
01536 {
01537   if( d )
01538     if( d != &KoXmlNodeData::null )
01539       d->unref();
01540 
01541   d = 0;
01542 }
01543 
01544 // Creates a copy of another node
01545 KoXmlNode::KoXmlNode( const KoXmlNode& node )
01546 {
01547   d = node.d;
01548   d->ref();
01549 }
01550 
01551 // Creates a node for specific implementation
01552 KoXmlNode::KoXmlNode( KoXmlNodeData* data )
01553 {
01554   d = data;
01555   data->ref();
01556 }
01557 
01558 // Creates a shallow copy of another node
01559 KoXmlNode& KoXmlNode::operator=( const KoXmlNode& node )
01560 {
01561   d->unref();
01562   d = node.d;
01563   d->ref();
01564   return *this;
01565 }
01566 
01567 // Note: two null nodes are always equal
01568 bool KoXmlNode::operator==( const KoXmlNode& node ) const
01569 {
01570   if( isNull() && node.isNull() ) return true;
01571   return( d==node.d );
01572 }
01573 
01574 // Note: two null nodes are always equal
01575 bool KoXmlNode::operator!=( const KoXmlNode& node ) const
01576 {
01577   if( isNull() && !node.isNull() ) return true;
01578   if( !isNull() && node.isNull() ) return true;
01579   if( isNull() && node.isNull() ) return false;
01580   return( d!=node.d );
01581 }
01582 
01583 KoXmlNode::NodeType KoXmlNode::nodeType() const
01584 {
01585   return d->nodeType;
01586 }
01587 
01588 bool KoXmlNode::isNull() const
01589 {
01590   return d->nodeType == NullNode;
01591 }
01592 
01593 bool KoXmlNode::isElement() const
01594 {
01595   return d->nodeType == ElementNode;
01596 }
01597 
01598 bool KoXmlNode::isText() const
01599 {
01600   return (d->nodeType == TextNode) || isCDATASection();
01601 }
01602 
01603 bool KoXmlNode::isCDATASection() const
01604 {
01605   return d->nodeType == CDATASectionNode;
01606 }
01607 
01608 bool KoXmlNode::isDocument() const
01609 {
01610   return d->nodeType == DocumentNode;
01611 }
01612 
01613 void KoXmlNode::clear()
01614 {
01615   d->unref();
01616   d = new KoXmlNodeData;
01617 }
01618 
01619 QString KoXmlNode::nodeName() const
01620 {
01621   return d->nodeName();
01622 }
01623 
01624 QString KoXmlNode::prefix() const
01625 {
01626   return isElement() ? d->prefix : QString();
01627 }
01628 
01629 QString KoXmlNode::namespaceURI() const
01630 {
01631   return isElement() ? d->namespaceURI : QString();
01632 }
01633 
01634 QString KoXmlNode::localName() const
01635 {
01636   return isElement() ? d->localName : QString();
01637 }
01638 
01639 KoXmlDocument KoXmlNode::ownerDocument() const
01640 {
01641   KoXmlNodeData* node = d; 
01642   while( node->parent ) node = node->parent;
01643 
01644   return KoXmlDocument( node );
01645 }
01646 
01647 KoXmlNode KoXmlNode::parentNode() const
01648 {
01649   return d->parent ? KoXmlNode( d->parent ) : KoXmlNode();
01650 }
01651 
01652 bool KoXmlNode::hasChildNodes() const
01653 {
01654   if( isText() )
01655     return false;
01656   
01657   if( !d->loaded)  
01658     d->loadChildren();
01659 
01660   return d->first!=0 ;
01661 }
01662 
01663 int KoXmlNode::childNodesCount() const
01664 {
01665   if( isText() )
01666     return 0;
01667     
01668   if( !d->loaded)  
01669     d->loadChildren();
01670 
01671   KoXmlNodeData* node = d->first;
01672   int count = 0;
01673   while ( node ) 
01674   {
01675     count++;
01676     node = node->next;
01677   }
01678   
01679   return count;
01680 }
01681 
01682 KoXmlNode KoXmlNode::firstChild() const
01683 {
01684   if( !d->loaded)  
01685     d->loadChildren();
01686   return d->first ? KoXmlNode( d->first ) : KoXmlNode();
01687 }
01688 
01689 KoXmlNode KoXmlNode::lastChild() const
01690 {
01691   if( !d->loaded)  
01692     d->loadChildren();
01693   return d->last ? KoXmlNode( d->last ) : KoXmlNode();
01694 }
01695 
01696 KoXmlNode KoXmlNode::nextSibling() const
01697 {
01698   return d->next ? KoXmlNode( d->next ) : KoXmlNode();
01699 }
01700 
01701 KoXmlNode KoXmlNode::previousSibling() const
01702 {
01703   return d->prev ? KoXmlNode( d->prev ) : KoXmlNode();
01704 }
01705 
01706 KoXmlNode KoXmlNode::namedItem( const QString& name ) const
01707 {
01708   if( !d->loaded)  
01709     d->loadChildren();
01710 
01711   KoXmlNodeData* node = d->first;
01712   while ( node ) 
01713   {
01714     if( node->nodeName() == name )
01715       return KoXmlNode( node );
01716     node = node->next;
01717   }
01718 
01719   // not found
01720   return KoXmlNode();
01721 }
01722 
01723 KoXmlNode KoXmlNode::namedItemNS( const QString& nsURI, const QString& name ) const
01724 {
01725   if( !d->loaded)  
01726     d->loadChildren();
01727 
01728 
01729   KoXmlNodeData* node = d->first;
01730   while ( node ) 
01731   {
01732     if( !node->prefix.isNull() )
01733     if( node->namespaceURI == nsURI )
01734     if( node->localName == name )
01735       return KoXmlNode( node );
01736     node = node->next;
01737   }
01738 
01739   // not found
01740   return KoXmlNode();
01741 }
01742 
01743 KoXmlElement KoXmlNode::toElement() const
01744 {
01745   return isElement() ? KoXmlElement( d ) : KoXmlElement();
01746 }
01747 
01748 KoXmlText KoXmlNode::toText() const
01749 {
01750   return isText() ? KoXmlText( d ) : KoXmlText();
01751 }
01752 
01753 KoXmlCDATASection KoXmlNode::toCDATASection() const
01754 {
01755   return isCDATASection() ? KoXmlCDATASection( d ) : KoXmlCDATASection();
01756 }
01757 
01758 KoXmlDocument KoXmlNode::toDocument() const
01759 {
01760   if( isDocument() )
01761     return KoXmlDocument( d );
01762     
01763   KoXmlDocument newDocument;
01764   newDocument.d->emptyDocument = false;
01765   return newDocument;
01766 }
01767 
01768 void KoXmlNode::load( int depth )
01769 {
01770   d->loadChildren( depth );
01771 }
01772 
01773 void KoXmlNode::unload()
01774 {
01775   d->unloadChildren();
01776 }
01777 
01778 QDomNode KoXmlNode::asQDomNode( QDomDocument ownerDoc ) const
01779 {
01780   return d->asQDomNode( ownerDoc );
01781 }
01782 
01783 // ==================================================================
01784 //
01785 //         KoXmlElement 
01786 //
01787 // ==================================================================
01788 
01789 // Creates an empty element
01790 KoXmlElement::KoXmlElement(): KoXmlNode( new KoXmlNodeData )
01791 {
01792   // because referenced also once in KoXmlNode constructor
01793   d->unref(); 
01794 }
01795 
01796 KoXmlElement::~KoXmlElement()
01797 {
01798   if( d )
01799     if( d != &KoXmlNodeData::null )
01800       d->unref();
01801 
01802   d = 0;
01803 }
01804 
01805 // Creates a shallow copy of another element
01806 KoXmlElement::KoXmlElement( const KoXmlElement& element ): KoXmlNode( element.d )
01807 {
01808 }
01809 
01810 KoXmlElement::KoXmlElement( KoXmlNodeData* data ): KoXmlNode( data )
01811 {
01812 }
01813 
01814 // Copies another element
01815 KoXmlElement& KoXmlElement::operator=( const KoXmlElement& element )
01816 {
01817   KoXmlNode::operator=( element );
01818   return *this;
01819 }
01820 
01821 bool KoXmlElement::operator== ( const KoXmlElement& element ) const
01822 {
01823   if( isNull() || element.isNull() ) return false;
01824   return (d==element.d);
01825 }
01826 
01827 bool KoXmlElement::operator!= ( const KoXmlElement& element ) const
01828 {
01829   if( isNull() && element.isNull() ) return false;
01830   if( isNull() || element.isNull() ) return true;
01831   return (d!=element.d);
01832 }
01833 
01834 QString KoXmlElement::tagName() const
01835 {
01836   return isElement() ? ((KoXmlNodeData*)d)->tagName: QString();
01837 }
01838 
01839 QString KoXmlElement::text() const
01840 {
01841   return d->text();
01842 }
01843 
01844 QString KoXmlElement::attribute( const QString& name ) const
01845 {
01846   if( !isElement() )
01847     return QString();
01848     
01849   if( !d->loaded)  
01850     d->loadChildren();
01851 
01852   return d->attribute( name, QString() );
01853 }
01854 
01855 QString KoXmlElement::attribute( const QString& name, 
01856 const QString& defaultValue ) const
01857 {
01858   if( !isElement() )
01859     return defaultValue;
01860     
01861   if( !d->loaded)  
01862     d->loadChildren();
01863 
01864   return d->attribute( name, defaultValue );
01865 }
01866 
01867 QString KoXmlElement::attributeNS( const QString& namespaceURI, 
01868 const QString& localName, const QString& defaultValue ) const
01869 {
01870   if( !isElement() )
01871     return defaultValue;
01872     
01873   if( !d->loaded)  
01874     d->loadChildren();
01875 
01876   KoXmlStringPair key( namespaceURI, localName );
01877   if( d->attrNS.contains( key ) )
01878     return d->attrNS[ key ];
01879   else
01880     return defaultValue;  
01881 
01882 //  return d->attributeNS( namespaceURI, localName, defaultValue );
01883 }
01884 
01885 bool KoXmlElement::hasAttribute( const QString& name ) const
01886 {
01887   if( !d->loaded)  
01888     d->loadChildren();
01889 
01890   return isElement() ? d->hasAttribute( name ) : false;
01891 }
01892 
01893 bool KoXmlElement::hasAttributeNS( const QString& namespaceURI, 
01894 const QString& localName ) const
01895 {
01896   if( !d->loaded)  
01897     d->loadChildren();
01898 
01899   return isElement() ? d->hasAttributeNS( namespaceURI, localName ) : false;
01900 }
01901 
01902 // ==================================================================
01903 //
01904 //         KoXmlText
01905 //
01906 // ==================================================================
01907 
01908 KoXmlText::KoXmlText(): KoXmlNode( new KoXmlNodeData )
01909 {
01910   // because referenced also once in KoXmlNode constructor
01911   d->unref();
01912 }
01913 
01914 KoXmlText::~KoXmlText()
01915 {
01916   if( d )
01917     if( d != &KoXmlNodeData::null )
01918       d->unref();
01919 
01920   d = 0;
01921 }
01922 
01923 KoXmlText::KoXmlText( const KoXmlText& text ): KoXmlNode( text.d )
01924 {
01925 }
01926 
01927 KoXmlText::KoXmlText( KoXmlNodeData* data ): KoXmlNode( data )
01928 {
01929 }
01930 
01931 bool KoXmlText::isText() const
01932 {
01933   return true;
01934 }
01935 
01936 QString KoXmlText::data() const
01937 {
01938   return d->data();
01939 }
01940 
01941 KoXmlText& KoXmlText::operator=( const KoXmlText& element )
01942 {
01943   KoXmlNode::operator=( element );
01944   return *this;
01945 }
01946 
01947 // ==================================================================
01948 //
01949 //         KoXmlCDATASection
01950 //
01951 // ==================================================================
01952 
01953 KoXmlCDATASection::KoXmlCDATASection(): KoXmlText()
01954 {
01955   d->nodeType = KoXmlNode::CDATASectionNode;
01956 }
01957 
01958 KoXmlCDATASection::~KoXmlCDATASection()
01959 {
01960   d->unref();
01961   d = 0;
01962 }
01963 
01964 KoXmlCDATASection::KoXmlCDATASection( KoXmlNodeData* cdata ):
01965 KoXmlText( cdata )
01966 {
01967 }
01968 
01969 bool KoXmlCDATASection::isCDATASection() const
01970 {
01971   return true;
01972 }
01973 
01974 KoXmlCDATASection& KoXmlCDATASection::operator=( const KoXmlCDATASection& cdata )
01975 {
01976   KoXmlNode::operator=( cdata );
01977   return *this;
01978 }
01979 
01980 // ==================================================================
01981 //
01982 //         KoXmlDocument 
01983 //
01984 // ==================================================================
01985 
01986 KoXmlDocument::KoXmlDocument(): KoXmlNode()
01987 {
01988   d->emptyDocument = false;
01989 }
01990 
01991 KoXmlDocument::~KoXmlDocument()
01992 {
01993   if( d )
01994     if( d != &KoXmlNodeData::null )
01995       d->unref();
01996 
01997   d = 0;
01998 }
01999 
02000 KoXmlDocument::KoXmlDocument( KoXmlNodeData* data ): KoXmlNode( data )
02001 {
02002   d->emptyDocument = true;
02003 }
02004 
02005 // Creates a copy of another document
02006 KoXmlDocument::KoXmlDocument( const KoXmlDocument& doc ): KoXmlNode( doc.d )
02007 {
02008 }
02009 
02010 // Creates a shallow copy of another document
02011 KoXmlDocument& KoXmlDocument::operator=( const KoXmlDocument& doc )
02012 {
02013   KoXmlNode::operator=( doc );
02014   return *this;
02015 }
02016 
02017 // Checks if this document and doc are equals
02018 bool KoXmlDocument::operator==( const KoXmlDocument& doc ) const
02019 {
02020   return( d==doc.d );
02021 }
02022 
02023 // Checks if this document and doc are not equals
02024 bool KoXmlDocument::operator!=( const KoXmlDocument& doc ) const
02025 {
02026   return( d!=doc.d );
02027 }
02028 
02029 KoXmlElement KoXmlDocument::documentElement() const
02030 {
02031   d->loadChildren();
02032   
02033   for( KoXmlNodeData* node=d->first; node; )
02034   {
02035     if( node->nodeType==KoXmlNode::ElementNode )
02036       return KoXmlElement( node );
02037     else node = node->next;  
02038   }
02039   
02040   return KoXmlElement();
02041 }
02042 
02043 QString KoXmlDocument::nodeName() const
02044 {
02045   if( d->emptyDocument )
02046     return QLatin1String("#document");
02047   else   
02048     return QString();
02049 }
02050 
02051 void KoXmlDocument::clear()
02052 {
02053   KoXmlNode::clear();
02054   d->emptyDocument = false;
02055 }
02056 
02057 bool KoXmlDocument::setContent( QXmlInputSource *source, QXmlReader *reader, 
02058     QString* errorMsg, int* errorLine, int* errorColumn )
02059 {
02060   if( d->nodeType != KoXmlNode::DocumentNode ) 
02061   {
02062     d->unref();
02063     d = new KoXmlNodeData;
02064     d->nodeType = KoXmlNode::DocumentNode;
02065   }
02066 
02067   return d->setContent( source, reader, errorMsg, errorLine, errorColumn );
02068 }
02069 
02070 // no namespace processing
02071 bool KoXmlDocument::setContent( QIODevice* device, QString* errorMsg,
02072 int* errorLine, int* errorColumn )
02073 {
02074   return setContent( device, false, errorMsg, errorLine, errorColumn );
02075 }
02076 
02077 bool KoXmlDocument::setContent( QIODevice* device, bool namespaceProcessing, 
02078 QString* errorMsg, int* errorLine, int* errorColumn )
02079 {
02080   if( d->nodeType != KoXmlNode::DocumentNode ) 
02081   {
02082     d->unref();
02083     d = new KoXmlNodeData;
02084     d->nodeType = KoXmlNode::DocumentNode;
02085   }
02086 
02087   QXmlSimpleReader reader;
02088   reader.setFeature( "http://xml.org/sax/features/namespaces", namespaceProcessing );
02089   reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", !namespaceProcessing );
02090   reader.setFeature( "http://trolltech.com/xml/features/report-whitespace-only-CharData", false );
02091 
02092   // FIXME this hack is apparently private
02093   //reader.setUndefEntityInAttrHack(true);
02094 
02095   QXmlInputSource source( device );
02096   return d->setContent( &source, &reader, errorMsg, errorLine, errorColumn );
02097 }
02098 
02099 bool KoXmlDocument::setContent( const QByteArray& text, bool namespaceProcessing,
02100 QString *errorMsg, int *errorLine, int *errorColumn )
02101 {
02102   QBuffer buffer;
02103   buffer.setData( text );
02104   return setContent( &buffer, namespaceProcessing, errorMsg, errorLine, errorColumn );
02105 }
02106 
02107 bool KoXmlDocument::setContent(const QString& text, bool namespaceProcessing, 
02108 QString *errorMsg, int *errorLine, int *errorColumn)
02109 {
02110   if( d->nodeType != KoXmlNode::DocumentNode ) 
02111   {
02112     d->unref();
02113     d = new KoXmlNodeData;
02114     d->nodeType = KoXmlNode::DocumentNode;
02115   }
02116 
02117   QXmlInputSource source;
02118   source.setData(text);
02119   return d->setContent( &source, namespaceProcessing, errorMsg, errorLine, errorColumn );
02120 }
02121 
02122 bool KoXmlDocument::setContent(const QString& text,  
02123 QString *errorMsg, int *errorLine, int *errorColumn)
02124 {
02125   return setContent( text, false, errorMsg, errorLine, errorColumn );
02126 }
02127 
02128 #endif
02129 
02130 // ==================================================================
02131 //
02132 //         KoXmlInputSource 
02133 //
02134 // ==================================================================
02135 
02136 /*
02137   This is the size of buffer every time we read a chunk of data from the device.
02138 
02139   Note 1: maximum allocated space is thus 2*KOXML_BUFSIZE due to the
02140   stringData (a QString instance).
02141   TODO: use mmap to avoid double-buffering like this.
02142   
02143   Note 2: a much larger buffer won't speed up significantly. This is because
02144   the bottleneck is elsewhere, not here.
02145   
02146 */
02147 
02148 #define KOXML_BUFSIZE 16*1024  // should be adequate
02149 
02150 KoXmlInputSource::KoXmlInputSource(QIODevice *dev): QXmlInputSource(), 
02151 device( dev )
02152 {
02153   int mib = 106; // UTF-8
02154   decoder = QTextCodec::codecForMib( mib )->makeDecoder();     
02155 
02156   stringLength = 0;
02157   stringIndex = 0;
02158   buffer = new char[KOXML_BUFSIZE];
02159 
02160   reset();
02161 }
02162 
02163 KoXmlInputSource::~KoXmlInputSource()
02164 {
02165   delete decoder;
02166   delete [] buffer;
02167 }
02168 
02169 void KoXmlInputSource::setData(const QString& dat)
02170 {
02171   Q_UNUSED(dat);
02172 }
02173 
02174 void KoXmlInputSource::setData(const QByteArray& dat)
02175 {
02176   Q_UNUSED(dat);
02177 }
02178 
02179 void KoXmlInputSource::fetchData()
02180 {
02181 }
02182 
02183 QString KoXmlInputSource::data() const
02184 {
02185   return QString();
02186 }
02187 
02188 QChar KoXmlInputSource::next()
02189 {
02190   if(stringIndex >= stringLength)
02191   {
02192     // read more data first
02193     qint64 bytes = device->read( buffer, KOXML_BUFSIZE );
02194     if( bytes == 0 )
02195       return EndOfDocument;
02196 
02197     stringData = decoder->toUnicode( buffer, bytes );
02198     stringLength = stringData.length();
02199     stringIndex = 0;
02200   }
02201 
02202   return stringData[stringIndex++];
02203 }
02204 
02205 void KoXmlInputSource::reset()
02206 {
02207   device->seek(0);
02208 }
02209 
02210 QString KoXmlInputSource::fromRawData(const QByteArray &data, bool beginning)
02211 {
02212   Q_UNUSED( data );
02213   Q_UNUSED( beginning );
02214   return QString();
02215 }
02216 
02217 // ==================================================================
02218 //
02219 //         functions in KoXml namespace
02220 //
02221 // ==================================================================
02222 
02223 KoXmlElement KoXml::namedItemNS( const KoXmlNode& node, const char* nsURI, 
02224 const char* localName )
02225 {
02226 #ifdef KOXML_USE_QDOM
02227     // David's solution for namedItemNS, only for QDom stuff
02228     KoXmlNode n = node.firstChild();
02229     for ( ; !n.isNull(); n = n.nextSibling() ) {
02230         if ( n.isElement() && n.localName() == localName && 
02231             n.namespaceURI() == nsURI )
02232                return n.toElement();
02233     }
02234     return KoXmlElement();
02235 #else
02236   return node.namedItemNS( nsURI, localName).toElement();
02237 #endif
02238 }
02239 
02240 void KoXml::load( KoXmlNode& node, int depth )
02241 {
02242 #ifdef KOXML_USE_QDOM
02243   // do nothing, QDom has no on-demand loading
02244   Q_UNUSED( node );
02245   Q_UNUSED( depth );
02246 #else
02247   node.load( depth );
02248 #endif
02249 }
02250 
02251 
02252 void KoXml::unload( KoXmlNode& node )
02253 {
02254 #ifdef KOXML_USE_QDOM
02255   // do nothing, QDom has no on-demand unloading
02256   Q_UNUSED( node );
02257 #else
02258   node.unload();
02259 #endif
02260 }
02261 
02262 int KoXml::childNodesCount( const KoXmlNode& node )
02263 {
02264 #ifdef KOXML_USE_QDOM
02265   return node.childNodes().count();
02266 #else
02267   // compatibility function, because no need to implement
02268   // a class like QDomNodeList
02269   return node.childNodesCount();
02270 #endif  
02271 }
02272 
02273 QDomNode KoXml::asQDomNode( QDomDocument ownerDoc, const KoXmlNode& node )
02274 {
02275 #ifdef KOXML_USE_QDOM
02276   Q_UNUSED( ownerDoc );
02277   return node;
02278 #else
02279   return node.asQDomNode( ownerDoc );
02280 #endif
02281 }
02282 
02283 QDomElement KoXml::asQDomElement( QDomDocument ownerDoc, const KoXmlElement& element )
02284 {
02285   return KoXml::asQDomNode( ownerDoc, element ).toElement();
02286 }
02287 
02288 QDomDocument KoXml::asQDomDocument( QDomDocument ownerDoc, const KoXmlDocument& document )
02289 {
02290   return KoXml::asQDomNode( ownerDoc, document ).toDocument();
02291 }
02292 
02293 bool KoXml::setDocument( KoXmlDocument& doc, QIODevice* device,
02294 bool namespaceProcessing, QString* errorMsg, int* errorLine, 
02295 int* errorColumn )
02296 {
02297   QXmlSimpleReader reader;
02298   reader.setFeature(QLatin1String("http://xml.org/sax/features/namespaces"), namespaceProcessing);
02299   reader.setFeature(QLatin1String("http://xml.org/sax/features/namespace-prefixes"), !namespaceProcessing);
02300   reader.setFeature(QLatin1String("http://trolltech.com/xml/features/report-whitespace-only-CharData"), false);
02301   
02302   KoXmlInputSource* source = new KoXmlInputSource( device );
02303   bool result = doc.setContent( source, &reader, errorMsg, errorLine, errorColumn);
02304   delete source;
02305   return result;  
02306 }
02307 
02308 bool KoXml::setDocument( KoXmlDocument& doc, QIODevice* device,
02309 QXmlSimpleReader* reader, QString* errorMsg, int* errorLine, int* errorColumn )
02310 {
02311   KoXmlInputSource* source = new KoXmlInputSource( device );
02312   bool result = doc.setContent( source, reader, errorMsg, errorLine, errorColumn);
02313   delete source;
02314   return result;  
02315 }
02316 

Généré le Wed Nov 22 23:41:14 2006 pour KPlato par  doxygen 1.5.1-p1