1
/* This file is part of the KDE project
2
Copyright (C) 2005-2006 Ariya Hidayat <ariya@kde.org>
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.
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.
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.
20
#include "KoXmlReader.h"
23
This is a memory-efficient DOM implementation for KOffice. See the API
24
documentation for details.
28
* When you change this stuff, make sure it DOES NOT BREAK the test suite.
29
Build tests/koxmlreadertest.cpp and verify it. Many sleepless nights
30
have been sacrificed for this piece of code, do not let those precious
33
* Run koxmlreadertest.cpp WITH Valgrind and make sure NO illegal
34
memory read/write and any type of leak occurs. If you are not familiar
35
with Valgrind then RTFM first and come back again later on.
37
* The public API shall remain as compatible as QDom.
39
* All QDom-compatible methods should behave the same. All QDom-compatible
40
functions should return the same result. In case of doubt, run
41
koxmlreadertest.cpp but uncomment KOXML_USE_QDOM in koxmlreader.h
42
so that the tests are performed with standard QDom.
44
Some differences compared to QDom:
46
- DOM tree in KoXmlDocument is read-only, you can not modify it. This is
47
sufficient for KOffice since the tree is only accessed when loading
48
a document to the application. For saving the document to XML file,
51
- Because the dynamic loading and unloading, you have to use the
52
nodes (and therefore also elements) carefully since the whole API
53
(just like QDom) is reference-based, not pointer-based. If the
54
parent node is unloaded from memory, the reference is not valid
55
anymore and may give unpredictable result.
56
The easiest way: use the node/element in very short time only.
58
- Comment node (like QDomComment) is not implemented as comments are
61
- DTD, entity and entity reference are not handled. Thus, the associated
62
nodes (like QDomDocumentType, QDomEntity, QDomEntityReference) are also
65
- Attribute mapping node is not implemented. But of course, functions to
66
query attributes of an element are available.
74
#include <QTextDecoder>
76
#ifndef KOXML_USE_QDOM
83
#include <QDataStream>
86
#include <QStringList>
90
Use more compact representation of in-memory nodes.
92
Advantages: faster iteration, can facilitate real-time compression.
93
Disadvantages: still buggy, eat slightly more memory.
98
Use real-time compression. Only works in conjuction with KOXML_COMPACT
99
above because otherwise the non-compact layout will slow down everything.
101
#define KOXML_COMPRESS
104
// prevent mistake, see above
105
#ifdef KOXML_COMPRESS
106
#ifndef KOXML_COMPACT
107
#error Please enable also KOXML_COMPACT
111
// this is used to quickly get namespaced attribute(s)
112
typedef QPair<QString, QString> KoXmlStringPair;
114
// this simplistic hash is rather fast-and-furious. it works because
115
// likely there is very few namespaced attributes per element
116
static inline uint qHash(const KoXmlStringPair &p)
118
return qHash(p.first[0].unicode()) ^ 0x1477;
120
// in case of doubt, use this:
121
// return qHash(p.first)^qHash(p.second);
124
// ==================================================================
128
// ==================================================================
130
// 24 bytes on most system
131
class KoXmlPackedItem
135
KoXmlNode::NodeType type: 3;
138
quint32 childStart: 28;
147
// it is important NOT to have a copy constructor, so that growth is optimal
148
// see http://doc.trolltech.com/4.2/containers.html#growth-strategies
150
KoXmlPackedItem(): attr(false), type(KoXmlNode::NullNode), childStart(0), depth(0) {}
154
Q_DECLARE_TYPEINFO(KoXmlPackedItem, Q_MOVABLE_TYPE);
156
#ifdef KOXML_COMPRESS
157
static QDataStream& operator<<(QDataStream& s, const KoXmlPackedItem& item)
159
quint8 flag = item.attr ? 1 : 0;
162
s << (quint8) item.type;
163
s << item.childStart;
165
s << item.nsURIIndex;
171
static QDataStream& operator>>(QDataStream& s, KoXmlPackedItem& item)
182
s >> item.nsURIIndex;
185
item.attr = (flag != 0);
186
item.type = (KoXmlNode::NodeType) type;
187
item.childStart = child;
194
#ifdef KOXML_COMPRESS
196
// ==================================================================
200
// ==================================================================
203
// similar to QVector, but using LZF compression to save memory space
204
// this class is however not reentrant
206
// comment it to test this class without the compression
207
#define KOXMLVECTOR_USE_LZF
209
// when number of buffered items reach this, compression will start
210
// small value will give better memory usage at the cost of speed
211
// bigger value will be better in term of speed, but use more memory
212
#define ITEMS_FULL (1*256)
214
// LZF stuff is wrapper in KoLZF
215
#ifdef KOXML_COMPRESS
216
#ifdef KOXMLVECTOR_USE_LZF
219
#define HASH_SIZE (1<< HASH_LOG)
220
#define HASH_MASK (HASH_SIZE-1)
222
#define UPDATE_HASH(v,p) { v = *((quint16*)p); v ^= *((quint16*)(p+1))^(v>>(16-HASH_LOG)); }
225
#define MAX_LEN 264 /* 256 + 8 */
226
#define MAX_DISTANCE 8192
228
// Lossless compression using LZF algorithm, this is faster on modern CPU than
229
// the original implementation in http://liblzf.plan9.de/
230
static int lzff_compress(const void* input, int length, void* output, int maxout)
234
const quint8* ip = (const quint8*) input;
235
const quint8* ip_limit = ip + length - MAX_COPY - 4;
236
quint8* op = (quint8*) output;
238
const quint8* htab[HASH_SIZE];
239
const quint8** hslot;
248
/* initializes hash table */
249
for (hslot = htab; hslot < htab + HASH_SIZE; hslot++)
252
/* we start with literal copy */
254
*op++ = MAX_COPY - 1;
257
while (ip < ip_limit) {
258
/* find potential match */
259
UPDATE_HASH(hval, ip);
260
hslot = htab + (hval & HASH_MASK);
261
ref = (quint8*) * hslot;
263
/* update hash table */
266
/* find itself? then it's no match */
270
/* is this a match? check the first 2 bytes */
271
if (*((quint16*)ref) != *((quint16*)ip))
274
/* now check the 3rd byte */
278
/* calculate distance to the match */
281
/* skip if too far away */
282
if (distance >= MAX_DISTANCE)
285
/* here we have 3-byte matches */
286
anchor = (quint8*)ip;
291
/* now we have to check how long the match is */
292
if (ip < ip_limit - MAX_LEN) {
293
while (len < MAX_LEN - 8) {
295
if (*ref++ != *ip++) break;
296
if (*ref++ != *ip++) break;
297
if (*ref++ != *ip++) break;
298
if (*ref++ != *ip++) break;
299
if (*ref++ != *ip++) break;
300
if (*ref++ != *ip++) break;
301
if (*ref++ != *ip++) break;
302
if (*ref++ != *ip++) break;
309
/* just before the last non-matching byte */
312
/* if we have copied something, adjust the copy count */
314
/* copy is biased, '0' means 1 byte copy */
315
anchor = anchor - copy - 1;
316
*(op - copy - 1) = copy - 1;
319
/* back, to overwrite the copy count */
322
/* length is biased, '1' means a match of 3 bytes */
325
/* distance is also biased */
328
/* encode the match */
330
*op++ = (len << 5) + (distance >> 8);
332
*op++ = (7 << 5) + (distance >> 8);
335
*op++ = (distance & 255);
337
/* assuming next will be literal copy */
338
*op++ = MAX_COPY - 1;
340
/* update the hash at match boundary */
342
UPDATE_HASH(hval, ip);
343
htab[hval & HASH_MASK] = ip;
351
if (copy >= MAX_COPY) {
353
*op++ = MAX_COPY - 1;
357
/* left-over as literal copy */
358
ip_limit = (const quint8*)input + length;
359
while (ip < ip_limit) {
362
if (copy == MAX_COPY) {
364
*op++ = MAX_COPY - 1;
368
/* if we have copied something, adjust the copy length */
370
*(op - copy - 1) = copy - 1;
374
return op - (quint8*)output;
377
static int lzff_decompress(const void* input, int length, void* output, int maxout)
379
const quint8* ip = (const quint8*) input;
380
const quint8* ip_limit = ip + length - 1;
381
quint8* op = (quint8*) output;
382
quint8* op_limit = op + maxout;
385
while (ip < ip_limit) {
386
quint32 ctrl = (*ip) + 1;
387
quint32 ofs = ((*ip) & 31) << 8;
388
quint32 len = (*ip++) >> 5;
392
if (op + ctrl > op_limit)
395
/* crazy unrolling */
424
if (op + len + 3 > op_limit)
427
if (ref < (quint8 *)output)
439
return op - (quint8*)output;
445
static QByteArray compress(const QByteArray&);
446
static void decompress(const QByteArray&, QByteArray&);
449
QByteArray KoLZF::compress(const QByteArray& input)
451
const void* const in_data = (const void*) input.constData();
452
unsigned int in_len = (unsigned int)input.size();
455
output.resize(in_len + 4 + 1);
457
// we use 4 bytes to store uncompressed length
458
// and 1 extra byte as flag (0=uncompressed, 1=compressed)
459
output[0] = in_len & 255;
460
output[1] = (in_len >> 8) & 255;
461
output[2] = (in_len >> 16) & 255;
462
output[3] = (in_len >> 24) & 255;
465
unsigned int out_len = in_len - 1;
466
unsigned char* out_data = (unsigned char*) output.data() + 5;
468
unsigned int len = lzff_compress(in_data, in_len, out_data, out_len);
471
if ((len > out_len) || (len == 0)) {
472
// output buffer is too small, likely because the data can't
473
// be compressed. so here just copy without compression
475
output.insert(5, input);
477
// flag must indicate "uncompressed block"
482
output.resize(out_len + 4 + 1);
488
// will not squeeze output
489
void KoLZF::decompress(const QByteArray& input, QByteArray& output)
491
// read out first how big is the uncompressed size
492
unsigned int unpack_size = 0;
493
unpack_size |= ((quint8)input[0]);
494
unpack_size |= ((quint8)input[1]) << 8;
495
unpack_size |= ((quint8)input[2]) << 16;
496
unpack_size |= ((quint8)input[3]) << 24;
498
// prepare the output
499
output.reserve(unpack_size);
502
quint8 flag = (quint8)input[4];
505
const void* const in_data = (const void*)(input.constData() + 5);
506
unsigned int in_len = (unsigned int)input.size() - 5;
507
unsigned char* out_data = (unsigned char*) output.data();
508
unsigned int out_len = (unsigned int)unpack_size;
511
memcpy(output.data(), in_data, in_len);
513
unsigned int len = lzff_decompress(in_data, in_len, out_data, out_len);
523
template <typename T>
528
QVector<unsigned> startIndex;
529
QVector<QByteArray> blocks;
531
unsigned bufferStartIndex;
532
QVector<T> bufferItems;
533
QByteArray bufferData;
536
// fetch given item index to the buffer
537
// will INVALIDATE all references to the buffer
538
void fetchItem(unsigned index) {
539
// already in the buffer ?
540
if (index >= bufferStartIndex)
541
if (index - bufferStartIndex < (unsigned)bufferItems.count())
544
// search in the stored blocks
545
// TODO: binary search to speed up
546
int loc = startIndex.count() - 1;
547
for (int c = 0; c < startIndex.count() - 1; c++)
548
if (index >= startIndex[c])
549
if (index < startIndex[c+1]) {
554
bufferStartIndex = startIndex[loc];
555
#ifdef KOXMLVECTOR_USE_LZF
556
KoLZF::decompress(blocks[loc], bufferData);
558
bufferData = blocks[loc];
560
QBuffer buffer(&bufferData);
561
buffer.open(QIODevice::ReadOnly);
562
QDataStream in(&buffer);
567
// store data in the buffer to main blocks
570
buffer.open(QIODevice::WriteOnly);
571
QDataStream out(&buffer);
574
startIndex.append(bufferStartIndex);
575
#ifdef KOXMLVECTOR_USE_LZF
576
blocks.append(KoLZF::compress(buffer.data()));
578
blocks.append(buffer.data());
581
bufferStartIndex += bufferItems.count();
586
inline KoXmlVector(): totalItems(0), bufferStartIndex(0) {};
593
bufferStartIndex = 0;
595
bufferData.reserve(1024*1024);
598
inline int count() const {
599
return (int)totalItems;
601
inline int size() const {
602
return (int)totalItems;
604
inline bool isEmpty() const {
605
return totalItems == 0;
609
// WARNING: use the return value as soon as possible
610
// it may be invalid if another function is invoked
613
if (bufferItems.count() >= ITEMS_FULL - 1)
617
bufferItems.resize(bufferItems.count() + 1);
618
return bufferItems[bufferItems.count()-1];
621
// WARNING: use the return value as soon as possible
622
// it may be invalid if another function is invoked
623
const T &operator[](int i) const {
624
((KoXmlVector*)this)->fetchItem((unsigned)i);
625
return bufferItems[i - bufferStartIndex];
628
// optimize memory usage
629
// will INVALIDATE all references to the buffer
638
// ==================================================================
640
// KoXmlPackedDocument
642
// ==================================================================
644
#ifdef KOXML_COMPRESS
645
typedef KoXmlVector<KoXmlPackedItem> KoXmlPackedGroup;
647
typedef QVector<KoXmlPackedItem> KoXmlPackedGroup;
650
// growth strategy: increase every GROUP_GROW_SIZE items
651
// this will override standard QVector's growth strategy
652
#define GROUP_GROW_SHIFT 3
653
#define GROUP_GROW_SIZE (1 << GROUP_GROW_SHIFT)
655
class KoXmlPackedDocument
658
bool processNamespace;
660
// map given depth to the list of items
661
QHash<int, KoXmlPackedGroup> groups;
663
QVector<KoXmlPackedItem> items;
666
QStringList stringList;
670
QHash<QString, unsigned> stringHash;
672
unsigned cacheString(const QString& str) {
676
const unsigned& ii = stringHash[str];
680
// not yet declared, so we add it
681
unsigned i = stringList.count();
682
stringList.append(str);
683
stringHash.insert(str, i);
688
QHash<QString, unsigned> valueHash;
689
QStringList valueList;
691
QString cacheValue(const QString& value) {
695
const unsigned& ii = valueHash[value];
697
return valueList[ii];
699
// not yet declared, so we add it
700
unsigned i = valueList.count();
701
valueList.append(value);
702
valueHash.insert(value, i);
709
const KoXmlPackedItem& itemAt(unsigned depth, unsigned index) {
710
KoXmlPackedGroup& group = groups[depth];
714
unsigned itemCount(unsigned depth) {
715
const KoXmlPackedGroup& group = groups[depth];
716
return group.count();
721
Function clear, newItem, addElement, addAttribute, addText,
722
addCData, addProcessing are all related. These are all necessary
723
for stateful manipulation of the document. See also the calls
724
to these function from KoXmlHandler.
726
The state itself is defined by the member variables
727
currentDepth and the groups (see above).
730
unsigned currentDepth;
732
KoXmlPackedItem& newItem(unsigned depth) {
733
KoXmlPackedGroup& group = groups[depth];
735
#ifdef KOXML_COMPRESS
736
KoXmlPackedItem& item = group.newItem();
739
if ((groups.size() % GROUP_GROW_SIZE) == 0)
740
group.reserve(GROUP_GROW_SIZE * (1 + (groups.size() >> GROUP_GROW_SHIFT)));
741
group.resize(group.count() + 1);
743
KoXmlPackedItem& item = group[group.count()-1];
746
// this is necessary, because intentionally we don't want to have
747
// a constructor for KoXmlPackedItem
749
item.type = KoXmlNode::NullNode;
752
item.childStart = itemCount(depth + 1);
768
cacheString(QString());
770
// first node is root
771
KoXmlPackedItem& rootItem = newItem(0);
772
rootItem.type = KoXmlNode::DocumentNode;
776
// won't be needed anymore
781
// optimize, see documentation on QVector::squeeze
782
for (int d = 0; d < groups.count(); d++) {
783
KoXmlPackedGroup& group = groups[d];
788
// in case namespace processing, 'name' contains the prefix already
789
void addElement(const QString& name, const QString& nsURI) {
790
KoXmlPackedItem& item = newItem(currentDepth + 1);
791
item.type = KoXmlNode::ElementNode;
792
item.nameIndex = cacheString(name);
793
item.nsURIIndex = cacheString(nsURI);
798
void closeElement() {
802
void addDTD(const QString& dt) {
806
void addAttribute(const QString& name, const QString& nsURI, const QString& value) {
807
KoXmlPackedItem& item = newItem(currentDepth + 1);
809
item.nameIndex = cacheString(name);
810
item.nsURIIndex = cacheString(nsURI);
811
//item.value = cacheValue( value );
815
void addText(const QString& text) {
816
KoXmlPackedItem& item = newItem(currentDepth + 1);
817
item.type = KoXmlNode::TextNode;
821
void addCData(const QString& text) {
822
KoXmlPackedItem& item = newItem(currentDepth + 1);
823
item.type = KoXmlNode::CDATASectionNode;
827
void addProcessingInstruction() {
828
KoXmlPackedItem& item = newItem(currentDepth + 1);
829
item.type = KoXmlNode::ProcessingInstructionNode;
833
KoXmlPackedDocument(): processNamespace(false), currentDepth(0) {
840
unsigned elementDepth;
844
KoXmlPackedItem& newItem() {
845
unsigned count = items.count() + 512;
846
count = 1024 * (count >> 10);
847
items.reserve(count);
849
items.resize(items.count() + 1);
851
// this is necessary, because intentionally we don't want to have
852
// a constructor for KoXmlPackedItem
853
KoXmlPackedItem& item = items[items.count()-1];
855
item.type = KoXmlNode::NullNode;
863
void addElement(const QString& name, const QString& nsURI) {
864
// we are going one level deeper
867
KoXmlPackedItem& item = newItem();
870
item.type = KoXmlNode::ElementNode;
871
item.depth = elementDepth;
872
item.nameIndex = cacheString(name);
873
item.nsURIIndex = cacheString(nsURI);
876
void closeElement() {
877
// we are going up one level
881
void addAttribute(const QString& name, const QString& nsURI, const QString& value) {
882
KoXmlPackedItem& item = newItem();
885
item.type = KoXmlNode::NullNode;
886
item.depth = elementDepth;
887
item.nameIndex = cacheString(name);
888
item.nsURIIndex = cacheString(nsURI);
889
//item.value = cacheValue( value );
893
void addText(const QString& str) {
894
KoXmlPackedItem& item = newItem();
897
item.type = KoXmlNode::TextNode;
898
item.depth = elementDepth + 1;
904
void addCData(const QString& str) {
905
KoXmlPackedItem& item = newItem();
908
item.type = KoXmlNode::CDATASectionNode;
909
item.depth = elementDepth + 1;
915
void addProcessingInstruction() {
916
KoXmlPackedItem& item = newItem();
919
item.type = KoXmlNode::ProcessingInstructionNode;
920
item.depth = elementDepth + 1;
937
KoXmlPackedItem& rootItem = newItem();
938
rootItem.attr = false;
939
rootItem.type = KoXmlNode::DocumentNode;
941
rootItem.nsURIIndex = 0;
942
rootItem.nameIndex = 0;
952
KoXmlPackedDocument(): processNamespace(false), elementDepth(0) {
959
// ==================================================================
963
// ==================================================================
965
class KoXmlHandler : public QXmlDefaultHandler
968
KoXmlHandler(KoXmlPackedDocument* doc);
971
bool startDocument();
974
bool startElement(const QString& nsURI, const QString& localName,
975
const QString& qName, const QXmlAttributes& atts);
976
bool endElement(const QString& nsURI, const QString& localName,
977
const QString& qName);
979
bool characters(const QString& ch);
980
bool processingInstruction(const QString& target, const QString& data);
981
bool skippedEntity(const QString& name);
986
bool startEntity(const QString &);
987
bool endEntity(const QString &);
988
bool startDTD(const QString& name, const QString& publicId,
989
const QString& systemId);
990
bool comment(const QString& ch);
993
bool externalEntityDecl(const QString &name, const QString &publicId,
994
const QString &systemId) ;
997
bool notationDecl(const QString & name, const QString & publicId,
998
const QString & systemId);
999
bool unparsedEntityDecl(const QString &name, const QString &publicId,
1000
const QString &systemId, const QString ¬ationName) ;
1003
bool fatalError(const QXmlParseException& exception);
1010
KoXmlPackedDocument* document;
1011
bool processNamespace;
1017
KoXmlHandler::KoXmlHandler(KoXmlPackedDocument* doc) : QXmlDefaultHandler(),
1018
errorLine(0), errorColumn(0), document(doc),
1019
processNamespace(doc->processNamespace), cdata(false)
1023
bool KoXmlHandler::startDocument()
1037
bool KoXmlHandler::endDocument()
1043
bool KoXmlHandler::startDTD(const QString& name, const QString& publicId,
1044
const QString& systemId)
1049
document->addDTD(name);
1054
bool KoXmlHandler::startElement(const QString& nsURI, const QString& localName,
1055
const QString& name, const QXmlAttributes& atts)
1057
Q_UNUSED(localName);
1059
document->addElement(name, nsURI);
1061
// add all attributes
1062
for (int c = 0; c < atts.length(); c++)
1063
document->addAttribute(atts.qName(c), atts.uri(c), atts.value(c));
1068
bool KoXmlHandler::endElement(const QString& nsURI, const QString& localName,
1069
const QString& qName)
1072
Q_UNUSED(localName);
1075
document->closeElement();
1080
bool KoXmlHandler::characters(const QString& str)
1082
// are we inside entity ?
1083
if (!entityName.isEmpty()) {
1084
// we do not handle entity but need to keep track of it
1085
// because we want to skip it alltogether
1089
// add a new text or CDATA
1091
document->addCData(str);
1093
document->addText(str);
1098
bool KoXmlHandler::processingInstruction(const QString& target,
1099
const QString& data)
1104
document->addProcessingInstruction();
1109
bool KoXmlHandler::skippedEntity(const QString& name)
1117
bool KoXmlHandler::startCDATA()
1123
bool KoXmlHandler::endCDATA()
1129
bool KoXmlHandler::startEntity(const QString& name)
1135
bool KoXmlHandler::endEntity(const QString& name)
1142
bool KoXmlHandler::comment(const QString& comment)
1150
bool KoXmlHandler::unparsedEntityDecl(const QString &name,
1151
const QString &publicId, const QString &systemId, const QString ¬ationName)
1156
Q_UNUSED(notationName);
1162
bool KoXmlHandler::externalEntityDecl(const QString &name,
1163
const QString &publicId, const QString &systemId)
1173
bool KoXmlHandler::notationDecl(const QString & name,
1174
const QString & publicId, const QString & systemId)
1180
// we skip notation node
1184
bool KoXmlHandler::fatalError(const QXmlParseException& exception)
1186
errorMsg = exception.message();
1187
errorLine = exception.lineNumber();
1188
errorColumn = exception.columnNumber();
1189
return QXmlDefaultHandler::fatalError(exception);
1193
// ==================================================================
1197
// ==================================================================
1206
// generic properties
1207
KoXmlNode::NodeType nodeType;
1209
QString namespaceURI;
1213
#ifdef KOXML_COMPACT
1217
// reference counting
1218
unsigned long count;
1223
if (this == &null) return; if (!--count) delete this;
1228
QString nodeName() const;
1230
// for tree and linked-list
1231
KoXmlNodeData* parent;
1232
KoXmlNodeData* prev;
1233
KoXmlNodeData* next;
1234
KoXmlNodeData* first;
1235
KoXmlNodeData* last;
1239
// node manipulation
1240
void appendChild(KoXmlNodeData* child);
1244
inline void setAttribute(const QString& name, const QString& value);
1245
inline QString attribute(const QString& name, const QString& def) const;
1246
inline bool hasAttribute(const QString& name) const;
1247
inline void setAttributeNS(const QString& nsURI, const QString& name, const QString& value);
1248
inline QString attributeNS(const QString& nsURI, const QString& name, const QString& def) const;
1249
inline bool hasAttributeNS(const QString& nsURI, const QString& name) const;
1250
inline void clearAttributes();
1251
inline QStringList attributeNames() const;
1253
// for text and CDATA
1254
QString data() const;
1255
void setData(const QString& data);
1257
// reference from within the packed doc
1258
KoXmlPackedDocument* packedDoc;
1259
unsigned long nodeIndex;
1261
// for document node
1262
bool setContent(QXmlInputSource* source, QXmlReader* reader,
1263
QString* errorMsg = 0, int* errorLine = 0, int* errorColumn = 0);
1264
bool setContent(QXmlInputSource* source, bool namespaceProcessing,
1265
QString* errorMsg = 0, int* errorLine = 0, int* errorColumn = 0);
1267
// used when doing on-demand (re)parse
1269
void loadChildren(int depth = 1);
1270
void unloadChildren();
1274
static KoXmlNodeData null;
1277
QDomNode asQDomNode(QDomDocument ownerDoc) const;
1280
QHash<QString, QString> attr;
1281
QHash<KoXmlStringPair, QString> attrNS;
1283
friend class KoXmlHandler;
1284
friend class KoXmlElement;
1287
KoXmlNodeData KoXmlNodeData::null;
1289
KoXmlNodeData::KoXmlNodeData() : nodeType(KoXmlNode::NullNode),
1290
#ifdef KOXML_COMPACT
1293
count(1), emptyDocument(true), parent(0), prev(0), next(0), first(0), last(0),
1294
packedDoc(0), nodeIndex(0), loaded(false)
1298
KoXmlNodeData::~KoXmlNodeData()
1303
void KoXmlNodeData::clear()
1306
for (KoXmlNodeData* node = first; node ;) {
1307
KoXmlNodeData* next = node->next;
1312
// only document can delete these
1313
// normal nodes don't "own" them
1314
if (nodeType == KoXmlNode::DocumentNode)
1317
nodeType = KoXmlNode::NullNode;
1320
namespaceURI.clear();
1334
QString KoXmlNodeData::text()
1340
KoXmlNodeData* node = first;
1342
switch (node->nodeType) {
1343
case KoXmlNode::ElementNode:
1344
t += node->text(); break;
1345
case KoXmlNode::TextNode:
1346
t += node->data(); break;
1347
case KoXmlNode::CDATASectionNode:
1348
t += node->data(); break;
1357
QString KoXmlNodeData::nodeName() const
1360
case KoXmlNode::ElementNode: {
1362
if (!prefix.isEmpty())
1363
n.prepend(':').prepend(prefix);
1368
case KoXmlNode::TextNode: return QLatin1String("#text");
1369
case KoXmlNode::CDATASectionNode: return QLatin1String("#cdata-section");
1370
case KoXmlNode::DocumentNode: return QLatin1String("#document");
1371
case KoXmlNode::DocumentTypeNode: return tagName;
1373
default: return QString(); break;
1376
// should not happen
1380
void KoXmlNodeData::appendChild(KoXmlNodeData* node)
1382
node->parent = this;
1384
first = last = node;
1393
void KoXmlNodeData::setAttribute(const QString& name, const QString& value)
1395
attr[ name ] = value;
1398
QString KoXmlNodeData::attribute(const QString& name, const QString& def) const
1400
if (attr.contains(name))
1401
return attr[ name ];
1406
bool KoXmlNodeData::hasAttribute(const QString& name) const
1408
return attr.contains(name);
1411
void KoXmlNodeData::setAttributeNS(const QString& nsURI,
1412
const QString& name, const QString& value)
1415
QString localName = name;
1416
int i = name.indexOf(':');
1418
localName = name.mid(i + 1);
1419
prefix = name.left(i);
1422
if (prefix.isNull()) return;
1424
KoXmlStringPair key(nsURI, localName);
1425
attrNS[ key ] = value;
1428
QString KoXmlNodeData::attributeNS(const QString& nsURI, const QString& name,
1429
const QString& def) const
1431
KoXmlStringPair key(nsURI, name);
1432
if (attrNS.contains(key))
1433
return attrNS[ key ];
1438
bool KoXmlNodeData::hasAttributeNS(const QString& nsURI, const QString& name) const
1440
KoXmlStringPair key(nsURI, name);
1441
return attrNS.contains(key);
1444
void KoXmlNodeData::clearAttributes()
1450
// FIXME how about namespaced attributes ?
1451
QStringList KoXmlNodeData::attributeNames() const
1454
result = attr.keys();
1459
QString KoXmlNodeData::data() const
1464
void KoXmlNodeData::setData(const QString& d)
1469
bool KoXmlNodeData::setContent(QXmlInputSource* source, bool namespaceProcessing,
1470
QString* errorMsg, int* errorLine, int* errorColumn)
1472
QXmlSimpleReader reader;
1473
reader.setFeature(QLatin1String("http://xml.org/sax/features/namespaces"), namespaceProcessing);
1474
reader.setFeature(QLatin1String("http://xml.org/sax/features/namespace-prefixes"), !namespaceProcessing);
1475
reader.setFeature(QLatin1String("http://trolltech.com/xml/features/report-whitespace-only-CharData"), false);
1476
return setContent(source, &reader, errorMsg, errorLine, errorColumn);
1479
bool KoXmlNodeData::setContent(QXmlInputSource* source,
1480
QXmlReader* reader, QString* errorMsg, int* errorLine, int* errorColumn)
1482
if (nodeType != KoXmlNode::DocumentNode)
1486
nodeType = KoXmlNode::DocumentNode;
1489
if (!source) return false;
1490
if (!reader) return false;
1493
packedDoc = new KoXmlPackedDocument;
1494
packedDoc->processNamespace = false;
1496
packedDoc->processNamespace =
1497
reader->feature("http://xml.org/sax/features/namespaces") &&
1498
!reader->feature("http://xml.org/sax/features/namespace-prefixes");
1500
KoXmlHandler handler(packedDoc);
1501
reader->setContentHandler(&handler);
1502
reader->setErrorHandler(&handler);
1503
reader->setLexicalHandler(&handler);
1504
reader->setDeclHandler(&handler);
1505
reader->setDTDHandler(&handler);
1507
bool result = reader->parse(source);
1509
// parsing error has occurred
1510
if (errorMsg) *errorMsg = handler.errorMsg;
1511
if (errorLine) *errorLine = handler.errorLine;
1512
if (errorColumn) *errorColumn = handler.errorColumn;
1523
#ifdef KOXML_COMPACT
1525
void KoXmlNodeData::loadChildren(int depth)
1528
if (!packedDoc) return;
1531
if (loaded && (depth <= 1)) return;
1533
// in case depth is different
1537
KoXmlNodeData* lastDat = 0;
1539
unsigned childStop = 0;
1540
if (nodeIndex == packedDoc->itemCount(nodeDepth) - 1)
1541
childStop = packedDoc->itemCount(nodeDepth + 1);
1543
const KoXmlPackedItem& next = packedDoc->itemAt(nodeDepth, nodeIndex + 1);
1544
childStop = next.childStart;
1547
const KoXmlPackedItem& self = packedDoc->itemAt(nodeDepth, nodeIndex);
1549
for (unsigned i = self.childStart; i < childStop; i++) {
1550
const KoXmlPackedItem& item = packedDoc->itemAt(nodeDepth + 1, i);
1551
bool textItem = (item.type == KoXmlNode::TextNode);
1552
textItem |= (item.type == KoXmlNode::CDATASectionNode);
1554
// attribute belongs to this node
1556
QString name = packedDoc->stringList[item.nameIndex];
1557
QString nsURI = packedDoc->stringList[item.nsURIIndex];
1558
QString value = item.value;
1562
QString qName; // with prefix
1563
QString localName; // without prefix, i.e. local name
1565
localName = qName = name;
1566
int i = qName.indexOf(':');
1567
if (i != -1) prefix = qName.left(i);
1568
if (i != -1) localName = qName.mid(i + 1);
1570
if (packedDoc->processNamespace) {
1571
setAttributeNS(nsURI, qName, value);
1572
setAttribute(localName, value);
1574
setAttribute(qName, value);
1576
QString name = packedDoc->stringList[item.nameIndex];
1577
QString nsURI = packedDoc->stringList[item.nsURIIndex];
1578
QString value = item.value;
1580
QString nodeName = name;
1584
if (packedDoc->processNamespace) {
1586
int di = name.indexOf(':');
1588
localName = name.mid(di + 1);
1589
prefix = name.left(di);
1591
nodeName = localName;
1594
// make a node out of this item
1595
KoXmlNodeData* dat = new KoXmlNodeData;
1597
dat->packedDoc = packedDoc;
1598
dat->nodeDepth = nodeDepth + 1;
1599
dat->nodeType = item.type;
1600
dat->tagName = nodeName;
1601
dat->localName = localName;
1602
dat->prefix = prefix;
1603
dat->namespaceURI = nsURI;
1606
dat->prev = lastDat;
1610
dat->loaded = false;
1611
dat->textData = (textItem) ? value : QString();
1613
// adjust our linked-list
1614
first = (first) ? first : dat;
1617
lastDat->next = dat;
1622
dat->loadChildren(depth - 1);
1631
void KoXmlNodeData::loadChildren(int depth)
1634
if (!packedDoc) return;
1637
if (loaded && (depth <= 1)) return;
1639
// cause we don't know how deep this node's children already loaded are
1642
KoXmlNodeData* lastDat = 0;
1643
int nodeDepth = packedDoc->items[nodeIndex].depth;
1645
for (int i = nodeIndex + 1; i < packedDoc->items.count(); i++) {
1646
KoXmlPackedItem& item = packedDoc->items[i];
1647
bool textItem = (item.type == KoXmlNode::TextNode);
1648
textItem |= (item.type == KoXmlNode::CDATASectionNode);
1650
// element already outside our depth
1651
if (!item.attr && (item.type == KoXmlNode::ElementNode))
1652
if (item.depth <= (unsigned)nodeDepth)
1655
// attribute belongs to this node
1656
if (item.attr && (item.depth == (unsigned)nodeDepth)) {
1657
QString name = packedDoc->stringList[item.nameIndex];
1658
QString nsURI = packedDoc->stringList[item.nsURIIndex];
1659
QString value = item.value;
1663
QString qName; // with prefix
1664
QString localName; // without prefix, i.e. local name
1666
localName = qName = name;
1667
int i = qName.indexOf(':');
1668
if (i != -1) prefix = qName.left(i);
1669
if (i != -1) localName = qName.mid(i + 1);
1671
if (packedDoc->processNamespace) {
1672
setAttributeNS(nsURI, qName, value);
1673
setAttribute(localName, value);
1675
setAttribute(name, value);
1680
bool instruction = (item.type == KoXmlNode::ProcessingInstructionNode);
1681
bool ok = (textItem || instruction) ? (item.depth == (unsigned)nodeDepth) : (item.depth == (unsigned)nodeDepth + 1);
1683
ok = (item.depth == (unsigned)nodeDepth + 1);
1686
QString name = packedDoc->stringList[item.nameIndex];
1687
QString nsURI = packedDoc->stringList[item.nsURIIndex];
1688
QString value = item.value;
1690
QString nodeName = name;
1694
if (packedDoc->processNamespace) {
1696
int di = name.indexOf(':');
1698
localName = name.mid(di + 1);
1699
prefix = name.left(di);
1701
nodeName = localName;
1704
// make a node out of this item
1705
KoXmlNodeData* dat = new KoXmlNodeData;
1707
dat->packedDoc = packedDoc;
1708
dat->nodeType = item.type;
1709
dat->tagName = nodeName;
1710
dat->localName = localName;
1711
dat->prefix = prefix;
1712
dat->namespaceURI = nsURI;
1715
dat->prev = lastDat;
1719
dat->loaded = false;
1720
dat->textData = (textItem) ? value : QString();
1722
// adjust our linked-list
1723
first = (first) ? first : dat;
1726
lastDat->next = dat;
1731
dat->loadChildren(depth - 1);
1740
void KoXmlNodeData::unloadChildren()
1743
if (!packedDoc) return;
1745
if (!loaded) return;
1748
for (KoXmlNodeData* node = first; node ;) {
1749
KoXmlNodeData* next = node->next;
1750
node->unloadChildren();
1760
#ifdef KOXML_COMPACT
1763
static QDomNode itemAsQDomNode(QDomDocument ownerDoc, KoXmlPackedDocument* packedDoc,
1764
unsigned nodeDepth, unsigned nodeIndex)
1770
const KoXmlPackedItem& self = packedDoc->itemAt(nodeDepth, nodeIndex);
1772
unsigned childStop = 0;
1773
if (nodeIndex == packedDoc->itemCount(nodeDepth) - 1)
1774
childStop = packedDoc->itemCount(nodeDepth + 1);
1776
const KoXmlPackedItem& next = packedDoc->itemAt(nodeDepth, nodeIndex + 1);
1777
childStop = next.childStart;
1780
// nothing to do here
1781
if (self.type == KoXmlNode::NullNode)
1784
// create the element properly
1785
if (self.type == KoXmlNode::ElementNode) {
1786
QDomElement element;
1788
QString name = packedDoc->stringList[self.nameIndex];
1789
QString nsURI = packedDoc->stringList[self.nsURIIndex];
1791
if (packedDoc->processNamespace)
1792
element = ownerDoc.createElementNS(nsURI, name);
1794
element = ownerDoc.createElement(name);
1796
// check all subnodes for attributes
1797
for (unsigned i = self.childStart; i < childStop; i++) {
1798
const KoXmlPackedItem& item = packedDoc->itemAt(nodeDepth + 1, i);
1799
bool textItem = (item.type == KoXmlNode::TextNode);
1800
textItem |= (item.type == KoXmlNode::CDATASectionNode);
1802
// attribute belongs to this node
1804
QString name = packedDoc->stringList[item.nameIndex];
1805
QString nsURI = packedDoc->stringList[item.nsURIIndex];
1806
QString value = item.value;
1810
QString qName; // with prefix
1811
QString localName; // without prefix, i.e. local name
1813
localName = qName = name;
1814
int i = qName.indexOf(':');
1815
if (i != -1) prefix = qName.left(i);
1816
if (i != -1) localName = qName.mid(i + 1);
1818
if (packedDoc->processNamespace) {
1819
element.setAttributeNS(nsURI, qName, value);
1820
element.setAttribute(localName, value);
1822
element.setAttribute(name, value);
1824
// add it recursively
1825
QDomNode childNode = itemAsQDomNode(ownerDoc, packedDoc, nodeDepth + 1, i);
1826
element.appendChild(childNode);
1833
// create the text node
1834
if (self.type == KoXmlNode::TextNode) {
1835
QString text = self.value;
1837
// FIXME: choose CDATA when the value contains special characters
1838
QDomText textNode = ownerDoc.createTextNode(text);
1842
// nothing matches? strange...
1846
QDomNode KoXmlNodeData::asQDomNode(QDomDocument ownerDoc) const
1848
return itemAsQDomNode(ownerDoc, packedDoc, nodeDepth, nodeIndex);
1853
static QDomNode itemAsQDomNode(QDomDocument ownerDoc, KoXmlPackedDocument* packedDoc,
1860
KoXmlPackedItem& item = packedDoc->items[nodeIndex];
1862
// nothing to do here
1863
if (item.type == KoXmlNode::NullNode)
1866
// create the element properly
1867
if (item.type == KoXmlNode::ElementNode) {
1868
QDomElement element;
1870
QString name = packedDoc->stringList[item.nameIndex];
1871
QString nsURI = packedDoc->stringList[item.nsURIIndex];
1873
if (packedDoc->processNamespace)
1874
element = ownerDoc.createElementNS(nsURI, name);
1876
element = ownerDoc.createElement(name);
1878
// check all subnodes for attributes
1879
int nodeDepth = item.depth;
1880
for (int i = nodeIndex + 1; i < packedDoc->items.count(); i++) {
1881
KoXmlPackedItem& item = packedDoc->items[i];
1882
bool textItem = (item.type == KoXmlNode::TextNode);
1883
textItem |= (item.type == KoXmlNode::CDATASectionNode);
1885
// element already outside our depth
1886
if (!item.attr && (item.type == KoXmlNode::ElementNode))
1887
if (item.depth <= (unsigned)nodeDepth)
1890
// attribute belongs to this node
1891
if (item.attr && (item.depth == (unsigned)nodeDepth)) {
1892
QString name = packedDoc->stringList[item.nameIndex];
1893
QString nsURI = packedDoc->stringList[item.nsURIIndex];
1894
QString value = item.value;
1897
QString qName; // with prefix
1898
QString localName; // without prefix, i.e. local name
1900
localName = qName = name;
1901
int i = qName.indexOf(':');
1902
if (i != -1) prefix = qName.left(i);
1903
if (i != -1) localName = qName.mid(i + 1);
1905
if (packedDoc->processNamespace) {
1906
element.setAttributeNS(nsURI, qName, value);
1907
element.setAttribute(localName, value);
1909
element.setAttribute(name, value);
1912
// direct child of this node
1913
if (!item.attr && (item.depth == (unsigned)nodeDepth + 1)) {
1914
// add it recursively
1915
QDomNode childNode = itemAsQDomNode(ownerDoc, packedDoc, i);
1916
element.appendChild(childNode);
1923
// create the text node
1924
if (item.type == KoXmlNode::TextNode) {
1925
QString text = item.value;
1926
// FIXME: choose CDATA when the value contains special characters
1927
QDomText textNode = ownerDoc.createTextNode(text);
1931
// nothing matches? strange...
1935
QDomNode KoXmlNodeData::asQDomNode(QDomDocument ownerDoc) const
1937
return itemAsQDomNode(ownerDoc, packedDoc, nodeIndex);
1942
void KoXmlNodeData::dump()
1944
printf("NodeData %p\n", (void*)this);
1946
printf(" nodeIndex: %d\n", (int)nodeIndex);
1947
printf(" packedDoc: %p\n", (void*)packedDoc);
1949
printf(" nodeType : %d\n", (int)nodeType);
1950
printf(" tagName: %s\n", qPrintable(tagName));
1951
printf(" namespaceURI: %s\n", qPrintable(namespaceURI));
1952
printf(" prefix: %s\n", qPrintable(prefix));
1953
printf(" localName: %s\n", qPrintable(localName));
1955
printf(" parent : %p\n", (void*)parent);
1956
printf(" prev : %p\n", (void*)prev);
1957
printf(" next : %p\n", (void*)next);
1958
printf(" first : %p\n", (void*)first);
1959
printf(" last : %p\n", (void*)last);
1961
printf(" count: %ld\n", count);
1964
printf(" loaded: TRUE\n");
1966
printf(" loaded: FALSE\n");
1969
// ==================================================================
1973
// ==================================================================
1975
// Creates a null node
1976
KoXmlNode::KoXmlNode()
1978
d = &KoXmlNodeData::null;
1981
// Destroys this node
1982
KoXmlNode::~KoXmlNode()
1985
if (d != &KoXmlNodeData::null)
1991
// Creates a copy of another node
1992
KoXmlNode::KoXmlNode(const KoXmlNode& node)
1998
// Creates a node for specific implementation
1999
KoXmlNode::KoXmlNode(KoXmlNodeData* data)
2005
// Creates a shallow copy of another node
2006
KoXmlNode& KoXmlNode::operator=(const KoXmlNode & node)
2014
// Note: two null nodes are always equal
2015
bool KoXmlNode::operator==(const KoXmlNode& node) const
2017
if (isNull() && node.isNull()) return true;
2018
return(d == node.d);
2021
// Note: two null nodes are always equal
2022
bool KoXmlNode::operator!=(const KoXmlNode& node) const
2024
if (isNull() && !node.isNull()) return true;
2025
if (!isNull() && node.isNull()) return true;
2026
if (isNull() && node.isNull()) return false;
2027
return(d != node.d);
2030
KoXmlNode::NodeType KoXmlNode::nodeType() const
2035
bool KoXmlNode::isNull() const
2037
return d->nodeType == NullNode;
2040
bool KoXmlNode::isElement() const
2042
return d->nodeType == ElementNode;
2045
bool KoXmlNode::isText() const
2047
return (d->nodeType == TextNode) || isCDATASection();
2050
bool KoXmlNode::isCDATASection() const
2052
return d->nodeType == CDATASectionNode;
2055
bool KoXmlNode::isDocument() const
2057
return d->nodeType == DocumentNode;
2060
bool KoXmlNode::isDocumentType() const
2062
return d->nodeType == DocumentTypeNode;
2065
void KoXmlNode::clear()
2068
d = new KoXmlNodeData;
2071
QString KoXmlNode::nodeName() const
2073
return d->nodeName();
2076
QString KoXmlNode::prefix() const
2078
return isElement() ? d->prefix : QString();
2081
QString KoXmlNode::namespaceURI() const
2083
return isElement() ? d->namespaceURI : QString();
2086
QString KoXmlNode::localName() const
2088
return isElement() ? d->localName : QString();
2091
KoXmlDocument KoXmlNode::ownerDocument() const
2093
KoXmlNodeData* node = d;
2094
while (node->parent) node = node->parent;
2096
return KoXmlDocument(node);
2099
KoXmlNode KoXmlNode::parentNode() const
2101
return d->parent ? KoXmlNode(d->parent) : KoXmlNode();
2104
bool KoXmlNode::hasChildNodes() const
2112
return d->first != 0 ;
2115
int KoXmlNode::childNodesCount() const
2123
KoXmlNodeData* node = d->first;
2133
QStringList KoXmlNode::attributeNames() const
2138
return d->attributeNames();
2141
KoXmlNode KoXmlNode::firstChild() const
2145
return d->first ? KoXmlNode(d->first) : KoXmlNode();
2148
KoXmlNode KoXmlNode::lastChild() const
2152
return d->last ? KoXmlNode(d->last) : KoXmlNode();
2155
KoXmlNode KoXmlNode::nextSibling() const
2157
return d->next ? KoXmlNode(d->next) : KoXmlNode();
2160
KoXmlNode KoXmlNode::previousSibling() const
2162
return d->prev ? KoXmlNode(d->prev) : KoXmlNode();
2165
KoXmlNode KoXmlNode::namedItem(const QString& name) const
2170
KoXmlNodeData* node = d->first;
2172
if (node->nodeName() == name)
2173
return KoXmlNode(node);
2181
KoXmlNode KoXmlNode::namedItemNS(const QString& nsURI, const QString& name) const
2187
KoXmlNodeData* node = d->first;
2189
if (node->namespaceURI == nsURI)
2190
if (node->localName == name)
2191
return KoXmlNode(node);
2199
KoXmlElement KoXmlNode::toElement() const
2201
return isElement() ? KoXmlElement(d) : KoXmlElement();
2204
KoXmlText KoXmlNode::toText() const
2206
return isText() ? KoXmlText(d) : KoXmlText();
2209
KoXmlCDATASection KoXmlNode::toCDATASection() const
2211
return isCDATASection() ? KoXmlCDATASection(d) : KoXmlCDATASection();
2214
KoXmlDocument KoXmlNode::toDocument() const
2217
return KoXmlDocument(d);
2219
KoXmlDocument newDocument;
2220
newDocument.d->emptyDocument = false;
2224
void KoXmlNode::load(int depth)
2226
d->loadChildren(depth);
2229
void KoXmlNode::unload()
2231
d->unloadChildren();
2234
QDomNode KoXmlNode::asQDomNode(QDomDocument ownerDoc) const
2236
return d->asQDomNode(ownerDoc);
2239
// ==================================================================
2243
// ==================================================================
2245
// Creates an empty element
2246
KoXmlElement::KoXmlElement(): KoXmlNode(new KoXmlNodeData)
2248
// because referenced also once in KoXmlNode constructor
2252
KoXmlElement::~KoXmlElement()
2255
if (d != &KoXmlNodeData::null)
2261
// Creates a shallow copy of another element
2262
KoXmlElement::KoXmlElement(const KoXmlElement& element): KoXmlNode(element.d)
2266
KoXmlElement::KoXmlElement(KoXmlNodeData* data): KoXmlNode(data)
2270
// Copies another element
2271
KoXmlElement& KoXmlElement::operator=(const KoXmlElement & element)
2273
KoXmlNode::operator=(element);
2277
bool KoXmlElement::operator== (const KoXmlElement& element) const
2279
if (isNull() || element.isNull()) return false;
2280
return (d == element.d);
2283
bool KoXmlElement::operator!= (const KoXmlElement& element) const
2285
if (isNull() && element.isNull()) return false;
2286
if (isNull() || element.isNull()) return true;
2287
return (d != element.d);
2290
QString KoXmlElement::tagName() const
2292
return isElement() ? ((KoXmlNodeData*)d)->tagName : QString();
2295
QString KoXmlElement::text() const
2300
QString KoXmlElement::attribute(const QString& name) const
2308
return d->attribute(name, QString());
2311
QString KoXmlElement::attribute(const QString& name,
2312
const QString& defaultValue) const
2315
return defaultValue;
2320
return d->attribute(name, defaultValue);
2323
QString KoXmlElement::attributeNS(const QString& namespaceURI,
2324
const QString& localName, const QString& defaultValue) const
2327
return defaultValue;
2332
KoXmlStringPair key(namespaceURI, localName);
2333
if (d->attrNS.contains(key))
2334
return d->attrNS[ key ];
2336
return defaultValue;
2338
// return d->attributeNS( namespaceURI, localName, defaultValue );
2341
bool KoXmlElement::hasAttribute(const QString& name) const
2346
return isElement() ? d->hasAttribute(name) : false;
2349
bool KoXmlElement::hasAttributeNS(const QString& namespaceURI,
2350
const QString& localName) const
2355
return isElement() ? d->hasAttributeNS(namespaceURI, localName) : false;
2358
// ==================================================================
2362
// ==================================================================
2364
KoXmlText::KoXmlText(): KoXmlNode(new KoXmlNodeData)
2366
// because referenced also once in KoXmlNode constructor
2370
KoXmlText::~KoXmlText()
2373
if (d != &KoXmlNodeData::null)
2379
KoXmlText::KoXmlText(const KoXmlText& text): KoXmlNode(text.d)
2383
KoXmlText::KoXmlText(KoXmlNodeData* data): KoXmlNode(data)
2387
bool KoXmlText::isText() const
2392
QString KoXmlText::data() const
2397
KoXmlText& KoXmlText::operator=(const KoXmlText & element)
2399
KoXmlNode::operator=(element);
2403
// ==================================================================
2405
// KoXmlCDATASection
2407
// ==================================================================
2409
KoXmlCDATASection::KoXmlCDATASection(): KoXmlText()
2411
d->nodeType = KoXmlNode::CDATASectionNode;
2414
KoXmlCDATASection::KoXmlCDATASection(const KoXmlCDATASection& cdata)
2420
KoXmlCDATASection::~KoXmlCDATASection()
2426
KoXmlCDATASection::KoXmlCDATASection(KoXmlNodeData* cdata):
2431
bool KoXmlCDATASection::isCDATASection() const
2436
KoXmlCDATASection& KoXmlCDATASection::operator=(const KoXmlCDATASection & cdata)
2438
KoXmlNode::operator=(cdata);
2442
// ==================================================================
2444
// KoXmlDocumentType
2446
// ==================================================================
2448
KoXmlDocumentType::KoXmlDocumentType(): KoXmlNode(new KoXmlNodeData)
2450
// because referenced also once in KoXmlNode constructor
2454
KoXmlDocumentType::~KoXmlDocumentType()
2460
KoXmlDocumentType::KoXmlDocumentType(const KoXmlDocumentType& dt):
2465
QString KoXmlDocumentType::name() const
2470
KoXmlDocumentType::KoXmlDocumentType(KoXmlNodeData* dt): KoXmlNode(dt)
2474
KoXmlDocumentType& KoXmlDocumentType::operator=(const KoXmlDocumentType & dt)
2476
KoXmlNode::operator=(dt);
2480
// ==================================================================
2484
// ==================================================================
2486
KoXmlDocument::KoXmlDocument(): KoXmlNode()
2488
d->emptyDocument = false;
2491
KoXmlDocument::~KoXmlDocument()
2494
if (d != &KoXmlNodeData::null)
2500
KoXmlDocument::KoXmlDocument(KoXmlNodeData* data): KoXmlNode(data)
2502
d->emptyDocument = true;
2505
// Creates a copy of another document
2506
KoXmlDocument::KoXmlDocument(const KoXmlDocument& doc): KoXmlNode(doc.d)
2510
// Creates a shallow copy of another document
2511
KoXmlDocument& KoXmlDocument::operator=(const KoXmlDocument & doc)
2513
KoXmlNode::operator=(doc);
2517
// Checks if this document and doc are equals
2518
bool KoXmlDocument::operator==(const KoXmlDocument& doc) const
2523
// Checks if this document and doc are not equals
2524
bool KoXmlDocument::operator!=(const KoXmlDocument& doc) const
2529
KoXmlElement KoXmlDocument::documentElement() const
2533
for (KoXmlNodeData* node = d->first; node;) {
2534
if (node->nodeType == KoXmlNode::ElementNode)
2535
return KoXmlElement(node);
2536
else node = node->next;
2539
return KoXmlElement();
2542
KoXmlDocumentType KoXmlDocument::doctype() const
2547
QString KoXmlDocument::nodeName() const
2549
if (d->emptyDocument)
2550
return QLatin1String("#document");
2555
void KoXmlDocument::clear()
2558
d->emptyDocument = false;
2561
bool KoXmlDocument::setContent(QXmlInputSource *source, QXmlReader *reader,
2562
QString* errorMsg, int* errorLine, int* errorColumn)
2564
if (d->nodeType != KoXmlNode::DocumentNode) {
2566
d = new KoXmlNodeData;
2567
d->nodeType = KoXmlNode::DocumentNode;
2570
dt = KoXmlDocumentType();
2571
bool result = d->setContent(source, reader, errorMsg, errorLine, errorColumn);
2572
if (result && !isNull()) {
2573
dt.d->nodeType = KoXmlNode::DocumentTypeNode;
2574
dt.d->tagName = d->packedDoc->docType;
2581
// no namespace processing
2582
bool KoXmlDocument::setContent(QIODevice* device, QString* errorMsg,
2583
int* errorLine, int* errorColumn)
2585
return setContent(device, false, errorMsg, errorLine, errorColumn);
2588
bool KoXmlDocument::setContent(QIODevice* device, bool namespaceProcessing,
2589
QString* errorMsg, int* errorLine, int* errorColumn)
2591
if (d->nodeType != KoXmlNode::DocumentNode) {
2593
d = new KoXmlNodeData;
2594
d->nodeType = KoXmlNode::DocumentNode;
2597
QXmlSimpleReader reader;
2598
reader.setFeature("http://xml.org/sax/features/namespaces", namespaceProcessing);
2599
reader.setFeature("http://xml.org/sax/features/namespace-prefixes", !namespaceProcessing);
2600
reader.setFeature(QLatin1String("http://trolltech.com/xml/features/report-whitespace-only-CharData"), false);
2602
// FIXME this hack is apparently private
2603
//reader.setUndefEntityInAttrHack(true);
2605
QXmlInputSource source(device);
2607
dt = KoXmlDocumentType();
2608
bool result = d->setContent(&source, &reader, errorMsg, errorLine, errorColumn);
2610
dt.d->nodeType = KoXmlNode::DocumentTypeNode;
2611
dt.d->tagName = d->packedDoc->docType;
2618
bool KoXmlDocument::setContent(const QByteArray& text, bool namespaceProcessing,
2619
QString *errorMsg, int *errorLine, int *errorColumn)
2622
buffer.setData(text);
2623
return setContent(&buffer, namespaceProcessing, errorMsg, errorLine, errorColumn);
2626
bool KoXmlDocument::setContent(const QString& text, bool namespaceProcessing,
2627
QString *errorMsg, int *errorLine, int *errorColumn)
2629
if (d->nodeType != KoXmlNode::DocumentNode) {
2631
d = new KoXmlNodeData;
2632
d->nodeType = KoXmlNode::DocumentNode;
2635
QXmlInputSource source;
2636
source.setData(text);
2638
dt = KoXmlDocumentType();
2639
bool result = d->setContent(&source, namespaceProcessing, errorMsg, errorLine, errorColumn);
2640
if (result && !isNull()) {
2641
dt.d->nodeType = KoXmlNode::DocumentTypeNode;
2642
dt.d->tagName = d->packedDoc->docType;
2649
bool KoXmlDocument::setContent(const QString& text,
2650
QString *errorMsg, int *errorLine, int *errorColumn)
2652
return setContent(text, false, errorMsg, errorLine, errorColumn);
2657
// ==================================================================
2661
// ==================================================================
2664
This is the size of buffer every time we read a chunk of data from the device.
2666
Note 1: maximum allocated space is thus 2*KOXML_BUFSIZE due to the
2667
stringData (a QString instance).
2668
TODO: use mmap to avoid double-buffering like this.
2670
Note 2: a much larger buffer won't speed up significantly. This is because
2671
the bottleneck is elsewhere, not here.
2675
#define KOXML_BUFSIZE 16*1024 // should be adequate
2677
KoXmlInputSource::KoXmlInputSource(QIODevice *dev): QXmlInputSource(),
2680
int mib = 106; // UTF-8
2681
decoder = QTextCodec::codecForMib(mib)->makeDecoder();
2685
buffer = new char[KOXML_BUFSIZE];
2690
KoXmlInputSource::~KoXmlInputSource()
2696
void KoXmlInputSource::setData(const QString& dat)
2701
void KoXmlInputSource::setData(const QByteArray& dat)
2706
void KoXmlInputSource::fetchData()
2710
QString KoXmlInputSource::data() const
2715
QChar KoXmlInputSource::next()
2717
if (stringIndex >= stringLength) {
2718
// read more data first
2719
qint64 bytes = device->read(buffer, KOXML_BUFSIZE);
2721
return EndOfDocument;
2723
stringData = decoder->toUnicode(buffer, bytes);
2724
stringLength = stringData.length();
2728
return stringData[stringIndex++];
2731
void KoXmlInputSource::reset()
2736
QString KoXmlInputSource::fromRawData(const QByteArray &data, bool beginning)
2739
Q_UNUSED(beginning);
2743
// ==================================================================
2745
// functions in KoXml namespace
2747
// ==================================================================
2749
KoXmlElement KoXml::namedItemNS(const KoXmlNode& node, const char* nsURI,
2750
const char* localName)
2752
#ifdef KOXML_USE_QDOM
2753
// David's solution for namedItemNS, only for QDom stuff
2754
KoXmlNode n = node.firstChild();
2755
for (; !n.isNull(); n = n.nextSibling()) {
2756
if (n.isElement() && n.localName() == localName &&
2757
n.namespaceURI() == nsURI)
2758
return n.toElement();
2760
return KoXmlElement();
2762
return node.namedItemNS(nsURI, localName).toElement();
2766
void KoXml::load(KoXmlNode& node, int depth)
2768
#ifdef KOXML_USE_QDOM
2769
// do nothing, QDom has no on-demand loading
2778
void KoXml::unload(KoXmlNode& node)
2780
#ifdef KOXML_USE_QDOM
2781
// do nothing, QDom has no on-demand unloading
2788
int KoXml::childNodesCount(const KoXmlNode& node)
2790
#ifdef KOXML_USE_QDOM
2791
return node.childNodes().count();
2793
// compatibility function, because no need to implement
2794
// a class like QDomNodeList
2795
return node.childNodesCount();
2799
QStringList KoXml::attributeNames(const KoXmlNode& node)
2801
#ifdef KOXML_USE_QDOM
2804
QDomNamedNodeMap attrMap = node.attributes();
2805
for (int i = 0; i < attrMap.count(); i++)
2806
result += attrMap.item(i).toAttr().name();
2810
// compatibility function, because no need to implement
2811
// a class like QDomNamedNodeMap
2812
return node.attributeNames();
2816
QDomNode KoXml::asQDomNode(QDomDocument ownerDoc, const KoXmlNode& node)
2818
#ifdef KOXML_USE_QDOM
2822
return node.asQDomNode(ownerDoc);
2826
QDomElement KoXml::asQDomElement(QDomDocument ownerDoc, const KoXmlElement& element)
2828
return KoXml::asQDomNode(ownerDoc, element).toElement();
2831
QDomDocument KoXml::asQDomDocument(QDomDocument ownerDoc, const KoXmlDocument& document)
2833
return KoXml::asQDomNode(ownerDoc, document).toDocument();
2836
bool KoXml::setDocument(KoXmlDocument& doc, QIODevice* device,
2837
bool namespaceProcessing, QString* errorMsg, int* errorLine,
2840
QXmlSimpleReader reader;
2841
reader.setFeature(QLatin1String("http://xml.org/sax/features/namespaces"), namespaceProcessing);
2842
reader.setFeature(QLatin1String("http://xml.org/sax/features/namespace-prefixes"), !namespaceProcessing);
2843
reader.setFeature(QLatin1String("http://trolltech.com/xml/features/report-whitespace-only-CharData"), false);
2845
KoXmlInputSource* source = new KoXmlInputSource(device);
2846
bool result = doc.setContent(source, &reader, errorMsg, errorLine, errorColumn);
2851
bool KoXml::setDocument(KoXmlDocument& doc, QIODevice* device,
2852
QXmlSimpleReader* reader, QString* errorMsg, int* errorLine, int* errorColumn)
2854
KoXmlInputSource* source = new KoXmlInputSource(device);
2855
bool result = doc.setContent(source, reader, errorMsg, errorLine, errorColumn);