lib

KoXmlReader.cpp
1/* This file is part of the KDE project
2 Copyright (C) 2005 Ariya Hidayat <ariya@kde.org>
3
4 This library is free software; you can redistribute it and/or
5 modify it under the terms of the GNU Library General Public
6 License as published by the Free Software Foundation; either
7 version 2 of the License, or (at your option) any later version.
8
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
13
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 * Boston, MA 02110-1301, USA.
18*/
19
20/*
21 This is a memory-efficient DOM implementation for KOffice. See the API
22 documentation for details.
23
24 IMPORTANT !
25
26 * When you change this stuff, make sure it DOES NOT BREAK the test suite.
27 Build tests/koxmlreadertest.cpp and verify it. Many sleepless nights
28 have been sacrificed for this piece of code, do not let those precious
29 hours wasted!
30
31 * Run testdom.cpp WITH Valgrind's memcheck tool and make sure NO illegal
32 memory read/write and any type of leak occurs. If you are not familiar
33 with Valgrind then RTFM first and come back again later on.
34
35 * The public API shall remain as compatible as TQDom.
36
37 * All TQDom-compatible methods should behave the same. All TQDom-compatible
38 functions should return the same result. In case of doubt, run
39 koxmlreadertest.cpp but uncomment KOXML_USE_TQDOM in koxmlreader.h
40 so that the tests are performed with standard TQDom.
41
42 Some differences compared to TQDom:
43
44 - DOM tree in KoXmlDocument is read-only, you can not modify it. This is
45 sufficient for KOffice since the tree is only accessed when loading
46 a document to the application. For saving the document to XML file,
47 use KoXmlWriter.
48
49 - Comment node (like TQDomComment) is not implemented as comments are
50 simply ignored.
51
52 - DTD, entity and entity reference are not handled. Thus, the associated
53 nodes (like TQDomDocumentType, TQDomEntity, TQDomEntityReference) are also
54 not implemented.
55
56 - Attribute mapping node is not implemented. But of course, functions to
57 query attributes of an element are available.
58
59
60 */
61
62#include "KoXmlReader.h"
63
64#ifndef KOXML_USE_TQDOM
65
66#include <tqxml.h>
67#include <tqdom.h>
68
69#include <tqmap.h>
70#include <tqcstring.h>
71
72// double TQString, used for hashing against namespace and qualified name pair
73class DTQString
74{
75public:
76 DTQString() { }
77 DTQString( const TQString& s1, const TQString& s2 ){ str1 = s1; str2 = s2; }
78 DTQString( const DTQString& b ) { str1 = b.str1; str2 = b.str2; }
79 DTQString& operator=( const DTQString& b ){ str1 = b.str1; str2 = b.str2; return *this; }
80 bool operator==( const DTQString& b ) const { return (str1==b.str1) && (str2==b.str2); }
81 bool operator!=( const DTQString& b ) const { return (str1!=b.str1) || (str2!=b.str2); }
82 bool operator<( const DTQString& b ) const
83 { return ( str1 < b.str1 ) ? true : ( str1==b.str1 ) ? str2<b.str2 : false; }
84 TQString s1() const { return str1; }
85 TQString s2() const { return str2; }
86private:
87 TQString str1, str2;
88};
89
90// just for completeness, this is the self-test for DTQString above
91#if 0
92 DTQString b1;
93 DTQString b2;
94 CHECK( b1==b2, true );
95 CHECK( b1!=b2, false );
96
97 b1 = DTQString( "sweet","princess" );
98 b2 = DTQString( "sweet","princess" );
99 CHECK( b1==b2, true );
100 CHECK( b1!=b2, false );
101
102 b1 = DTQString( "sweet","princess" );
103 b2 = DTQString( "bad","prince" );
104 CHECK( b1==b2, false );
105 CHECK( b1!=b2, true );
106#endif
107
108class KoXmlStream
109{
110public:
111 KoXmlStream(){ saveData = true; data.reserve(1024); pos = 0; }
112 TQString stringData() const { return data; }
113 void setSaveData( bool s ){ saveData = s; }
114 int at() const { return pos; }
115 KoXmlStream& operator<<( const TQString& str )
116 { if(saveData) data.append(str); pos+=str.length(); return *this; }
117 KoXmlStream& appendEscape( const TQString& str );
118
119private:
120 bool saveData;
121 TQString data;
122 int pos;
123};
124
125KoXmlStream& KoXmlStream::appendEscape( const TQString& str )
126{
127 unsigned len = str.length();
128
129 if( saveData )
130 {
131 data.reserve( data.length() + len );
132 for( unsigned c=0; c<len; c++ )
133 if( str[c]=='<' ){ data.append( "&lt;"); pos += 4; } else
134 if( str[c]=='>'){ data.append( "&gt;"); pos+= 4; } else
135 if( str[c]=='"'){ data.append( "&quot;"); pos += 6; } else
136 if( str[c]=='&'){ data.append( "&amp;"); pos += 5; } else
137 { data.append( str[c] ); pos++; }
138 }
139 else
140 {
141 pos += len;
142 for( unsigned c=0; c<len; c++ )
143 if( str[c]=='<' ) pos += 3; else // "&lt;"
144 if( str[c]=='>') pos+= 3; else // "&gt;"
145 if( str[c]=='"') pos += 5; else // "&quot;"
146 if( str[c]=='&') pos += 4; // "&amp;"
147 }
148
149 return *this;
150}
151
152class KoXmlNodeData
153{
154public:
155
156 KoXmlNodeData();
157 virtual ~KoXmlNodeData();
158
159 // generic properties
160 KoXmlNode::NodeType nodeType;
161 TQString tagName;
162 TQString namespaceURI;
163 TQString prefix;
164 TQString localName;
165
166 // reference counting
167 unsigned long count;
168 void ref() { count++; }
169 void unref() { --count; if( !count ) delete this; }
170
171 // type information
172 virtual const char* typeInfo() const { return "Node"; }
173 TQString nodeName() const;
174
175 // for tree and linked-list
176 KoXmlNodeData* parent;
177 KoXmlNodeData* prev;
178 KoXmlNodeData* next;
179 KoXmlNodeData* first;
180 KoXmlNodeData* last;
181
182 TQString text();
183
184 // node manipulation
185 void appendChild( KoXmlNodeData* child );
186 virtual void clear();
187 KoXmlNodeData* ownerDocument();
188
189 // attributes
190 void setAttribute( const TQString& name, const TQString& value );
191 TQString attribute( const TQString& name );
192 bool hasAttribute( const TQString& name );
193 void setAttributeNS( const TQString& nsURI, const TQString& name, const TQString& value );
194 TQString attributeNS( const TQString& nsURI, const TQString& name );
195 bool hasAttributeNS( const TQString& nsURI, const TQString& name );
196
197 // for text and CDATA
198 TQString data() const;
199 void setData( const TQString& data );
200
201 // for document node
202 TQXmlSimpleReader* xmlReader;
203 TQString buffer;
204 bool setContent( TQXmlInputSource* source, TQXmlReader* reader,
205 TQString* errorMsg = 0, int* errorLine = 0, int* errorColumn = 0 );
206
207 // used when doing on-demand (re)parse
208 bool loaded;
209 unsigned startPos, endPos;
210 void loadChildren( int depth=1 );
211 void unloadChildren();
212 bool fastLoading;
213
214private:
215 TQMap<TQString,TQString> attr;
216 TQMap<DTQString,TQString> attrNS;
217 TQString textData;
218 friend class KoXmlHandler;
219};
220
221class KoXmlHandler : public TQXmlDefaultHandler
222{
223public:
224 KoXmlHandler( KoXmlNodeData*, bool processNamespace );
225 ~KoXmlHandler();
226
227 void setMaxDepth( int d ){ maxDepth = d; }
228 void setInitialOffset( int ofs ){ parseOffset = ofs; }
229
230 // content handler
231 bool startDocument();
232 bool endDocument();
233
234 bool startElement( const TQString& nsURI, const TQString& localName,
235 const TQString& qName, const TQXmlAttributes& atts );
236 bool endElement( const TQString& nsURI, const TQString& localName,
237 const TQString& qName );
238
239 bool characters( const TQString& ch );
240 bool processingInstruction( const TQString& target, const TQString& data );
241 bool skippedEntity( const TQString& name );
242
243 // lexical handler
244 bool startCDATA();
245 bool endCDATA();
246 bool startEntity( const TQString & );
247 bool endEntity( const TQString & );
248 bool startDTD( const TQString& name, const TQString& publicId,
249 const TQString& systemId );
250 bool comment( const TQString& ch );
251
252 // decl handler
253 bool externalEntityDecl( const TQString &name, const TQString &publicId,
254 const TQString &systemId ) ;
255
256 // DTD handler
257 bool notationDecl( const TQString & name, const TQString & publicId,
258 const TQString & systemId );
259 bool unparsedEntityDecl( const TQString &name, const TQString &publicId,
260 const TQString &systemId, const TQString &notationName ) ;
261
262 // error handler
263 bool fatalError( const TQXmlParseException& exception );
264
265 TQString errorMsg;
266 int errorLine;
267 int errorColumn;
268
269private:
270 bool processNamespace;
271 KoXmlNodeData* rootNode;
272 KoXmlNodeData* currentNode;
273 TQString entityName;
274 bool cdata;
275 int parseOffset;
276 KoXmlStream bufferStream;
277 int elementDepth;
278 int maxDepth;
279};
280
281// ==================================================================
282//
283// KoXmlNodeData
284//
285// ==================================================================
286
287KoXmlNodeData::KoXmlNodeData()
288{
289 nodeType = KoXmlNode::NullNode;
290
291 tagName = TQString();
292 prefix = TQString();
293 localName = TQString();
294 namespaceURI = TQString();
295 textData = TQString();
296
297 count = 1;
298 parent = 0;
299 prev = next = 0;
300 first = last = 0;
301
302 xmlReader = 0;
303 startPos = endPos = 0;
304
305 fastLoading = false;
306
307 // assume true, it will be set to false by XML parser when this node
308 // apparently has children AND the children are not loaded
309 loaded = true;
310}
311
312KoXmlNodeData::~KoXmlNodeData()
313{
314 clear();
315}
316
317void KoXmlNodeData::clear()
318{
319 if( first )
320 for( KoXmlNodeData* node = first; node ; )
321 {
322 KoXmlNodeData* next = node->next;
323 node->unref();
324 node = next;
325 }
326
327 nodeType = KoXmlNode::NullNode;
328 tagName = TQString();
329 prefix = TQString();
330 namespaceURI = TQString();
331 textData = TQString();
332
333 attr.clear();
334 attrNS.clear();
335
336 parent = 0;
337 prev = next = 0;
338 first = last = 0;
339
340 delete xmlReader;
341 xmlReader = 0;
342 buffer = TQString();
343}
344
345TQString KoXmlNodeData::text()
346{
347 TQString t( "" );
348
349 loadChildren();
350
351 KoXmlNodeData* node = first;
352 while ( node )
353 {
354 switch( node->nodeType )
355 {
356 case KoXmlNode::ElementNode:
357 t += node->text(); break;
358 case KoXmlNode::TextNode:
359 t += node->data(); break;
360 case KoXmlNode::CDATASectionNode:
361 t += node->data(); break;
362 default: break;
363 }
364 node = node->next;
365 }
366
367 return t;
368}
369
370TQString KoXmlNodeData::nodeName() const
371{
372 TQString n;
373
374 switch( nodeType )
375 {
376 case KoXmlNode::ElementNode:
377 n = tagName;
378 if (!prefix.isEmpty()) n.prepend(":").prepend(prefix); break;
379 case KoXmlNode::TextNode: return TQString("#text");
380 case KoXmlNode::CDATASectionNode: return TQString("#cdata-section");
381 case KoXmlNode::DocumentNode: return TQString("#document");
382 default: break;
383 }
384
385 return n;
386}
387
388KoXmlNodeData* KoXmlNodeData::ownerDocument()
389{
390 KoXmlNodeData* owner = this;
391
392 while( owner->parent )
393 owner = owner->parent;
394
395 return (owner->nodeType==KoXmlNode::DocumentNode) ? owner : 0;
396}
397
398void KoXmlNodeData::appendChild( KoXmlNodeData* node )
399{
400 node->parent = this;
401 if( !last )
402 first = last = node;
403 else
404 {
405 last->next = node;
406 node->prev = last;
407 node->next = 0;
408 last = node;
409 }
410}
411
412void KoXmlNodeData::setAttribute( const TQString& name, const TQString& value )
413{
414 attr[ name ] = value;
415}
416
417TQString KoXmlNodeData::attribute( const TQString& name )
418{
419 return attr[ name ];
420}
421
422bool KoXmlNodeData::hasAttribute( const TQString& name )
423{
424 return attr.contains( name );
425}
426
427void KoXmlNodeData::setAttributeNS( const TQString& nsURI,
428const TQString& name, const TQString& value )
429{
430 TQString prefix;
431 TQString localName = name;
432 int i = name.find( ':' );
433 if( i != -1 )
434 {
435 localName = name.mid( i + 1 );
436 prefix = name.left( i );
437 }
438
439 if( prefix.isNull() ) return;
440
441 DTQString key( nsURI, localName );
442 attrNS[ key ] = value;
443}
444
445TQString KoXmlNodeData::attributeNS( const TQString& nsURI, const TQString& name )
446{
447 DTQString key( nsURI, name );
448 return attrNS[ key ];
449}
450
451bool KoXmlNodeData::hasAttributeNS( const TQString& nsURI, const TQString& name )
452{
453 DTQString key( nsURI, name );
454 return attrNS.contains( key );
455}
456
457TQString KoXmlNodeData::data() const
458{
459 return textData;
460}
461
462void KoXmlNodeData::setData( const TQString& d )
463{
464 textData = d;
465}
466
467bool KoXmlNodeData::setContent( TQXmlInputSource* source,
468TQXmlReader* reader, TQString* errorMsg, int* errorLine, int* errorColumn )
469{
470 if( nodeType != KoXmlNode::DocumentNode )
471 return false;
472
473 clear();
474 nodeType = KoXmlNode::DocumentNode;
475
476 // sanity checks
477 if( !source ) return false;
478 if( !reader ) return false;
479
480 // copy the reader for later on-demand loading
481 // FIXME this is a workaround because no copy is possible with TQXmlReader
482 char* features[] =
483 {
484 "http://xml.org/sax/features/namespaces",
485 "http://xml.org/sax/features/namespace-prefixes",
486 "http://trolltech.com/xml/features/report-whitespace-only-CharData",
487 "http://trolltech.com/xml/features/report-start-end-entity"
488 };
489 xmlReader = new TQXmlSimpleReader;
490 for( int fi=0; fi<4; fi++ )
491 xmlReader->setFeature( features[fi], reader->feature( features[fi] ) );
492
493 bool processNamespace =
494 reader->feature( "http://xml.org/sax/features/namespaces" ) &&
495 !reader->feature( "http://xml.org/sax/features/namespace-prefixes" );
496
497 KoXmlHandler handler( this, processNamespace );
498 reader->setContentHandler( &handler );
499 reader->setErrorHandler( &handler );
500 reader->setLexicalHandler( &handler );
501 reader->setDeclHandler( &handler );
502 reader->setDTDHandler( &handler );
503
504 if( !fastLoading )
505 handler.setMaxDepth( 4 );
506
507 if( !reader->parse( source ) )
508 {
509 // parsing error has occurred
510 if( errorMsg ) *errorMsg = handler.errorMsg;
511 if( errorLine ) *errorLine = handler.errorLine;
512 if( errorColumn ) *errorColumn = handler.errorColumn;
513 return false;
514 }
515
516 return true;
517}
518
519void KoXmlNodeData::loadChildren( int depth )
520{
521 // for more than 1 level, force reloading anyway
522 if( ( depth== 1 ) && loaded ) return;
523
524 KoXmlNodeData* doc = ownerDocument();
525 if( !doc ) return;
526
527 // fast loading? then this on-demand loading makes no sense
528 if( doc->fastLoading ) return;
529
530 unloadChildren();
531
532 bool nsProcess =
533 doc->xmlReader->feature( "http://xml.org/sax/features/namespaces" ) &&
534 !doc->xmlReader->feature( "http://xml.org/sax/features/namespace-prefixes" );
535
536
537 // XML snippet for the children, including this element
538 TQString snippet = doc->buffer.mid( startPos, endPos-startPos+1 );
539
540 // now parse all subnodes
541 KoXmlHandler handler( this, nsProcess );
542 handler.setMaxDepth( depth );
543 handler.setInitialOffset( startPos );
544 doc->xmlReader->setContentHandler( &handler );
545 doc->xmlReader->setErrorHandler( &handler );
546 doc->xmlReader->setLexicalHandler( &handler );
547 doc->xmlReader->setDeclHandler( &handler );
548 doc->xmlReader->setDTDHandler( &handler );
549
550 TQXmlInputSource source;
551 source.setData( snippet );
552 if( !doc->xmlReader->parse( source ) )
553 {
554 // parsing error has occurred, which should not happen
555 // nothing we can do except...
556 loaded = false;
557 tqWarning( "On-demand loading triggers parse error!" );
558 }
559 else
560 loaded = true;
561}
562
563void KoXmlNodeData::unloadChildren()
564{
565 if( !loaded ) return;
566
567 if( first )
568 for( KoXmlNodeData* node = first; node ; )
569 {
570 KoXmlNodeData* next = node->next;
571 node->unloadChildren();
572 node->unref();
573 node = next;
574 }
575
576 loaded = false;
577 first = last = 0;
578}
579
580
581// ==================================================================
582//
583// KoXmlHandler
584//
585// ==================================================================
586
587KoXmlHandler::KoXmlHandler( KoXmlNodeData* n, bool ns ):
588TQXmlDefaultHandler()
589{
590 processNamespace = ns;
591
592 rootNode = n;
593 currentNode = n;
594 cdata = false;
595 entityName = TQString();
596
597 errorMsg = TQString();
598 errorLine = 0;
599 errorColumn = 0;
600
601 parseOffset = 0;
602 elementDepth = -1;
603 maxDepth = 999;
604
605 bufferStream.setSaveData( rootNode->nodeType == KoXmlNode::DocumentNode );
606}
607
608KoXmlHandler::~KoXmlHandler()
609{
610}
611
612bool KoXmlHandler::startDocument()
613{
614 // just for sanity
615 currentNode = rootNode;
616 cdata = false;
617 entityName = TQString();
618 elementDepth = -1;
619
620 return true;
621}
622
623bool KoXmlHandler::endDocument()
624{
625 // just for sanity
626 if( rootNode->nodeType == KoXmlNode::DocumentNode )
627 if( currentNode!=rootNode )
628 return false;
629
630 if( rootNode->nodeType == KoXmlNode::DocumentNode )
631 rootNode->buffer = bufferStream.stringData();
632
633 return true;
634}
635
636bool KoXmlHandler::startDTD( const TQString& name, const TQString& publicId,
637const TQString& systemId )
638{
639 Q_UNUSED( name );
640 Q_UNUSED( publicId );
641 Q_UNUSED( systemId );
642
643 // we skip DTD
644 return true;
645}
646
647bool KoXmlHandler::startElement( const TQString& nsURI, const TQString& localName,
648const TQString& name, const TQXmlAttributes& atts )
649{
650 Q_UNUSED( localName );
651
652 // sanity check
653 if( !currentNode )
654 return false;
655
656 // we are going one level deeper
657 elementDepth++;
658
659 TQString nodePrefix, nodeLocalName, nodeTagName;
660 KoXmlNodeData* element = 0;
661
662 if( processNamespace )
663 {
664 // parse, using namespace
665 nodeTagName = name;
666 nodeLocalName = name;
667 nodePrefix = nsURI.isNull() ? TQString() : TQString("");
668 int i = name.find( ':' );
669 if( i != -1 )
670 {
671 nodeTagName = name.mid( i + 1 );
672 nodeLocalName = nodeTagName;
673 nodePrefix = name.left( i );
674 }
675
676 if( elementDepth <= maxDepth )
677 {
678 // construct a new element
679 element = new KoXmlNodeData;
680 element->nodeType = KoXmlNode::ElementNode;
681 element->parent = currentNode;
682 element->namespaceURI = nsURI;
683 element->prefix = nodePrefix;
684 element->localName = nodeLocalName;
685 element->tagName = nodeTagName;
686
687 // Note: endPos will be later fixed in endElement
688 element->endPos = element->startPos = parseOffset + bufferStream.at();
689
690 // handle the attributes
691 for( int c=0; c<atts.length(); c++ )
692 {
693 TQString prefix;
694 TQString qName; // with prefix
695 TQString name; // without prefix, i.e. local name
696
697 name = qName = atts.qName(c);
698 int i = qName.find( ':' );
699 if( i != -1 ) prefix = qName.left( i );
700 if( i != -1 ) name = qName.mid( i + 1 );
701 element->setAttributeNS( atts.uri(c), qName, atts.value(c) );
702 element->setAttribute( name, atts.value(c) );
703 }
704 }
705
706 // save in buffer for later on-demand loading
707 if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
708 {
709 bufferStream << "<";
710 if( !nodePrefix.isEmpty() )
711 bufferStream << nodePrefix << ":";
712 bufferStream << localName;
713 bufferStream << " xmlns";
714 if( !nodePrefix.isEmpty() )
715 bufferStream << ":" << nodePrefix;
716 bufferStream << "=\"";
717 bufferStream.appendEscape( nsURI );
718 bufferStream << "\"";
719 for( int c=0; c<atts.length(); c++ )
720 {
721 TQString prefix;
722 TQString name = atts.qName(c); // qName contains the prefix
723 int i = name.find( ':' );
724 if( i != -1 ) prefix = name.left( i );
725 if( i != -1 ) name = atts.qName(c).mid( i + 1 );
726 if( !atts.uri(c).isEmpty() )
727 bufferStream << " xmlns:" << prefix << "=\"" << atts.uri(c) << "\"";
728 bufferStream << " ";
729 if( !prefix.isEmpty() ) bufferStream << prefix << ":";
730 bufferStream << name << "=\"";
731 bufferStream.appendEscape( atts.value(c) );
732 bufferStream << "\"";
733 }
734 bufferStream << ">";
735 }
736 }
737 else
738 {
739 // parse, without using namespace
740 nodeTagName = name;
741
742 if( elementDepth <= maxDepth )
743 {
744 // construct a new element
745 element = new KoXmlNodeData;
746 element->nodeType = KoXmlNode::ElementNode;
747 element->parent = currentNode;
748 element->namespaceURI = TQString();
749 element->prefix = TQString();
750 element->localName = TQString();
751 element->tagName = nodeTagName;
752
753 if( rootNode->nodeType == KoXmlNode::DocumentNode )
754 element->fastLoading = rootNode->fastLoading;
755
756 // Note: endPos will be later fixed in endElement
757 element->endPos = element->startPos = parseOffset + bufferStream.at();
758
759 // handle the attributes
760 for( int c=0; c<atts.length(); c++ )
761 element->setAttribute( atts.qName(c), atts.value(c) );
762 }
763
764 // save in buffer for later on-demand loading
765 if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
766 {
767 bufferStream << "<";
768 bufferStream << nodeTagName;
769 for( int c=0; c<atts.length(); c++ )
770 {
771 bufferStream << " " << atts.qName(c) << "=\"";
772 bufferStream.appendEscape( atts.value(c) );
773 bufferStream << "\"";
774 }
775 bufferStream << ">";
776 }
777
778 }
779
780 // if we do not parse a complete document, the first element is ignored
781 // this feature is used in on-demand loading
782 // e.g. "<ul><li><b>bold items</b></li></ul>", if root node points to
783 // <ul> tag, then the first <ul> is skipped so that next <li> element
784 // becomes the child of it.
785 if( elementDepth == 0 )
786 if( rootNode->nodeType != KoXmlNode::DocumentNode )
787 {
788 delete element;
789 return true;
790 }
791
792 if( element )
793 {
794 // add as the child and traverse to it
795 currentNode->loaded = true;
796 currentNode->appendChild( element );
797 currentNode = element;
798 }
799 else
800 currentNode->loaded = false;
801
802 return true;
803}
804
805bool KoXmlHandler::endElement( const TQString& nsURI, const TQString& localName,
806const TQString& qName )
807{
808 Q_UNUSED( nsURI );
809 Q_UNUSED( localName );
810
811 // sanity check
812 if( !currentNode ) return false;
813 if( !currentNode->parent ) return false;
814
815 // see comments in startElement about first element and on-demand loading
816 if( rootNode->nodeType == KoXmlNode::DocumentNode )
817 if( currentNode == rootNode )
818 return false;
819
820 // buffer for on-demand loading
821 if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
822 bufferStream << "</" << qName << ">";
823
824 // fix up the pointer
825 currentNode->endPos = parseOffset + bufferStream.at() - 1;
826
827 // go up one level
828 if( elementDepth <= maxDepth )
829 currentNode = currentNode->parent;
830
831 // we are going up one level
832 elementDepth--;
833
834 return true;
835}
836
837bool KoXmlHandler::characters( const TQString& str )
838{
839 // sanity check
840 if( rootNode->nodeType == KoXmlNode::DocumentNode )
841 if( currentNode == rootNode )
842 return false;
843
844 // are we inside entity ?
845 if( !entityName.isEmpty() )
846 {
847 // we do not handle entity but need to keep track of it
848 // because we want to skip it alltogether
849 return true;
850 }
851
852 if( cdata )
853 {
854 // are we inside CDATA section ?
855 if( elementDepth <= maxDepth )
856 {
857 KoXmlNodeData* cdata = new KoXmlNodeData;
858 cdata->nodeType = KoXmlNode::CDATASectionNode;
859 cdata->parent = currentNode;
860 cdata->setData( str );
861 currentNode->appendChild( cdata );
862 }
863
864 if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
865 bufferStream << "<![CDATA[" << str << "]]>"; // no escape for CDATA
866 }
867 else
868 {
869 // this must be normal text node
870 if( elementDepth <= maxDepth )
871 {
872 KoXmlNodeData* text = new KoXmlNodeData;
873 text->nodeType = KoXmlNode::TextNode;
874 text->parent = currentNode;
875 text->setData( str );
876 currentNode->appendChild( text );
877 }
878
879 // save it for later use
880 if( ( rootNode->nodeType != KoXmlNode::DocumentNode ) || !rootNode->fastLoading )
881 bufferStream.appendEscape( str );
882 }
883
884 return true;
885}
886
887bool KoXmlHandler::processingInstruction( const TQString& target,
888const TQString& data )
889{
890 // sanity check
891 if( !currentNode )
892 return false;
893
894 KoXmlNodeData* instruction = new KoXmlNodeData;
895 instruction->nodeType = KoXmlNode::ProcessingInstructionNode;
896 instruction->parent = currentNode;
897 instruction->tagName = target;
898 instruction->setData( data );
899
900 currentNode->appendChild( instruction );
901
902 return true;
903}
904
905bool KoXmlHandler::skippedEntity( const TQString& name )
906{
907 Q_UNUSED( name );
908
909 // we skip entity
910 return true;
911}
912
913bool KoXmlHandler::startCDATA()
914{
915 cdata = true;
916 return true;
917}
918
919bool KoXmlHandler::endCDATA()
920{
921 cdata = false;
922 return true;
923}
924
925bool KoXmlHandler::startEntity( const TQString& name )
926{
927 entityName = name;
928 return true;
929}
930
931bool KoXmlHandler::endEntity( const TQString& name )
932{
933 Q_UNUSED( name );
934 entityName = TQString();
935 return true;
936}
937
938bool KoXmlHandler::comment( const TQString& comment )
939{
940 Q_UNUSED( comment );
941
942 // we skip comment
943 return true;
944}
945
946bool KoXmlHandler::unparsedEntityDecl( const TQString &name,
947const TQString &publicId, const TQString &systemId, const TQString &notationName )
948{
949 Q_UNUSED( name );
950 Q_UNUSED( publicId );
951 Q_UNUSED( systemId );
952 Q_UNUSED( notationName );
953
954 // we skip entity
955 return true;
956}
957
958bool KoXmlHandler::externalEntityDecl( const TQString &name,
959const TQString &publicId, const TQString &systemId )
960{
961 Q_UNUSED( name );
962 Q_UNUSED( publicId );
963 Q_UNUSED( systemId );
964
965 // we skip entity
966 return true;
967}
968
969bool KoXmlHandler::notationDecl( const TQString & name,
970const TQString & publicId, const TQString & systemId )
971{
972 Q_UNUSED( name );
973 Q_UNUSED( publicId );
974 Q_UNUSED( systemId );
975
976 // we skip notation node
977 return true;
978}
979
980bool KoXmlHandler::fatalError( const TQXmlParseException& exception )
981{
982 errorMsg = exception.message();
983 errorLine = exception.lineNumber();
984 errorColumn = exception.columnNumber();
985 return TQXmlDefaultHandler::fatalError( exception );
986}
987
988// ==================================================================
989//
990// KoXmlNode
991//
992// ==================================================================
993
994// Creates a null node
995KoXmlNode::KoXmlNode()
996{
997 d = new KoXmlNodeData;
998}
999
1000// Destroys this node
1001KoXmlNode::~KoXmlNode()
1002{
1003 if( d ) d->unref();
1004}
1005
1006// Creates a copy of another node
1007KoXmlNode::KoXmlNode( const KoXmlNode& node )
1008{
1009 d = node.d;
1010 d->ref();
1011}
1012
1013// Creates a node for specific implementation
1014KoXmlNode::KoXmlNode( KoXmlNodeData* data )
1015{
1016 d = data;
1017 data->ref();
1018}
1019
1020// Creates a shallow copy of another node
1021KoXmlNode& KoXmlNode::operator=( const KoXmlNode& node )
1022{
1023 d->unref();
1024 d = node.d;
1025 d->ref();
1026 return *this;
1027}
1028
1029// Note: two null nodes are always equal
1030bool KoXmlNode::operator==( const KoXmlNode& node ) const
1031{
1032 if( isNull() && node.isNull() ) return true;
1033 return( d==node.d );
1034}
1035
1036// Note: two null nodes are always equal
1037bool KoXmlNode::operator!=( const KoXmlNode& node ) const
1038{
1039 if( isNull() && !node.isNull() ) return true;
1040 if( !isNull() && node.isNull() ) return true;
1041 if( isNull() && node.isNull() ) return false;
1042 return( d!=node.d );
1043}
1044
1045KoXmlNode::NodeType KoXmlNode::nodeType() const
1046{
1047 return d->nodeType;
1048}
1049
1050bool KoXmlNode::isElement() const
1051{
1052 return d->nodeType == ElementNode;
1053}
1054
1055bool KoXmlNode::isText() const
1056{
1057 return (d->nodeType == TextNode) || isCDATASection();
1058}
1059
1060bool KoXmlNode::isCDATASection() const
1061{
1062 return d->nodeType == CDATASectionNode;
1063}
1064
1065bool KoXmlNode::isDocument() const
1066{
1067 return d->nodeType == DocumentNode;
1068}
1069
1070bool KoXmlNode::isNull() const
1071{
1072 return d->nodeType == NullNode;
1073}
1074
1075void KoXmlNode::clear()
1076{
1077 d->unref();
1078 d = new KoXmlNodeData;
1079}
1080
1081TQString KoXmlNode::nodeName() const
1082{
1083 return d->nodeName();
1084}
1085
1086TQString KoXmlNode::prefix() const
1087{
1088 return isElement() ? d->prefix : TQString();
1089}
1090
1091TQString KoXmlNode::namespaceURI() const
1092{
1093 return isElement() ? d->namespaceURI : TQString();
1094}
1095
1096TQString KoXmlNode::localName() const
1097{
1098 return isElement() ? d->localName : TQString();
1099}
1100
1101KoXmlDocument KoXmlNode::ownerDocument() const
1102{
1103 KoXmlNodeData* node = d;
1104 while( node->parent ) node = node->parent;
1105
1106 if( node->nodeType != DocumentNode ) return KoXmlDocument();
1107 return KoXmlDocument( node );
1108}
1109
1110KoXmlNode KoXmlNode::parentNode() const
1111{
1112 return d->parent ? KoXmlNode( d->parent ) : KoXmlNode();
1113}
1114
1115bool KoXmlNode::hasChildNodes() const
1116{
1117 d->loadChildren();
1118 return d->first!=0 ;
1119}
1120
1121KoXmlNode KoXmlNode::firstChild() const
1122{
1123 if( !d->fastLoading )
1124 d->loadChildren();
1125 return d->first ? KoXmlNode( d->first ) : KoXmlNode();
1126}
1127
1128KoXmlNode KoXmlNode::lastChild() const
1129{
1130 if( !d->fastLoading )
1131 d->loadChildren();
1132 return d->last ? KoXmlNode( d->last ) : KoXmlNode();
1133}
1134
1135KoXmlNode KoXmlNode::nextSibling() const
1136{
1137 return d->next ? KoXmlNode( d->next ) : KoXmlNode();
1138}
1139
1140KoXmlNode KoXmlNode::previousSibling() const
1141{
1142 return d->prev ? KoXmlNode( d->prev ) : KoXmlNode();
1143}
1144
1145KoXmlNode KoXmlNode::namedItem( const TQString& name ) const
1146{
1147 if( !d->fastLoading )
1148 d->loadChildren();
1149
1150 KoXmlNodeData* node = d->first;
1151 while ( node )
1152 {
1153 if( node->nodeName() == name )
1154 return KoXmlNode( node );
1155 node = node->next;
1156 }
1157
1158 // not found
1159 return KoXmlNode();
1160}
1161
1162KoXmlNode KoXmlNode::namedItemNS( const TQString& nsURI, const TQString& name ) const
1163{
1164 if( !d->fastLoading )
1165 d->loadChildren();
1166
1167 KoXmlNodeData* node = d->first;
1168 while ( node )
1169 {
1170 if( !node->prefix.isNull() )
1171 if( node->namespaceURI == nsURI )
1172 if( node->localName == name )
1173 return KoXmlNode( node );
1174 node = node->next;
1175 }
1176
1177 // not found
1178 return KoXmlNode();
1179}
1180
1181KoXmlElement KoXmlNode::toElement()
1182{
1183 return isElement() ? KoXmlElement( d ) : KoXmlElement();
1184}
1185
1186KoXmlText KoXmlNode::toText()
1187{
1188 return isText() ? KoXmlText( d ) : KoXmlText();
1189}
1190
1191KoXmlCDATASection KoXmlNode::toCDATASection()
1192{
1193 return isCDATASection() ? KoXmlCDATASection( (KoXmlNodeData*)d ) :
1194 KoXmlCDATASection();
1195}
1196
1197KoXmlDocument KoXmlNode::toDocument()
1198{
1199 return isDocument() ? KoXmlDocument( d ) : KoXmlDocument();
1200}
1201
1202void KoXmlNode::load( int depth )
1203{
1204 d->loadChildren( depth );
1205}
1206
1208{
1209 d->unloadChildren();
1210}
1211
1212// ==================================================================
1213//
1214// KoXmlElement
1215//
1216// ==================================================================
1217
1218// Creates an empty element
1219KoXmlElement::KoXmlElement(): KoXmlNode()
1220{
1221 d->unref();
1222 d = new KoXmlNodeData;
1223}
1224
1225KoXmlElement::~KoXmlElement()
1226{
1227 d->unref();
1228 d = 0;
1229}
1230
1231// Creates a shallow copy of another element
1232KoXmlElement::KoXmlElement( const KoXmlElement& element ): KoXmlNode()
1233{
1234 d->unref();
1235 d = element.d;
1236 d->ref();
1237}
1238
1239KoXmlElement::KoXmlElement( KoXmlNodeData* data ): KoXmlNode()
1240{
1241 d->unref();
1242 d = data;
1243 d->ref();
1244}
1245
1246// Copies another element
1247KoXmlElement& KoXmlElement::operator=( const KoXmlElement& element )
1248{
1249 KoXmlNode::operator=( element );
1250 return *this;
1251}
1252
1253bool KoXmlElement::operator== ( const KoXmlElement& element ) const
1254{
1255 if( isNull() || element.isNull() ) return false;
1256 return (d==element.d);
1257}
1258
1259bool KoXmlElement::operator!= ( const KoXmlElement& element ) const
1260{
1261 if( isNull() && element.isNull() ) return false;
1262 if( isNull() || element.isNull() ) return true;
1263 return (d!=element.d);
1264}
1265
1266TQString KoXmlElement::tagName() const
1267{
1268 return isElement() ? ((KoXmlNodeData*)d)->tagName: TQString();
1269}
1270
1271TQString KoXmlElement::text() const
1272{
1273 return d->text();
1274}
1275
1276bool KoXmlElement::isElement() const
1277{
1278 return true;
1279}
1280
1281TQString KoXmlElement::attribute( const TQString& name ) const
1282{
1283 return attribute( name, TQString() );
1284}
1285
1286TQString KoXmlElement::attribute( const TQString& name,
1287const TQString& defaultValue ) const
1288{
1289 if( !isElement() )
1290 return defaultValue;
1291
1292 if( !hasAttribute( name ) )
1293 return defaultValue;
1294
1295 return ((KoXmlNodeData*)d)->attribute( name );
1296}
1297
1298TQString KoXmlElement::attributeNS( const TQString& namespaceURI,
1299const TQString& localName, const TQString& defaultValue ) const
1300{
1301 if( !isElement() )
1302 return defaultValue;
1303
1304 if( !hasAttributeNS( namespaceURI,localName ) )
1305 return defaultValue;
1306
1307 return ((KoXmlNodeData*)d)->attributeNS( namespaceURI,localName );
1308}
1309
1310bool KoXmlElement::hasAttribute( const TQString& name ) const
1311{
1312 return isElement() ? ((KoXmlNodeData*)d)->hasAttribute( name ) : false;
1313}
1314
1315bool KoXmlElement::hasAttributeNS( const TQString& namespaceURI,
1316const TQString& localName ) const
1317{
1318 return isElement() ? ((KoXmlNodeData*)d)->hasAttributeNS(
1319 namespaceURI, localName ) : false;;
1320}
1321
1322// ==================================================================
1323//
1324// KoXmlText
1325//
1326// ==================================================================
1327
1328KoXmlText::KoXmlText(): KoXmlNode()
1329{
1330 d->unref();
1331 d = new KoXmlNodeData;
1332 d->nodeType = TextNode;
1333}
1334
1335KoXmlText::~KoXmlText()
1336{
1337 if( d ) d->unref();
1338 d = 0;
1339}
1340
1341KoXmlText::KoXmlText( const KoXmlText& text ): KoXmlNode()
1342{
1343 d->unref();
1344 d = (KoXmlNodeData*) text.d;
1345 d->ref();
1346}
1347
1348KoXmlText::KoXmlText( KoXmlNodeData* data ): KoXmlNode()
1349{
1350 d->unref();
1351 d = data;
1352 d->ref();
1353}
1354
1355bool KoXmlText::isText() const
1356{
1357 return true;
1358}
1359
1360TQString KoXmlText::data() const
1361{
1362 return ((KoXmlNodeData*)d)->data();
1363}
1364
1365KoXmlText& KoXmlText::operator=( const KoXmlText& element )
1366{
1367 KoXmlNode::operator=( element );
1368 return *this;
1369}
1370
1371// ==================================================================
1372//
1373// KoXmlCDATASection
1374//
1375// ==================================================================
1376
1377KoXmlCDATASection::KoXmlCDATASection(): KoXmlText()
1378{
1379 d->unref();
1380 d = new KoXmlNodeData;
1381 d->nodeType = KoXmlNode::CDATASectionNode;
1382}
1383
1384KoXmlCDATASection::~KoXmlCDATASection()
1385{
1386 d->unref();
1387 d = 0;
1388}
1389
1390KoXmlCDATASection::KoXmlCDATASection( const KoXmlCDATASection& cdata ):
1391KoXmlText()
1392{
1393 d->unref();
1394 d = (KoXmlNodeData*) cdata.d;
1395 d->ref();
1396}
1397
1398KoXmlCDATASection::KoXmlCDATASection( KoXmlNodeData* cdata ):
1399KoXmlText()
1400{
1401 d->unref();
1402 d = cdata;
1403 d->ref();
1404}
1405
1406bool KoXmlCDATASection::isCDATASection() const
1407{
1408 return true;
1409}
1410
1411KoXmlCDATASection& KoXmlCDATASection::operator=( const KoXmlCDATASection& cdata )
1412{
1413 KoXmlNode::operator=( cdata );
1414 return *this;
1415}
1416
1417// ==================================================================
1418//
1419// KoXmlDocument
1420//
1421// ==================================================================
1422
1423KoXmlDocument::KoXmlDocument(): KoXmlNode()
1424{
1425 d->unref();
1426 d = new KoXmlNodeData;
1427 d->nodeType = KoXmlNode::DocumentNode;
1428}
1429
1430KoXmlDocument::~KoXmlDocument()
1431{
1432 d->unref();
1433 d = 0;
1434}
1435
1436KoXmlDocument::KoXmlDocument( KoXmlNodeData* data ): KoXmlNode()
1437{
1438 d->unref();
1439 d = data;
1440 d->ref();
1441}
1442
1443// Creates a copy of another document
1444KoXmlDocument::KoXmlDocument( const KoXmlDocument& doc ): KoXmlNode()
1445{
1446 d->unref();
1447 d = doc.d;
1448 d->ref();
1449}
1450
1451// Creates a shallow copy of another document
1452KoXmlDocument& KoXmlDocument::operator=( const KoXmlDocument& doc )
1453{
1454 KoXmlNode::operator=( doc );
1455 return *this;
1456}
1457
1458// Checks if this document and doc are equals
1459bool KoXmlDocument::operator==( const KoXmlDocument& doc ) const
1460{
1461 return( d==doc.d );
1462}
1463
1464// Checks if this document and doc are not equals
1465bool KoXmlDocument::operator!=( const KoXmlDocument& doc ) const
1466{
1467 return( d!=doc.d );
1468}
1469
1470bool KoXmlDocument::isDocument() const
1471{
1472 return true;
1473}
1474
1475KoXmlElement KoXmlDocument::documentElement() const
1476{
1477 for( KoXmlNodeData* node=d->first; node; )
1478 if( node->nodeType==KoXmlNode::ElementNode )
1479 return KoXmlElement( node );
1480 else node = node->next;
1481
1482 return KoXmlElement();
1483}
1484
1485void KoXmlDocument::setFastLoading( bool f )
1486{
1487 d->fastLoading = f;
1488}
1489
1490bool KoXmlDocument::fastLoading() const
1491{
1492 return d->fastLoading;
1493}
1494
1495bool KoXmlDocument::setContent( TQXmlInputSource *source, TQXmlReader *reader,
1496 TQString* errorMsg, int* errorLine, int* errorColumn )
1497{
1498 if( d->nodeType != KoXmlNode::DocumentNode )
1499 return false;
1500
1501 return d->setContent( source, reader, errorMsg, errorLine, errorColumn );
1502}
1503
1504// no namespace processing
1505bool KoXmlDocument::setContent( TQIODevice* device, TQString* errorMsg,
1506int* errorLine, int* errorColumn )
1507{
1508 return setContent( device, false, errorMsg, errorLine, errorColumn );
1509}
1510
1511bool KoXmlDocument::setContent( TQIODevice* device, bool namespaceProcessing,
1512TQString* errorMsg, int* errorLine, int* errorColumn )
1513{
1514 if( d->nodeType != KoXmlNode::DocumentNode )
1515 return false;
1516
1517 TQXmlSimpleReader reader;
1518 reader.setFeature( "http://xml.org/sax/features/namespaces", namespaceProcessing );
1519 reader.setFeature( "http://xml.org/sax/features/namespace-prefixes", !namespaceProcessing );
1520 reader.setFeature( "http://trolltech.com/xml/features/report-whitespace-only-CharData", false );
1521
1522 // FIXME this hack is apparently private
1523 //reader.setUndefEntityInAttrHack(true);
1524
1525 TQXmlInputSource source( device );
1526 return d->setContent( &source, &reader, errorMsg, errorLine, errorColumn );
1527}
1528
1529#endif
1530
1531KoXmlElement KoXml::namedItemNS( const KoXmlNode& node, const char* nsURI,
1532const char* localName )
1533{
1534#ifdef KOXML_USE_TQDOM
1535 // David's solution for namedItemNS, only for TQDom stuff
1536 KoXmlNode n = node.firstChild();
1537 for ( ; !n.isNull(); n = n.nextSibling() ) {
1538 if ( n.isElement() && n.localName() == localName &&
1539 n.namespaceURI() == nsURI )
1540 return n.toElement();
1541 }
1542 return KoXmlElement();
1543#else
1544 return node.namedItemNS( nsURI, localName).toElement();
1545#endif
1546}
1547
1548void KoXml::load( KoXmlNode& node, int depth )
1549{
1550#ifdef KOXML_USE_TQDOM
1551 // do nothing, TQDom has no on-demand loading
1552 Q_UNUSED( node );
1553 Q_UNUSED( depth );
1554#else
1555 node.load( depth );
1556#endif
1557}
1558
1559
1561{
1562#ifdef KOXML_USE_TQDOM
1563 // do nothing, TQDom has no on-demand unloading
1564 Q_UNUSED( node );
1565#else
1566 node.unload();
1567#endif
1568}
KoXmlCDATASection represents a CDATA section in a DOM tree.
KoXmlDocument represents an XML document, structured in a DOM tree.
KoXmlElement represents a tag element in a DOM tree.
KoXmlNode represents a node in a DOM tree.
Definition KoXmlReader.h:65
void load(int depth=1)
Loads all child nodes (if any) of this node.
void unload()
Releases all child nodes of this node.
KoXmlText represents a text in a DOM tree.
const char * name(StdAction id)
KOFFICECORE_EXPORT void unload(KoXmlNode &node)
Unload child nodes of specified node.
KOFFICECORE_EXPORT void load(KoXmlNode &node, int depth=1)
Explicitly load child nodes of specified node, up to given depth.
KOFFICECORE_EXPORT KoXmlElement namedItemNS(const KoXmlNode &node, const char *nsURI, const char *localName)
A namespace-aware version of TQDomNode::namedItem(), which also takes care of casting to a TQDomEleme...