~ubuntu-branches/ubuntu/precise/koffice/precise

« back to all changes in this revision

Viewing changes to libs/store/KoXmlReader.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Jonathan Riddell
  • Date: 2010-09-21 15:36:35 UTC
  • mfrom: (1.4.1 upstream) (60.2.11 maverick)
  • Revision ID: james.westby@ubuntu.com-20100921153635-6tejqkiro2u21ydi
Tags: 1:2.2.2-0ubuntu3
Add kubuntu_03_fix-crash-on-closing-sqlite-connection-2.2.2.diff and
kubuntu_04_support-large-memo-values-for-msaccess-2.2.2.diff as
recommended by upstream http://kexi-
project.org/wiki/wikiview/index.php@Kexi2.2_Patches.html#sqlite_stab
ility

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* This file is part of the KDE project
2
 
   Copyright (C) 2005-2006 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
 
#include "KoXmlReader.h"
21
 
 
22
 
/*
23
 
  This is a memory-efficient DOM implementation for KOffice. See the API
24
 
  documentation for details.
25
 
 
26
 
  IMPORTANT !
27
 
 
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
31
 
    hours wasted!
32
 
 
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.
36
 
 
37
 
  * The public API shall remain as compatible as QDom.
38
 
 
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.
43
 
 
44
 
  Some differences compared to QDom:
45
 
 
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,
49
 
    use KoXmlWriter.
50
 
 
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.
57
 
 
58
 
  - Comment node (like QDomComment) is not implemented as comments are
59
 
    simply ignored.
60
 
 
61
 
  - DTD, entity and entity reference are not handled. Thus, the associated
62
 
    nodes (like QDomDocumentType, QDomEntity, QDomEntityReference) are also
63
 
    not implemented.
64
 
 
65
 
  - Attribute mapping node is not implemented. But of course, functions to
66
 
    query attributes of an element are available.
67
 
 
68
 
 
69
 
 */
70
 
 
71
 
 
72
 
 
73
 
#include <QTextCodec>
74
 
#include <QTextDecoder>
75
 
 
76
 
#ifndef KOXML_USE_QDOM
77
 
 
78
 
#include <qxml.h>
79
 
#include <qdom.h>
80
 
 
81
 
#include <QBuffer>
82
 
#include <QByteArray>
83
 
#include <QDataStream>
84
 
#include <QHash>
85
 
#include <QPair>
86
 
#include <QStringList>
87
 
#include <QVector>
88
 
 
89
 
/*
90
 
 Use more compact representation of in-memory nodes.
91
 
 
92
 
 Advantages: faster iteration, can facilitate real-time compression.
93
 
 Disadvantages: still buggy, eat slightly more memory.
94
 
*/
95
 
#define KOXML_COMPACT
96
 
 
97
 
/*
98
 
 Use real-time compression. Only works in conjuction with KOXML_COMPACT
99
 
 above because otherwise the non-compact layout will slow down everything.
100
 
*/
101
 
#define KOXML_COMPRESS
102
 
 
103
 
 
104
 
// prevent mistake, see above
105
 
#ifdef KOXML_COMPRESS
106
 
#ifndef KOXML_COMPACT
107
 
#error Please enable also KOXML_COMPACT
108
 
#endif
109
 
#endif
110
 
 
111
 
// this is used to quickly get namespaced attribute(s)
112
 
typedef QPair<QString, QString> KoXmlStringPair;
113
 
 
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)
117
 
{
118
 
    return qHash(p.first[0].unicode()) ^ 0x1477;
119
 
 
120
 
    // in case of doubt, use this:
121
 
    // return qHash(p.first)^qHash(p.second);
122
 
}
123
 
 
124
 
// ==================================================================
125
 
//
126
 
//         KoXmlPackedItem
127
 
//
128
 
// ==================================================================
129
 
 
130
 
// 24 bytes on most system
131
 
class KoXmlPackedItem
132
 
{
133
 
public:
134
 
bool attr: 1;
135
 
KoXmlNode::NodeType type: 3;
136
 
 
137
 
#ifdef KOXML_COMPACT
138
 
quint32 childStart: 28;
139
 
#else
140
 
unsigned depth: 28;
141
 
#endif
142
 
 
143
 
    unsigned nameIndex;
144
 
    unsigned nsURIIndex;
145
 
    QString value;
146
 
 
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
149
 
#if 0
150
 
    KoXmlPackedItem(): attr(false), type(KoXmlNode::NullNode), childStart(0), depth(0) {}
151
 
#endif
152
 
};
153
 
 
154
 
Q_DECLARE_TYPEINFO(KoXmlPackedItem, Q_MOVABLE_TYPE);
155
 
 
156
 
#ifdef KOXML_COMPRESS
157
 
static QDataStream& operator<<(QDataStream& s, const KoXmlPackedItem& item)
158
 
{
159
 
    quint8 flag = item.attr ? 1 : 0;
160
 
 
161
 
    s << flag;
162
 
    s << (quint8) item.type;
163
 
    s << item.childStart;
164
 
    s << item.nameIndex;
165
 
    s << item.nsURIIndex;
166
 
    s << item.value;
167
 
 
168
 
    return s;
169
 
}
170
 
 
171
 
static QDataStream& operator>>(QDataStream& s, KoXmlPackedItem& item)
172
 
{
173
 
    quint8 flag;
174
 
    quint8 type;
175
 
    quint32 child;
176
 
    QString value;
177
 
 
178
 
    s >> flag;
179
 
    s >> type;
180
 
    s >> child;
181
 
    s >> item.nameIndex;
182
 
    s >> item.nsURIIndex;
183
 
    s >> value;
184
 
 
185
 
    item.attr = (flag != 0);
186
 
    item.type = (KoXmlNode::NodeType) type;
187
 
    item.childStart = child;
188
 
    item.value = value;
189
 
 
190
 
    return s;
191
 
}
192
 
#endif
193
 
 
194
 
#ifdef KOXML_COMPRESS
195
 
 
196
 
// ==================================================================
197
 
//
198
 
//         KoXmlVector
199
 
//
200
 
// ==================================================================
201
 
 
202
 
 
203
 
// similar to QVector, but using LZF compression to save memory space
204
 
// this class is however not reentrant
205
 
 
206
 
// comment it to test this class without the compression
207
 
#define KOXMLVECTOR_USE_LZF
208
 
 
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)
213
 
 
214
 
// LZF stuff is wrapper in KoLZF
215
 
#ifdef KOXML_COMPRESS
216
 
#ifdef KOXMLVECTOR_USE_LZF
217
 
 
218
 
#define HASH_LOG  12
219
 
#define HASH_SIZE (1<< HASH_LOG)
220
 
#define HASH_MASK  (HASH_SIZE-1)
221
 
 
222
 
#define UPDATE_HASH(v,p) { v = *((quint16*)p); v ^= *((quint16*)(p+1))^(v>>(16-HASH_LOG)); }
223
 
 
224
 
#define MAX_COPY       32
225
 
#define MAX_LEN       264  /* 256 + 8 */
226
 
#define MAX_DISTANCE 8192
227
 
 
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)
231
 
{
232
 
    Q_UNUSED(maxout);
233
 
 
234
 
    const quint8* ip = (const quint8*) input;
235
 
    const quint8* ip_limit = ip + length - MAX_COPY - 4;
236
 
    quint8* op = (quint8*) output;
237
 
 
238
 
    const quint8* htab[HASH_SIZE];
239
 
    const quint8** hslot;
240
 
    quint32 hval;
241
 
 
242
 
    quint8* ref;
243
 
    qint32 copy;
244
 
    qint32 len;
245
 
    qint32 distance;
246
 
    quint8* anchor;
247
 
 
248
 
    /* initializes hash table */
249
 
    for (hslot = htab; hslot < htab + HASH_SIZE; hslot++)
250
 
        *hslot = ip;
251
 
 
252
 
    /* we start with literal copy */
253
 
    copy = 0;
254
 
    *op++ = MAX_COPY - 1;
255
 
 
256
 
    /* main loop */
257
 
    while (ip < ip_limit) {
258
 
        /* find potential match */
259
 
        UPDATE_HASH(hval, ip);
260
 
        hslot = htab + (hval & HASH_MASK);
261
 
        ref = (quint8*) * hslot;
262
 
 
263
 
        /* update hash table */
264
 
        *hslot = ip;
265
 
 
266
 
        /* find itself? then it's no match */
267
 
        if (ip == ref)
268
 
            goto literal;
269
 
 
270
 
        /* is this a match? check the first 2 bytes */
271
 
        if (*((quint16*)ref) != *((quint16*)ip))
272
 
            goto literal;
273
 
 
274
 
        /* now check the 3rd byte */
275
 
        if (ref[2] != ip[2])
276
 
            goto literal;
277
 
 
278
 
        /* calculate distance to the match */
279
 
        distance = ip - ref;
280
 
 
281
 
        /* skip if too far away */
282
 
        if (distance >= MAX_DISTANCE)
283
 
            goto literal;
284
 
 
285
 
        /* here we have 3-byte matches */
286
 
        anchor = (quint8*)ip;
287
 
        len = 3;
288
 
        ref += 3;
289
 
        ip += 3;
290
 
 
291
 
        /* now we have to check how long the match is */
292
 
        if (ip < ip_limit - MAX_LEN) {
293
 
            while (len < MAX_LEN - 8) {
294
 
                /* unroll 8 times */
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;
303
 
                len += 8;
304
 
            }
305
 
            ip--;
306
 
        }
307
 
        len = ip - anchor;
308
 
 
309
 
        /* just before the last non-matching byte */
310
 
        ip = anchor + len;
311
 
 
312
 
        /* if we have copied something, adjust the copy count */
313
 
        if (copy) {
314
 
            /* copy is biased, '0' means 1 byte copy */
315
 
            anchor = anchor - copy - 1;
316
 
            *(op - copy - 1) = copy - 1;
317
 
            copy = 0;
318
 
        } else
319
 
            /* back, to overwrite the copy count */
320
 
            op--;
321
 
 
322
 
        /* length is biased, '1' means a match of 3 bytes */
323
 
        len -= 2;
324
 
 
325
 
        /* distance is also biased */
326
 
        distance--;
327
 
 
328
 
        /* encode the match */
329
 
        if (len < 7)
330
 
            *op++ = (len << 5) + (distance >> 8);
331
 
        else {
332
 
            *op++ = (7 << 5) + (distance >> 8);
333
 
            *op++ = len - 7;
334
 
        }
335
 
        *op++ = (distance & 255);
336
 
 
337
 
        /* assuming next will be literal copy */
338
 
        *op++ = MAX_COPY - 1;
339
 
 
340
 
        /* update the hash at match boundary */
341
 
        --ip;
342
 
        UPDATE_HASH(hval, ip);
343
 
        htab[hval & HASH_MASK] = ip;
344
 
        ip++;
345
 
 
346
 
        continue;
347
 
 
348
 
    literal:
349
 
        *op++ = *ip++;
350
 
        copy++;
351
 
        if (copy >= MAX_COPY) {
352
 
            copy = 0;
353
 
            *op++ = MAX_COPY - 1;
354
 
        }
355
 
    }
356
 
 
357
 
    /* left-over as literal copy */
358
 
    ip_limit = (const quint8*)input + length;
359
 
    while (ip < ip_limit) {
360
 
        *op++ = *ip++;
361
 
        copy++;
362
 
        if (copy == MAX_COPY) {
363
 
            copy = 0;
364
 
            *op++ = MAX_COPY - 1;
365
 
        }
366
 
    }
367
 
 
368
 
    /* if we have copied something, adjust the copy length */
369
 
    if (copy)
370
 
        *(op - copy - 1) = copy - 1;
371
 
    else
372
 
        op--;
373
 
 
374
 
    return op - (quint8*)output;
375
 
}
376
 
 
377
 
static int lzff_decompress(const void* input, int length, void* output, int maxout)
378
 
{
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;
383
 
    quint8* ref;
384
 
 
385
 
    while (ip < ip_limit) {
386
 
        quint32 ctrl = (*ip) + 1;
387
 
        quint32 ofs = ((*ip) & 31) << 8;
388
 
        quint32 len = (*ip++) >> 5;
389
 
 
390
 
        if (ctrl < 33) {
391
 
            /* literal copy */
392
 
            if (op + ctrl > op_limit)
393
 
                return 0;
394
 
 
395
 
            /* crazy unrolling */
396
 
            if (ctrl) {
397
 
                *op++ = *ip++;
398
 
                ctrl--;
399
 
 
400
 
                if (ctrl) {
401
 
                    *op++ = *ip++;
402
 
                    ctrl--;
403
 
 
404
 
                    if (ctrl) {
405
 
                        *op++ = *ip++;
406
 
                        ctrl--;
407
 
 
408
 
                        for (;ctrl; ctrl--)
409
 
                            *op++ = *ip++;
410
 
                    }
411
 
                }
412
 
            }
413
 
        } else {
414
 
            /* back reference */
415
 
            len--;
416
 
            ref = op - ofs;
417
 
            ref--;
418
 
 
419
 
            if (len == 7 - 1)
420
 
                len += *ip++;
421
 
 
422
 
            ref -= *ip++;
423
 
 
424
 
            if (op + len + 3 > op_limit)
425
 
                return 0;
426
 
 
427
 
            if (ref < (quint8 *)output)
428
 
                return 0;
429
 
 
430
 
            *op++ = *ref++;
431
 
            *op++ = *ref++;
432
 
            *op++ = *ref++;
433
 
            if (len)
434
 
                for (; len; --len)
435
 
                    *op++ = *ref++;
436
 
        }
437
 
    }
438
 
 
439
 
    return op - (quint8*)output;
440
 
}
441
 
 
442
 
class KoLZF
443
 
{
444
 
public:
445
 
    static QByteArray compress(const QByteArray&);
446
 
    static void decompress(const QByteArray&, QByteArray&);
447
 
};
448
 
 
449
 
QByteArray KoLZF::compress(const QByteArray& input)
450
 
{
451
 
    const void* const in_data = (const void*) input.constData();
452
 
    unsigned int in_len = (unsigned int)input.size();
453
 
 
454
 
    QByteArray output;
455
 
    output.resize(in_len + 4 + 1);
456
 
 
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;
463
 
    output[4] = 1;
464
 
 
465
 
    unsigned int out_len = in_len - 1;
466
 
    unsigned char* out_data = (unsigned char*) output.data() + 5;
467
 
 
468
 
    unsigned int len = lzff_compress(in_data, in_len, out_data, out_len);
469
 
    out_len = len;
470
 
 
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
474
 
        out_len = in_len;
475
 
        output.insert(5, input);
476
 
 
477
 
        // flag must indicate "uncompressed block"
478
 
        output[4] = 0;
479
 
    }
480
 
 
481
 
    // minimize memory
482
 
    output.resize(out_len + 4 + 1);
483
 
    output.squeeze();
484
 
 
485
 
    return output;
486
 
}
487
 
 
488
 
// will not squeeze output
489
 
void KoLZF::decompress(const QByteArray& input, QByteArray& output)
490
 
{
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;
497
 
 
498
 
    // prepare the output
499
 
    output.reserve(unpack_size);
500
 
 
501
 
    // compression flag
502
 
    quint8 flag = (quint8)input[4];
503
 
 
504
 
    // prepare for lzf
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;
509
 
 
510
 
    if (flag == 0)
511
 
        memcpy(output.data(), in_data, in_len);
512
 
    else {
513
 
        unsigned int len = lzff_decompress(in_data, in_len, out_data, out_len);
514
 
        output.resize(len);
515
 
        output.squeeze();
516
 
    }
517
 
}
518
 
 
519
 
 
520
 
#endif
521
 
#endif
522
 
 
523
 
template <typename T>
524
 
class KoXmlVector
525
 
{
526
 
private:
527
 
    unsigned totalItems;
528
 
    QVector<unsigned> startIndex;
529
 
    QVector<QByteArray> blocks;
530
 
 
531
 
    unsigned bufferStartIndex;
532
 
    QVector<T> bufferItems;
533
 
    QByteArray bufferData;
534
 
 
535
 
protected:
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())
542
 
                return;
543
 
 
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]) {
550
 
                    loc = c;
551
 
                    break;
552
 
                }
553
 
 
554
 
        bufferStartIndex = startIndex[loc];
555
 
#ifdef KOXMLVECTOR_USE_LZF
556
 
        KoLZF::decompress(blocks[loc], bufferData);
557
 
#else
558
 
        bufferData = blocks[loc];
559
 
#endif
560
 
        QBuffer buffer(&bufferData);
561
 
        buffer.open(QIODevice::ReadOnly);
562
 
        QDataStream in(&buffer);
563
 
        bufferItems.clear();
564
 
        in >> bufferItems;
565
 
    }
566
 
 
567
 
    // store data in the buffer to main blocks
568
 
    void storeBuffer() {
569
 
        QBuffer buffer;
570
 
        buffer.open(QIODevice::WriteOnly);
571
 
        QDataStream out(&buffer);
572
 
        out << bufferItems;
573
 
 
574
 
        startIndex.append(bufferStartIndex);
575
 
#ifdef KOXMLVECTOR_USE_LZF
576
 
        blocks.append(KoLZF::compress(buffer.data()));
577
 
#else
578
 
        blocks.append(buffer.data());
579
 
#endif
580
 
 
581
 
        bufferStartIndex += bufferItems.count();
582
 
        bufferItems.clear();
583
 
    }
584
 
 
585
 
public:
586
 
    inline KoXmlVector(): totalItems(0), bufferStartIndex(0) {};
587
 
 
588
 
    void clear() {
589
 
        totalItems = 0;
590
 
        startIndex.clear();
591
 
        blocks.clear();
592
 
 
593
 
        bufferStartIndex = 0;
594
 
        bufferItems.clear();
595
 
        bufferData.reserve(1024*1024);
596
 
    }
597
 
 
598
 
    inline int count() const {
599
 
        return (int)totalItems;
600
 
    }
601
 
    inline int size() const {
602
 
        return (int)totalItems;
603
 
    }
604
 
    inline bool isEmpty() const {
605
 
        return totalItems == 0;
606
 
    }
607
 
 
608
 
    // append a new item
609
 
    // WARNING: use the return value as soon as possible
610
 
    // it may be invalid if another function is invoked
611
 
    T& newItem() {
612
 
        // buffer full?
613
 
        if (bufferItems.count() >= ITEMS_FULL - 1)
614
 
            storeBuffer();
615
 
 
616
 
        totalItems++;
617
 
        bufferItems.resize(bufferItems.count() + 1);
618
 
        return bufferItems[bufferItems.count()-1];
619
 
    }
620
 
 
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];
626
 
    }
627
 
 
628
 
    // optimize memory usage
629
 
    // will INVALIDATE all references to the buffer
630
 
    void squeeze() {
631
 
        storeBuffer();
632
 
    }
633
 
 
634
 
};
635
 
 
636
 
#endif
637
 
 
638
 
// ==================================================================
639
 
//
640
 
//         KoXmlPackedDocument
641
 
//
642
 
// ==================================================================
643
 
 
644
 
#ifdef KOXML_COMPRESS
645
 
typedef KoXmlVector<KoXmlPackedItem> KoXmlPackedGroup;
646
 
#else
647
 
typedef QVector<KoXmlPackedItem> KoXmlPackedGroup;
648
 
#endif
649
 
 
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)
654
 
 
655
 
class KoXmlPackedDocument
656
 
{
657
 
public:
658
 
    bool processNamespace;
659
 
#ifdef KOXML_COMPACT
660
 
    // map given depth to the list of items
661
 
    QHash<int, KoXmlPackedGroup> groups;
662
 
#else
663
 
    QVector<KoXmlPackedItem> items;
664
 
#endif
665
 
 
666
 
    QStringList stringList;
667
 
    QString docType;
668
 
 
669
 
private:
670
 
    QHash<QString, unsigned> stringHash;
671
 
 
672
 
    unsigned cacheString(const QString& str) {
673
 
        if (str.isEmpty())
674
 
            return 0;
675
 
 
676
 
        const unsigned& ii = stringHash[str];
677
 
        if (ii > 0)
678
 
            return ii;
679
 
 
680
 
        // not yet declared, so we add it
681
 
        unsigned i = stringList.count();
682
 
        stringList.append(str);
683
 
        stringHash.insert(str, i);
684
 
 
685
 
        return i;
686
 
    }
687
 
 
688
 
    QHash<QString, unsigned> valueHash;
689
 
    QStringList valueList;
690
 
 
691
 
    QString cacheValue(const QString& value) {
692
 
        if (value.isEmpty())
693
 
            return 0;
694
 
 
695
 
        const unsigned& ii = valueHash[value];
696
 
        if (ii > 0)
697
 
            return valueList[ii];
698
 
 
699
 
        // not yet declared, so we add it
700
 
        unsigned i = valueList.count();
701
 
        valueList.append(value);
702
 
        valueHash.insert(value, i);
703
 
 
704
 
        return valueList[i];
705
 
    }
706
 
 
707
 
#ifdef KOXML_COMPACT
708
 
public:
709
 
    const KoXmlPackedItem& itemAt(unsigned depth, unsigned index) {
710
 
        KoXmlPackedGroup& group = groups[depth];
711
 
        return group[index];
712
 
    }
713
 
 
714
 
    unsigned itemCount(unsigned depth) {
715
 
        const KoXmlPackedGroup& group = groups[depth];
716
 
        return group.count();
717
 
    }
718
 
 
719
 
    /*
720
 
       NOTE:
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.
725
 
 
726
 
          The state itself is defined by the member variables
727
 
          currentDepth and the groups (see above).
728
 
     */
729
 
 
730
 
    unsigned currentDepth;
731
 
 
732
 
    KoXmlPackedItem& newItem(unsigned depth) {
733
 
        KoXmlPackedGroup& group = groups[depth];
734
 
 
735
 
#ifdef KOXML_COMPRESS
736
 
        KoXmlPackedItem& item = group.newItem();
737
 
#else
738
 
        // reserve up front
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);
742
 
 
743
 
        KoXmlPackedItem& item = group[group.count()-1];
744
 
#endif
745
 
 
746
 
        // this is necessary, because intentionally we don't want to have
747
 
        // a constructor for KoXmlPackedItem
748
 
        item.attr = false;
749
 
        item.type = KoXmlNode::NullNode;
750
 
        item.nameIndex = 0;
751
 
        item.nsURIIndex = 0;
752
 
        item.childStart = itemCount(depth + 1);
753
 
        item.value.clear();
754
 
 
755
 
        return item;
756
 
    }
757
 
 
758
 
    void clear() {
759
 
        currentDepth = 0;
760
 
        stringHash.clear();
761
 
        stringList.clear();
762
 
        valueHash.clear();
763
 
        valueList.clear();
764
 
        groups.clear();
765
 
        docType.clear();
766
 
 
767
 
        // reserve index #0
768
 
        cacheString(QString());
769
 
 
770
 
        // first node is root
771
 
        KoXmlPackedItem& rootItem = newItem(0);
772
 
        rootItem.type = KoXmlNode::DocumentNode;
773
 
    }
774
 
 
775
 
    void finish() {
776
 
        // won't be needed anymore
777
 
        stringHash.clear();
778
 
        valueHash.clear();
779
 
        valueList.clear();
780
 
 
781
 
        // optimize, see documentation on QVector::squeeze
782
 
        for (int d = 0; d < groups.count(); d++) {
783
 
            KoXmlPackedGroup& group = groups[d];
784
 
            group.squeeze();
785
 
        }
786
 
    }
787
 
 
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);
794
 
 
795
 
        currentDepth++;
796
 
    }
797
 
 
798
 
    void closeElement() {
799
 
        currentDepth--;
800
 
    }
801
 
 
802
 
    void addDTD(const QString& dt) {
803
 
        docType = dt;
804
 
    }
805
 
 
806
 
    void addAttribute(const QString& name, const QString& nsURI, const QString& value) {
807
 
        KoXmlPackedItem& item = newItem(currentDepth + 1);
808
 
        item.attr = true;
809
 
        item.nameIndex = cacheString(name);
810
 
        item.nsURIIndex = cacheString(nsURI);
811
 
        //item.value = cacheValue( value );
812
 
        item.value = value;
813
 
    }
814
 
 
815
 
    void addText(const QString& text) {
816
 
        KoXmlPackedItem& item = newItem(currentDepth + 1);
817
 
        item.type = KoXmlNode::TextNode;
818
 
        item.value = text;
819
 
    }
820
 
 
821
 
    void addCData(const QString& text) {
822
 
        KoXmlPackedItem& item = newItem(currentDepth + 1);
823
 
        item.type = KoXmlNode::CDATASectionNode;
824
 
        item.value = text;
825
 
    }
826
 
 
827
 
    void addProcessingInstruction() {
828
 
        KoXmlPackedItem& item = newItem(currentDepth + 1);
829
 
        item.type = KoXmlNode::ProcessingInstructionNode;
830
 
    }
831
 
 
832
 
public:
833
 
    KoXmlPackedDocument(): processNamespace(false), currentDepth(0) {
834
 
        clear();
835
 
    }
836
 
 
837
 
#else
838
 
 
839
 
private:
840
 
    unsigned elementDepth;
841
 
 
842
 
public:
843
 
 
844
 
    KoXmlPackedItem& newItem() {
845
 
        unsigned count = items.count() + 512;
846
 
        count = 1024 * (count >> 10);
847
 
        items.reserve(count);
848
 
 
849
 
        items.resize(items.count() + 1);
850
 
 
851
 
        // this is necessary, because intentionally we don't want to have
852
 
        // a constructor for KoXmlPackedItem
853
 
        KoXmlPackedItem& item = items[items.count()-1];
854
 
        item.attr = false;
855
 
        item.type = KoXmlNode::NullNode;
856
 
        item.nameIndex = 0;
857
 
        item.nsURIIndex = 0;
858
 
        item.depth = 0;
859
 
 
860
 
        return item;
861
 
    }
862
 
 
863
 
    void addElement(const QString& name, const QString& nsURI) {
864
 
        // we are going one level deeper
865
 
        elementDepth++;
866
 
 
867
 
        KoXmlPackedItem& item = newItem();
868
 
 
869
 
        item.attr = false;
870
 
        item.type = KoXmlNode::ElementNode;
871
 
        item.depth = elementDepth;
872
 
        item.nameIndex = cacheString(name);
873
 
        item.nsURIIndex = cacheString(nsURI);
874
 
    }
875
 
 
876
 
    void closeElement() {
877
 
        // we are going up one level
878
 
        elementDepth--;
879
 
    }
880
 
 
881
 
    void addAttribute(const QString& name, const QString& nsURI, const QString& value) {
882
 
        KoXmlPackedItem& item = newItem();
883
 
 
884
 
        item.attr = true;
885
 
        item.type = KoXmlNode::NullNode;
886
 
        item.depth = elementDepth;
887
 
        item.nameIndex = cacheString(name);
888
 
        item.nsURIIndex = cacheString(nsURI);
889
 
        //item.value = cacheValue( value );
890
 
        item.value = value;
891
 
    }
892
 
 
893
 
    void addText(const QString& str) {
894
 
        KoXmlPackedItem& item = newItem();
895
 
 
896
 
        item.attr = false;
897
 
        item.type = KoXmlNode::TextNode;
898
 
        item.depth = elementDepth + 1;
899
 
        item.nameIndex = 0;
900
 
        item.nsURIIndex = 0;
901
 
        item.value = str;
902
 
    }
903
 
 
904
 
    void addCData(const QString& str) {
905
 
        KoXmlPackedItem& item = newItem();
906
 
 
907
 
        item.attr = false;
908
 
        item.type = KoXmlNode::CDATASectionNode;
909
 
        item.depth = elementDepth + 1;
910
 
        item.nameIndex = 0;
911
 
        item.nsURIIndex = 0;
912
 
        item.value = str;
913
 
    }
914
 
 
915
 
    void addProcessingInstruction() {
916
 
        KoXmlPackedItem& item = newItem();
917
 
 
918
 
        item.attr = false;
919
 
        item.type = KoXmlNode::ProcessingInstructionNode;
920
 
        item.depth = elementDepth + 1;
921
 
        item.nameIndex = 0;
922
 
        item.nsURIIndex = 0;
923
 
        item.value.clear();
924
 
    }
925
 
 
926
 
    void clear() {
927
 
        stringList.clear();
928
 
        stringHash.clear();
929
 
        valueHash.clear();
930
 
        valueList.clear();
931
 
        items.clear();
932
 
        elementDepth = 0;
933
 
 
934
 
        // reserve index #0
935
 
        cacheString(".");
936
 
 
937
 
        KoXmlPackedItem& rootItem = newItem();
938
 
        rootItem.attr = false;
939
 
        rootItem.type = KoXmlNode::DocumentNode;
940
 
        rootItem.depth = 0;
941
 
        rootItem.nsURIIndex = 0;
942
 
        rootItem.nameIndex = 0;
943
 
    }
944
 
 
945
 
    void finish() {
946
 
        stringHash.clear();
947
 
        valueList.clear();
948
 
        valueHash.clear();
949
 
        items.squeeze();
950
 
    }
951
 
 
952
 
    KoXmlPackedDocument(): processNamespace(false), elementDepth(0) {
953
 
    }
954
 
 
955
 
#endif
956
 
 
957
 
};
958
 
 
959
 
// ==================================================================
960
 
//
961
 
//         KoXmlHandler
962
 
//
963
 
// ==================================================================
964
 
 
965
 
class KoXmlHandler : public QXmlDefaultHandler
966
 
{
967
 
public:
968
 
    KoXmlHandler(KoXmlPackedDocument* doc);
969
 
 
970
 
    // content handler
971
 
    bool startDocument();
972
 
    bool endDocument();
973
 
 
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);
978
 
 
979
 
    bool characters(const QString& ch);
980
 
    bool processingInstruction(const QString& target, const QString& data);
981
 
    bool skippedEntity(const QString& name);
982
 
 
983
 
    // lexical handler
984
 
    bool startCDATA();
985
 
    bool endCDATA();
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);
991
 
 
992
 
    // decl handler
993
 
    bool externalEntityDecl(const QString &name, const QString &publicId,
994
 
                            const QString &systemId) ;
995
 
 
996
 
    // DTD handler
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 &notationName) ;
1001
 
 
1002
 
    // error handler
1003
 
    bool fatalError(const QXmlParseException& exception);
1004
 
 
1005
 
    QString errorMsg;
1006
 
    int errorLine;
1007
 
    int errorColumn;
1008
 
 
1009
 
private:
1010
 
    KoXmlPackedDocument* document;
1011
 
    bool processNamespace;
1012
 
    QString entityName;
1013
 
    bool cdata;
1014
 
};
1015
 
 
1016
 
 
1017
 
KoXmlHandler::KoXmlHandler(KoXmlPackedDocument* doc) : QXmlDefaultHandler(),
1018
 
        errorLine(0), errorColumn(0), document(doc),
1019
 
        processNamespace(doc->processNamespace), cdata(false)
1020
 
{
1021
 
}
1022
 
 
1023
 
bool KoXmlHandler::startDocument()
1024
 
{
1025
 
    // just for sanity
1026
 
    cdata = false;
1027
 
    entityName.clear();
1028
 
 
1029
 
    errorMsg.clear();
1030
 
    errorLine = 0;
1031
 
    errorColumn = 0;
1032
 
 
1033
 
    document->clear();
1034
 
    return true;
1035
 
}
1036
 
 
1037
 
bool KoXmlHandler::endDocument()
1038
 
{
1039
 
    document->finish();
1040
 
    return true;
1041
 
}
1042
 
 
1043
 
bool KoXmlHandler::startDTD(const QString& name, const QString& publicId,
1044
 
                            const QString& systemId)
1045
 
{
1046
 
    Q_UNUSED(publicId);
1047
 
    Q_UNUSED(systemId);
1048
 
 
1049
 
    document->addDTD(name);
1050
 
 
1051
 
    return true;
1052
 
}
1053
 
 
1054
 
bool KoXmlHandler::startElement(const QString& nsURI, const QString& localName,
1055
 
                                const QString& name, const QXmlAttributes& atts)
1056
 
{
1057
 
    Q_UNUSED(localName);
1058
 
 
1059
 
    document->addElement(name, nsURI);
1060
 
 
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));
1064
 
 
1065
 
    return true;
1066
 
}
1067
 
 
1068
 
bool KoXmlHandler::endElement(const QString& nsURI, const QString& localName,
1069
 
                              const QString& qName)
1070
 
{
1071
 
    Q_UNUSED(nsURI);
1072
 
    Q_UNUSED(localName);
1073
 
    Q_UNUSED(qName);
1074
 
 
1075
 
    document->closeElement();
1076
 
 
1077
 
    return true;
1078
 
}
1079
 
 
1080
 
bool KoXmlHandler::characters(const QString& str)
1081
 
{
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
1086
 
        return true;
1087
 
    }
1088
 
 
1089
 
    // add a new text or CDATA
1090
 
    if (cdata)
1091
 
        document->addCData(str);
1092
 
    else
1093
 
        document->addText(str);
1094
 
 
1095
 
    return true;
1096
 
}
1097
 
 
1098
 
bool KoXmlHandler::processingInstruction(const QString& target,
1099
 
        const QString& data)
1100
 
{
1101
 
    Q_UNUSED(target);
1102
 
    Q_UNUSED(data);
1103
 
 
1104
 
    document->addProcessingInstruction();
1105
 
 
1106
 
    return true;
1107
 
}
1108
 
 
1109
 
bool KoXmlHandler::skippedEntity(const QString& name)
1110
 
{
1111
 
    Q_UNUSED(name);
1112
 
 
1113
 
    // we skip entity
1114
 
    return true;
1115
 
}
1116
 
 
1117
 
bool KoXmlHandler::startCDATA()
1118
 
{
1119
 
    cdata = true;
1120
 
    return true;
1121
 
}
1122
 
 
1123
 
bool KoXmlHandler::endCDATA()
1124
 
{
1125
 
    cdata = false;
1126
 
    return true;
1127
 
}
1128
 
 
1129
 
bool KoXmlHandler::startEntity(const QString& name)
1130
 
{
1131
 
    entityName = name;
1132
 
    return true;
1133
 
}
1134
 
 
1135
 
bool KoXmlHandler::endEntity(const QString& name)
1136
 
{
1137
 
    Q_UNUSED(name);
1138
 
    entityName.clear();
1139
 
    return true;
1140
 
}
1141
 
 
1142
 
bool KoXmlHandler::comment(const QString& comment)
1143
 
{
1144
 
    Q_UNUSED(comment);
1145
 
 
1146
 
    // we skip comment
1147
 
    return true;
1148
 
}
1149
 
 
1150
 
bool KoXmlHandler::unparsedEntityDecl(const QString &name,
1151
 
                                      const QString &publicId, const QString &systemId, const QString &notationName)
1152
 
{
1153
 
    Q_UNUSED(name);
1154
 
    Q_UNUSED(publicId);
1155
 
    Q_UNUSED(systemId);
1156
 
    Q_UNUSED(notationName);
1157
 
 
1158
 
    // we skip entity
1159
 
    return true;
1160
 
}
1161
 
 
1162
 
bool KoXmlHandler::externalEntityDecl(const QString &name,
1163
 
                                      const QString &publicId, const QString &systemId)
1164
 
{
1165
 
    Q_UNUSED(name);
1166
 
    Q_UNUSED(publicId);
1167
 
    Q_UNUSED(systemId);
1168
 
 
1169
 
    // we skip entity
1170
 
    return true;
1171
 
}
1172
 
 
1173
 
bool KoXmlHandler::notationDecl(const QString & name,
1174
 
                                const QString & publicId, const QString & systemId)
1175
 
{
1176
 
    Q_UNUSED(name);
1177
 
    Q_UNUSED(publicId);
1178
 
    Q_UNUSED(systemId);
1179
 
 
1180
 
    // we skip notation node
1181
 
    return true;
1182
 
}
1183
 
 
1184
 
bool KoXmlHandler::fatalError(const QXmlParseException& exception)
1185
 
{
1186
 
    errorMsg = exception.message();
1187
 
    errorLine =  exception.lineNumber();
1188
 
    errorColumn =  exception.columnNumber();
1189
 
    return QXmlDefaultHandler::fatalError(exception);
1190
 
}
1191
 
 
1192
 
 
1193
 
// ==================================================================
1194
 
//
1195
 
//         KoXmlNodeData
1196
 
//
1197
 
// ==================================================================
1198
 
 
1199
 
class KoXmlNodeData
1200
 
{
1201
 
public:
1202
 
 
1203
 
    KoXmlNodeData();
1204
 
    ~KoXmlNodeData();
1205
 
 
1206
 
    // generic properties
1207
 
    KoXmlNode::NodeType nodeType;
1208
 
    QString tagName;
1209
 
    QString namespaceURI;
1210
 
    QString prefix;
1211
 
    QString localName;
1212
 
 
1213
 
#ifdef KOXML_COMPACT
1214
 
    unsigned nodeDepth;
1215
 
#endif
1216
 
 
1217
 
    // reference counting
1218
 
    unsigned long count;
1219
 
    void ref() {
1220
 
        count++;
1221
 
    }
1222
 
    void unref() {
1223
 
        if (this == &null) return; if (!--count) delete this;
1224
 
    }
1225
 
 
1226
 
    // type information
1227
 
    bool emptyDocument;
1228
 
    QString nodeName() const;
1229
 
 
1230
 
    // for tree and linked-list
1231
 
    KoXmlNodeData* parent;
1232
 
    KoXmlNodeData* prev;
1233
 
    KoXmlNodeData* next;
1234
 
    KoXmlNodeData* first;
1235
 
    KoXmlNodeData* last;
1236
 
 
1237
 
    QString text();
1238
 
 
1239
 
    // node manipulation
1240
 
    void appendChild(KoXmlNodeData* child);
1241
 
    void clear();
1242
 
 
1243
 
    // attributes
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;
1252
 
 
1253
 
    // for text and CDATA
1254
 
    QString data() const;
1255
 
    void setData(const QString& data);
1256
 
 
1257
 
    // reference from within the packed doc
1258
 
    KoXmlPackedDocument* packedDoc;
1259
 
    unsigned long nodeIndex;
1260
 
 
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);
1266
 
 
1267
 
    // used when doing on-demand (re)parse
1268
 
    bool loaded;
1269
 
    void loadChildren(int depth = 1);
1270
 
    void unloadChildren();
1271
 
 
1272
 
    void dump();
1273
 
 
1274
 
    static KoXmlNodeData null;
1275
 
 
1276
 
    // compatibility
1277
 
    QDomNode asQDomNode(QDomDocument ownerDoc) const;
1278
 
 
1279
 
private:
1280
 
    QHash<QString, QString> attr;
1281
 
    QHash<KoXmlStringPair, QString> attrNS;
1282
 
    QString textData;
1283
 
    friend class KoXmlHandler;
1284
 
    friend class KoXmlElement;
1285
 
};
1286
 
 
1287
 
KoXmlNodeData KoXmlNodeData::null;
1288
 
 
1289
 
KoXmlNodeData::KoXmlNodeData() : nodeType(KoXmlNode::NullNode),
1290
 
#ifdef KOXML_COMPACT
1291
 
        nodeDepth(0),
1292
 
#endif
1293
 
        count(1), emptyDocument(true), parent(0), prev(0), next(0), first(0), last(0),
1294
 
        packedDoc(0), nodeIndex(0), loaded(false)
1295
 
{
1296
 
}
1297
 
 
1298
 
KoXmlNodeData::~KoXmlNodeData()
1299
 
{
1300
 
    clear();
1301
 
}
1302
 
 
1303
 
void KoXmlNodeData::clear()
1304
 
{
1305
 
    if (first)
1306
 
        for (KoXmlNodeData* node = first; node ;) {
1307
 
            KoXmlNodeData* next = node->next;
1308
 
            node->unref();
1309
 
            node = next;
1310
 
        }
1311
 
 
1312
 
    // only document can delete these
1313
 
    // normal nodes don't "own" them
1314
 
    if (nodeType == KoXmlNode::DocumentNode)
1315
 
        delete packedDoc;
1316
 
 
1317
 
    nodeType = KoXmlNode::NullNode;
1318
 
    tagName.clear();
1319
 
    prefix.clear();
1320
 
    namespaceURI.clear();
1321
 
    textData.clear();
1322
 
    packedDoc = 0;
1323
 
 
1324
 
    attr.clear();
1325
 
    attrNS.clear();
1326
 
 
1327
 
    parent = 0;
1328
 
    prev = next = 0;
1329
 
    first = last = 0;
1330
 
 
1331
 
    loaded = false;
1332
 
}
1333
 
 
1334
 
QString KoXmlNodeData::text()
1335
 
{
1336
 
    QString t;
1337
 
 
1338
 
    loadChildren();
1339
 
 
1340
 
    KoXmlNodeData* node = first;
1341
 
    while (node) {
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;
1349
 
        default: break;
1350
 
        }
1351
 
        node = node->next;
1352
 
    }
1353
 
 
1354
 
    return t;
1355
 
}
1356
 
 
1357
 
QString KoXmlNodeData::nodeName() const
1358
 
{
1359
 
    switch (nodeType) {
1360
 
    case KoXmlNode::ElementNode: {
1361
 
        QString n(tagName);
1362
 
        if (!prefix.isEmpty())
1363
 
            n.prepend(':').prepend(prefix);
1364
 
        return n;
1365
 
    }
1366
 
    break;
1367
 
 
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;
1372
 
 
1373
 
    default: return QString(); break;
1374
 
    }
1375
 
 
1376
 
    // should not happen
1377
 
    return QString();
1378
 
}
1379
 
 
1380
 
void KoXmlNodeData::appendChild(KoXmlNodeData* node)
1381
 
{
1382
 
    node->parent = this;
1383
 
    if (!last)
1384
 
        first = last = node;
1385
 
    else {
1386
 
        last->next = node;
1387
 
        node->prev = last;
1388
 
        node->next = 0;
1389
 
        last = node;
1390
 
    }
1391
 
}
1392
 
 
1393
 
void KoXmlNodeData::setAttribute(const QString& name, const QString& value)
1394
 
{
1395
 
    attr[ name ] = value;
1396
 
}
1397
 
 
1398
 
QString KoXmlNodeData::attribute(const QString& name, const QString& def) const
1399
 
{
1400
 
    if (attr.contains(name))
1401
 
        return attr[ name ];
1402
 
    else
1403
 
        return def;
1404
 
}
1405
 
 
1406
 
bool KoXmlNodeData::hasAttribute(const QString& name) const
1407
 
{
1408
 
    return attr.contains(name);
1409
 
}
1410
 
 
1411
 
void KoXmlNodeData::setAttributeNS(const QString& nsURI,
1412
 
                                   const QString& name, const QString& value)
1413
 
{
1414
 
    QString prefix;
1415
 
    QString localName = name;
1416
 
    int i = name.indexOf(':');
1417
 
    if (i != -1) {
1418
 
        localName = name.mid(i + 1);
1419
 
        prefix = name.left(i);
1420
 
    }
1421
 
 
1422
 
    if (prefix.isNull()) return;
1423
 
 
1424
 
    KoXmlStringPair key(nsURI, localName);
1425
 
    attrNS[ key ] = value;
1426
 
}
1427
 
 
1428
 
QString KoXmlNodeData::attributeNS(const QString& nsURI, const QString& name,
1429
 
                                   const QString& def) const
1430
 
{
1431
 
    KoXmlStringPair key(nsURI, name);
1432
 
    if (attrNS.contains(key))
1433
 
        return attrNS[ key ];
1434
 
    else
1435
 
        return def;
1436
 
}
1437
 
 
1438
 
bool KoXmlNodeData::hasAttributeNS(const QString& nsURI, const QString& name) const
1439
 
{
1440
 
    KoXmlStringPair key(nsURI, name);
1441
 
    return attrNS.contains(key);
1442
 
}
1443
 
 
1444
 
void KoXmlNodeData::clearAttributes()
1445
 
{
1446
 
    attr.clear();
1447
 
    attrNS.clear();
1448
 
}
1449
 
 
1450
 
// FIXME how about namespaced attributes ?
1451
 
QStringList KoXmlNodeData::attributeNames() const
1452
 
{
1453
 
    QStringList result;
1454
 
    result = attr.keys();
1455
 
 
1456
 
    return result;
1457
 
}
1458
 
 
1459
 
QString KoXmlNodeData::data() const
1460
 
{
1461
 
    return textData;
1462
 
}
1463
 
 
1464
 
void KoXmlNodeData::setData(const QString& d)
1465
 
{
1466
 
    textData = d;
1467
 
}
1468
 
 
1469
 
bool KoXmlNodeData::setContent(QXmlInputSource* source, bool namespaceProcessing,
1470
 
                               QString* errorMsg, int* errorLine, int* errorColumn)
1471
 
{
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);
1477
 
}
1478
 
 
1479
 
bool KoXmlNodeData::setContent(QXmlInputSource* source,
1480
 
                               QXmlReader* reader, QString* errorMsg, int* errorLine, int* errorColumn)
1481
 
{
1482
 
    if (nodeType != KoXmlNode::DocumentNode)
1483
 
        return false;
1484
 
 
1485
 
    clear();
1486
 
    nodeType = KoXmlNode::DocumentNode;
1487
 
 
1488
 
    // sanity checks
1489
 
    if (!source) return false;
1490
 
    if (!reader) return false;
1491
 
 
1492
 
    delete packedDoc;
1493
 
    packedDoc = new KoXmlPackedDocument;
1494
 
    packedDoc->processNamespace = false;
1495
 
 
1496
 
    packedDoc->processNamespace =
1497
 
        reader->feature("http://xml.org/sax/features/namespaces") &&
1498
 
        !reader->feature("http://xml.org/sax/features/namespace-prefixes");
1499
 
 
1500
 
    KoXmlHandler handler(packedDoc);
1501
 
    reader->setContentHandler(&handler);
1502
 
    reader->setErrorHandler(&handler);
1503
 
    reader->setLexicalHandler(&handler);
1504
 
    reader->setDeclHandler(&handler);
1505
 
    reader->setDTDHandler(&handler);
1506
 
 
1507
 
    bool result = reader->parse(source);
1508
 
    if (!result) {
1509
 
        // parsing error has occurred
1510
 
        if (errorMsg) *errorMsg = handler.errorMsg;
1511
 
        if (errorLine) *errorLine = handler.errorLine;
1512
 
        if (errorColumn)  *errorColumn = handler.errorColumn;
1513
 
        return false;
1514
 
    }
1515
 
 
1516
 
 
1517
 
    // initially load
1518
 
    loadChildren();
1519
 
 
1520
 
    return true;
1521
 
}
1522
 
 
1523
 
#ifdef KOXML_COMPACT
1524
 
 
1525
 
void KoXmlNodeData::loadChildren(int depth)
1526
 
{
1527
 
    // sanity check
1528
 
    if (!packedDoc) return;
1529
 
 
1530
 
    // already loaded ?
1531
 
    if (loaded && (depth <= 1)) return;
1532
 
 
1533
 
    // in case depth is different
1534
 
    unloadChildren();
1535
 
 
1536
 
 
1537
 
    KoXmlNodeData* lastDat = 0;
1538
 
 
1539
 
    unsigned childStop = 0;
1540
 
    if (nodeIndex == packedDoc->itemCount(nodeDepth) - 1)
1541
 
        childStop = packedDoc->itemCount(nodeDepth + 1);
1542
 
    else {
1543
 
        const KoXmlPackedItem& next = packedDoc->itemAt(nodeDepth, nodeIndex + 1);
1544
 
        childStop = next.childStart;
1545
 
    }
1546
 
 
1547
 
    const KoXmlPackedItem& self = packedDoc->itemAt(nodeDepth, nodeIndex);
1548
 
 
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);
1553
 
 
1554
 
        // attribute belongs to this node
1555
 
        if (item.attr) {
1556
 
            QString name = packedDoc->stringList[item.nameIndex];
1557
 
            QString nsURI = packedDoc->stringList[item.nsURIIndex];
1558
 
            QString value = item.value;
1559
 
 
1560
 
            QString prefix;
1561
 
 
1562
 
            QString qName; // with prefix
1563
 
            QString localName;  // without prefix, i.e. local name
1564
 
 
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);
1569
 
 
1570
 
            if (packedDoc->processNamespace) {
1571
 
                setAttributeNS(nsURI, qName, value);
1572
 
                setAttribute(localName, value);
1573
 
            } else
1574
 
                setAttribute(qName, value);
1575
 
        } else {
1576
 
            QString name = packedDoc->stringList[item.nameIndex];
1577
 
            QString nsURI = packedDoc->stringList[item.nsURIIndex];
1578
 
            QString value = item.value;
1579
 
 
1580
 
            QString nodeName = name;
1581
 
            QString localName;
1582
 
            QString prefix;
1583
 
 
1584
 
            if (packedDoc->processNamespace) {
1585
 
                localName = name;
1586
 
                int di = name.indexOf(':');
1587
 
                if (di != -1) {
1588
 
                    localName = name.mid(di + 1);
1589
 
                    prefix = name.left(di);
1590
 
                }
1591
 
                nodeName = localName;
1592
 
            }
1593
 
 
1594
 
            // make a node out of this item
1595
 
            KoXmlNodeData* dat = new KoXmlNodeData;
1596
 
            dat->nodeIndex = i;
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;
1604
 
            dat->count = 1;
1605
 
            dat->parent = this;
1606
 
            dat->prev = lastDat;
1607
 
            dat->next = 0;
1608
 
            dat->first = 0;
1609
 
            dat->last = 0;
1610
 
            dat->loaded = false;
1611
 
            dat->textData = (textItem) ? value : QString();
1612
 
 
1613
 
            // adjust our linked-list
1614
 
            first = (first) ? first : dat;
1615
 
            last = dat;
1616
 
            if (lastDat)
1617
 
                lastDat->next = dat;
1618
 
            lastDat = dat;
1619
 
 
1620
 
            // recursive
1621
 
            if (depth > 1)
1622
 
                dat->loadChildren(depth - 1);
1623
 
        }
1624
 
    }
1625
 
 
1626
 
    loaded = true;
1627
 
}
1628
 
 
1629
 
#else
1630
 
 
1631
 
void KoXmlNodeData::loadChildren(int depth)
1632
 
{
1633
 
    // sanity check
1634
 
    if (!packedDoc) return;
1635
 
 
1636
 
    // already loaded ?
1637
 
    if (loaded && (depth <= 1)) return;
1638
 
 
1639
 
    // cause we don't know how deep this node's children already loaded are
1640
 
    unloadChildren();
1641
 
 
1642
 
    KoXmlNodeData* lastDat = 0;
1643
 
    int nodeDepth = packedDoc->items[nodeIndex].depth;
1644
 
 
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);
1649
 
 
1650
 
        // element already outside our depth
1651
 
        if (!item.attr && (item.type == KoXmlNode::ElementNode))
1652
 
            if (item.depth <= (unsigned)nodeDepth)
1653
 
                break;
1654
 
 
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;
1660
 
 
1661
 
            QString prefix;
1662
 
 
1663
 
            QString qName; // with prefix
1664
 
            QString localName;  // without prefix, i.e. local name
1665
 
 
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);
1670
 
 
1671
 
            if (packedDoc->processNamespace) {
1672
 
                setAttributeNS(nsURI, qName, value);
1673
 
                setAttribute(localName, value);
1674
 
            } else
1675
 
                setAttribute(name, value);
1676
 
        }
1677
 
 
1678
 
        // the child node
1679
 
        if (!item.attr) {
1680
 
            bool instruction = (item.type == KoXmlNode::ProcessingInstructionNode);
1681
 
            bool ok = (textItem || instruction)  ? (item.depth == (unsigned)nodeDepth) : (item.depth == (unsigned)nodeDepth + 1);
1682
 
 
1683
 
            ok = (item.depth == (unsigned)nodeDepth + 1);
1684
 
 
1685
 
            if (ok) {
1686
 
                QString name = packedDoc->stringList[item.nameIndex];
1687
 
                QString nsURI = packedDoc->stringList[item.nsURIIndex];
1688
 
                QString value = item.value;
1689
 
 
1690
 
                QString nodeName = name;
1691
 
                QString localName;
1692
 
                QString prefix;
1693
 
 
1694
 
                if (packedDoc->processNamespace) {
1695
 
                    localName = name;
1696
 
                    int di = name.indexOf(':');
1697
 
                    if (di != -1) {
1698
 
                        localName = name.mid(di + 1);
1699
 
                        prefix = name.left(di);
1700
 
                    }
1701
 
                    nodeName = localName;
1702
 
                }
1703
 
 
1704
 
                // make a node out of this item
1705
 
                KoXmlNodeData* dat = new KoXmlNodeData;
1706
 
                dat->nodeIndex = i;
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;
1713
 
                dat->count = 1;
1714
 
                dat->parent = this;
1715
 
                dat->prev = lastDat;
1716
 
                dat->next = 0;
1717
 
                dat->first = 0;
1718
 
                dat->last = 0;
1719
 
                dat->loaded = false;
1720
 
                dat->textData = (textItem) ? value : QString();
1721
 
 
1722
 
                // adjust our linked-list
1723
 
                first = (first) ? first : dat;
1724
 
                last = dat;
1725
 
                if (lastDat)
1726
 
                    lastDat->next = dat;
1727
 
                lastDat = dat;
1728
 
 
1729
 
                // recursive
1730
 
                if (depth > 1)
1731
 
                    dat->loadChildren(depth - 1);
1732
 
            }
1733
 
        }
1734
 
    }
1735
 
 
1736
 
    loaded = true;
1737
 
}
1738
 
#endif
1739
 
 
1740
 
void KoXmlNodeData::unloadChildren()
1741
 
{
1742
 
    // sanity check
1743
 
    if (!packedDoc) return;
1744
 
 
1745
 
    if (!loaded) return;
1746
 
 
1747
 
    if (first)
1748
 
        for (KoXmlNodeData* node = first; node ;) {
1749
 
            KoXmlNodeData* next = node->next;
1750
 
            node->unloadChildren();
1751
 
            node->unref();
1752
 
            node = next;
1753
 
        }
1754
 
 
1755
 
    clearAttributes();
1756
 
    loaded = false;
1757
 
    first = last = 0;
1758
 
}
1759
 
 
1760
 
#ifdef KOXML_COMPACT
1761
 
 
1762
 
 
1763
 
static QDomNode itemAsQDomNode(QDomDocument ownerDoc, KoXmlPackedDocument* packedDoc,
1764
 
                               unsigned nodeDepth, unsigned nodeIndex)
1765
 
{
1766
 
    // sanity check
1767
 
    if (!packedDoc)
1768
 
        return QDomNode();
1769
 
 
1770
 
    const KoXmlPackedItem& self = packedDoc->itemAt(nodeDepth, nodeIndex);
1771
 
 
1772
 
    unsigned childStop = 0;
1773
 
    if (nodeIndex == packedDoc->itemCount(nodeDepth) - 1)
1774
 
        childStop = packedDoc->itemCount(nodeDepth + 1);
1775
 
    else {
1776
 
        const KoXmlPackedItem& next = packedDoc->itemAt(nodeDepth, nodeIndex + 1);
1777
 
        childStop = next.childStart;
1778
 
    }
1779
 
 
1780
 
    // nothing to do here
1781
 
    if (self.type == KoXmlNode::NullNode)
1782
 
        return QDomNode();
1783
 
 
1784
 
    // create the element properly
1785
 
    if (self.type == KoXmlNode::ElementNode) {
1786
 
        QDomElement element;
1787
 
 
1788
 
        QString name = packedDoc->stringList[self.nameIndex];
1789
 
        QString nsURI = packedDoc->stringList[self.nsURIIndex];
1790
 
 
1791
 
        if (packedDoc->processNamespace)
1792
 
            element = ownerDoc.createElementNS(nsURI, name);
1793
 
        else
1794
 
            element = ownerDoc.createElement(name);
1795
 
 
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);
1801
 
 
1802
 
            // attribute belongs to this node
1803
 
            if (item.attr) {
1804
 
                QString name = packedDoc->stringList[item.nameIndex];
1805
 
                QString nsURI = packedDoc->stringList[item.nsURIIndex];
1806
 
                QString value = item.value;
1807
 
 
1808
 
                QString prefix;
1809
 
 
1810
 
                QString qName; // with prefix
1811
 
                QString localName;  // without prefix, i.e. local name
1812
 
 
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);
1817
 
 
1818
 
                if (packedDoc->processNamespace) {
1819
 
                    element.setAttributeNS(nsURI, qName, value);
1820
 
                    element.setAttribute(localName, value);
1821
 
                } else
1822
 
                    element.setAttribute(name, value);
1823
 
            } else {
1824
 
                // add it recursively
1825
 
                QDomNode childNode = itemAsQDomNode(ownerDoc, packedDoc, nodeDepth + 1, i);
1826
 
                element.appendChild(childNode);
1827
 
            }
1828
 
        }
1829
 
 
1830
 
        return element;
1831
 
    }
1832
 
 
1833
 
    // create the text node
1834
 
    if (self.type == KoXmlNode::TextNode) {
1835
 
        QString text = self.value;
1836
 
 
1837
 
        // FIXME: choose CDATA when the value contains special characters
1838
 
        QDomText textNode = ownerDoc.createTextNode(text);
1839
 
        return textNode;
1840
 
    }
1841
 
 
1842
 
    // nothing matches? strange...
1843
 
    return QDomNode();
1844
 
}
1845
 
 
1846
 
QDomNode KoXmlNodeData::asQDomNode(QDomDocument ownerDoc) const
1847
 
{
1848
 
    return itemAsQDomNode(ownerDoc, packedDoc, nodeDepth, nodeIndex);
1849
 
}
1850
 
 
1851
 
#else
1852
 
 
1853
 
static QDomNode itemAsQDomNode(QDomDocument ownerDoc, KoXmlPackedDocument* packedDoc,
1854
 
                               unsigned nodeIndex)
1855
 
{
1856
 
    // sanity check
1857
 
    if (!packedDoc)
1858
 
        return QDomNode();
1859
 
 
1860
 
    KoXmlPackedItem& item = packedDoc->items[nodeIndex];
1861
 
 
1862
 
    // nothing to do here
1863
 
    if (item.type == KoXmlNode::NullNode)
1864
 
        return QDomNode();
1865
 
 
1866
 
    // create the element properly
1867
 
    if (item.type == KoXmlNode::ElementNode) {
1868
 
        QDomElement element;
1869
 
 
1870
 
        QString name = packedDoc->stringList[item.nameIndex];
1871
 
        QString nsURI = packedDoc->stringList[item.nsURIIndex];
1872
 
 
1873
 
        if (packedDoc->processNamespace)
1874
 
            element = ownerDoc.createElementNS(nsURI, name);
1875
 
        else
1876
 
            element = ownerDoc.createElement(name);
1877
 
 
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);
1884
 
 
1885
 
            // element already outside our depth
1886
 
            if (!item.attr && (item.type == KoXmlNode::ElementNode))
1887
 
                if (item.depth <= (unsigned)nodeDepth)
1888
 
                    break;
1889
 
 
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;
1895
 
                QString prefix;
1896
 
 
1897
 
                QString qName; // with prefix
1898
 
                QString localName;  // without prefix, i.e. local name
1899
 
 
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);
1904
 
 
1905
 
                if (packedDoc->processNamespace) {
1906
 
                    element.setAttributeNS(nsURI, qName, value);
1907
 
                    element.setAttribute(localName, value);
1908
 
                } else
1909
 
                    element.setAttribute(name, value);
1910
 
            }
1911
 
 
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);
1917
 
            }
1918
 
        }
1919
 
 
1920
 
        return element;
1921
 
    }
1922
 
 
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);
1928
 
        return textNode;
1929
 
    }
1930
 
 
1931
 
    // nothing matches? strange...
1932
 
    return QDomNode();
1933
 
}
1934
 
 
1935
 
QDomNode KoXmlNodeData::asQDomNode(QDomDocument ownerDoc) const
1936
 
{
1937
 
    return itemAsQDomNode(ownerDoc, packedDoc, nodeIndex);
1938
 
}
1939
 
 
1940
 
#endif
1941
 
 
1942
 
void KoXmlNodeData::dump()
1943
 
{
1944
 
    printf("NodeData %p\n", (void*)this);
1945
 
 
1946
 
    printf("  nodeIndex: %d\n", (int)nodeIndex);
1947
 
    printf("  packedDoc: %p\n", (void*)packedDoc);
1948
 
 
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));
1954
 
 
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);
1960
 
 
1961
 
    printf("  count: %ld\n", count);
1962
 
 
1963
 
    if (loaded)
1964
 
        printf("  loaded: TRUE\n");
1965
 
    else
1966
 
        printf("  loaded: FALSE\n");
1967
 
}
1968
 
 
1969
 
// ==================================================================
1970
 
//
1971
 
//         KoXmlNode
1972
 
//
1973
 
// ==================================================================
1974
 
 
1975
 
// Creates a null node
1976
 
KoXmlNode::KoXmlNode()
1977
 
{
1978
 
    d = &KoXmlNodeData::null;
1979
 
}
1980
 
 
1981
 
// Destroys this node
1982
 
KoXmlNode::~KoXmlNode()
1983
 
{
1984
 
    if (d)
1985
 
        if (d != &KoXmlNodeData::null)
1986
 
            d->unref();
1987
 
 
1988
 
    d = 0;
1989
 
}
1990
 
 
1991
 
// Creates a copy of another node
1992
 
KoXmlNode::KoXmlNode(const KoXmlNode& node)
1993
 
{
1994
 
    d = node.d;
1995
 
    d->ref();
1996
 
}
1997
 
 
1998
 
// Creates a node for specific implementation
1999
 
KoXmlNode::KoXmlNode(KoXmlNodeData* data)
2000
 
{
2001
 
    d = data;
2002
 
    data->ref();
2003
 
}
2004
 
 
2005
 
// Creates a shallow copy of another node
2006
 
KoXmlNode& KoXmlNode::operator=(const KoXmlNode & node)
2007
 
{
2008
 
    d->unref();
2009
 
    d = node.d;
2010
 
    d->ref();
2011
 
    return *this;
2012
 
}
2013
 
 
2014
 
// Note: two null nodes are always equal
2015
 
bool KoXmlNode::operator==(const KoXmlNode& node) const
2016
 
{
2017
 
    if (isNull() && node.isNull()) return true;
2018
 
    return(d == node.d);
2019
 
}
2020
 
 
2021
 
// Note: two null nodes are always equal
2022
 
bool KoXmlNode::operator!=(const KoXmlNode& node) const
2023
 
{
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);
2028
 
}
2029
 
 
2030
 
KoXmlNode::NodeType KoXmlNode::nodeType() const
2031
 
{
2032
 
    return d->nodeType;
2033
 
}
2034
 
 
2035
 
bool KoXmlNode::isNull() const
2036
 
{
2037
 
    return d->nodeType == NullNode;
2038
 
}
2039
 
 
2040
 
bool KoXmlNode::isElement() const
2041
 
{
2042
 
    return d->nodeType == ElementNode;
2043
 
}
2044
 
 
2045
 
bool KoXmlNode::isText() const
2046
 
{
2047
 
    return (d->nodeType == TextNode) || isCDATASection();
2048
 
}
2049
 
 
2050
 
bool KoXmlNode::isCDATASection() const
2051
 
{
2052
 
    return d->nodeType == CDATASectionNode;
2053
 
}
2054
 
 
2055
 
bool KoXmlNode::isDocument() const
2056
 
{
2057
 
    return d->nodeType == DocumentNode;
2058
 
}
2059
 
 
2060
 
bool KoXmlNode::isDocumentType() const
2061
 
{
2062
 
    return d->nodeType == DocumentTypeNode;
2063
 
}
2064
 
 
2065
 
void KoXmlNode::clear()
2066
 
{
2067
 
    d->unref();
2068
 
    d = new KoXmlNodeData;
2069
 
}
2070
 
 
2071
 
QString KoXmlNode::nodeName() const
2072
 
{
2073
 
    return d->nodeName();
2074
 
}
2075
 
 
2076
 
QString KoXmlNode::prefix() const
2077
 
{
2078
 
    return isElement() ? d->prefix : QString();
2079
 
}
2080
 
 
2081
 
QString KoXmlNode::namespaceURI() const
2082
 
{
2083
 
    return isElement() ? d->namespaceURI : QString();
2084
 
}
2085
 
 
2086
 
QString KoXmlNode::localName() const
2087
 
{
2088
 
    return isElement() ? d->localName : QString();
2089
 
}
2090
 
 
2091
 
KoXmlDocument KoXmlNode::ownerDocument() const
2092
 
{
2093
 
    KoXmlNodeData* node = d;
2094
 
    while (node->parent) node = node->parent;
2095
 
 
2096
 
    return KoXmlDocument(node);
2097
 
}
2098
 
 
2099
 
KoXmlNode KoXmlNode::parentNode() const
2100
 
{
2101
 
    return d->parent ? KoXmlNode(d->parent) : KoXmlNode();
2102
 
}
2103
 
 
2104
 
bool KoXmlNode::hasChildNodes() const
2105
 
{
2106
 
    if (isText())
2107
 
        return false;
2108
 
 
2109
 
    if (!d->loaded)
2110
 
        d->loadChildren();
2111
 
 
2112
 
    return d->first != 0 ;
2113
 
}
2114
 
 
2115
 
int KoXmlNode::childNodesCount() const
2116
 
{
2117
 
    if (isText())
2118
 
        return 0;
2119
 
 
2120
 
    if (!d->loaded)
2121
 
        d->loadChildren();
2122
 
 
2123
 
    KoXmlNodeData* node = d->first;
2124
 
    int count = 0;
2125
 
    while (node) {
2126
 
        count++;
2127
 
        node = node->next;
2128
 
    }
2129
 
 
2130
 
    return count;
2131
 
}
2132
 
 
2133
 
QStringList KoXmlNode::attributeNames() const
2134
 
{
2135
 
    if (!d->loaded)
2136
 
        d->loadChildren();
2137
 
 
2138
 
    return d->attributeNames();
2139
 
}
2140
 
 
2141
 
KoXmlNode KoXmlNode::firstChild() const
2142
 
{
2143
 
    if (!d->loaded)
2144
 
        d->loadChildren();
2145
 
    return d->first ? KoXmlNode(d->first) : KoXmlNode();
2146
 
}
2147
 
 
2148
 
KoXmlNode KoXmlNode::lastChild() const
2149
 
{
2150
 
    if (!d->loaded)
2151
 
        d->loadChildren();
2152
 
    return d->last ? KoXmlNode(d->last) : KoXmlNode();
2153
 
}
2154
 
 
2155
 
KoXmlNode KoXmlNode::nextSibling() const
2156
 
{
2157
 
    return d->next ? KoXmlNode(d->next) : KoXmlNode();
2158
 
}
2159
 
 
2160
 
KoXmlNode KoXmlNode::previousSibling() const
2161
 
{
2162
 
    return d->prev ? KoXmlNode(d->prev) : KoXmlNode();
2163
 
}
2164
 
 
2165
 
KoXmlNode KoXmlNode::namedItem(const QString& name) const
2166
 
{
2167
 
    if (!d->loaded)
2168
 
        d->loadChildren();
2169
 
 
2170
 
    KoXmlNodeData* node = d->first;
2171
 
    while (node) {
2172
 
        if (node->nodeName() == name)
2173
 
            return KoXmlNode(node);
2174
 
        node = node->next;
2175
 
    }
2176
 
 
2177
 
    // not found
2178
 
    return KoXmlNode();
2179
 
}
2180
 
 
2181
 
KoXmlNode KoXmlNode::namedItemNS(const QString& nsURI, const QString& name) const
2182
 
{
2183
 
    if (!d->loaded)
2184
 
        d->loadChildren();
2185
 
 
2186
 
 
2187
 
    KoXmlNodeData* node = d->first;
2188
 
    while (node) {
2189
 
        if (node->namespaceURI == nsURI)
2190
 
            if (node->localName == name)
2191
 
                return KoXmlNode(node);
2192
 
        node = node->next;
2193
 
    }
2194
 
 
2195
 
    // not found
2196
 
    return KoXmlNode();
2197
 
}
2198
 
 
2199
 
KoXmlElement KoXmlNode::toElement() const
2200
 
{
2201
 
    return isElement() ? KoXmlElement(d) : KoXmlElement();
2202
 
}
2203
 
 
2204
 
KoXmlText KoXmlNode::toText() const
2205
 
{
2206
 
    return isText() ? KoXmlText(d) : KoXmlText();
2207
 
}
2208
 
 
2209
 
KoXmlCDATASection KoXmlNode::toCDATASection() const
2210
 
{
2211
 
    return isCDATASection() ? KoXmlCDATASection(d) : KoXmlCDATASection();
2212
 
}
2213
 
 
2214
 
KoXmlDocument KoXmlNode::toDocument() const
2215
 
{
2216
 
    if (isDocument())
2217
 
        return KoXmlDocument(d);
2218
 
 
2219
 
    KoXmlDocument newDocument;
2220
 
    newDocument.d->emptyDocument = false;
2221
 
    return newDocument;
2222
 
}
2223
 
 
2224
 
void KoXmlNode::load(int depth)
2225
 
{
2226
 
    d->loadChildren(depth);
2227
 
}
2228
 
 
2229
 
void KoXmlNode::unload()
2230
 
{
2231
 
    d->unloadChildren();
2232
 
}
2233
 
 
2234
 
QDomNode KoXmlNode::asQDomNode(QDomDocument ownerDoc) const
2235
 
{
2236
 
    return d->asQDomNode(ownerDoc);
2237
 
}
2238
 
 
2239
 
// ==================================================================
2240
 
//
2241
 
//         KoXmlElement
2242
 
//
2243
 
// ==================================================================
2244
 
 
2245
 
// Creates an empty element
2246
 
KoXmlElement::KoXmlElement(): KoXmlNode(new KoXmlNodeData)
2247
 
{
2248
 
    // because referenced also once in KoXmlNode constructor
2249
 
    d->unref();
2250
 
}
2251
 
 
2252
 
KoXmlElement::~KoXmlElement()
2253
 
{
2254
 
    if (d)
2255
 
        if (d != &KoXmlNodeData::null)
2256
 
            d->unref();
2257
 
 
2258
 
    d = 0;
2259
 
}
2260
 
 
2261
 
// Creates a shallow copy of another element
2262
 
KoXmlElement::KoXmlElement(const KoXmlElement& element): KoXmlNode(element.d)
2263
 
{
2264
 
}
2265
 
 
2266
 
KoXmlElement::KoXmlElement(KoXmlNodeData* data): KoXmlNode(data)
2267
 
{
2268
 
}
2269
 
 
2270
 
// Copies another element
2271
 
KoXmlElement& KoXmlElement::operator=(const KoXmlElement & element)
2272
 
{
2273
 
    KoXmlNode::operator=(element);
2274
 
    return *this;
2275
 
}
2276
 
 
2277
 
bool KoXmlElement::operator== (const KoXmlElement& element) const
2278
 
{
2279
 
    if (isNull() || element.isNull()) return false;
2280
 
    return (d == element.d);
2281
 
}
2282
 
 
2283
 
bool KoXmlElement::operator!= (const KoXmlElement& element) const
2284
 
{
2285
 
    if (isNull() && element.isNull()) return false;
2286
 
    if (isNull() || element.isNull()) return true;
2287
 
    return (d != element.d);
2288
 
}
2289
 
 
2290
 
QString KoXmlElement::tagName() const
2291
 
{
2292
 
    return isElement() ? ((KoXmlNodeData*)d)->tagName : QString();
2293
 
}
2294
 
 
2295
 
QString KoXmlElement::text() const
2296
 
{
2297
 
    return d->text();
2298
 
}
2299
 
 
2300
 
QString KoXmlElement::attribute(const QString& name) const
2301
 
{
2302
 
    if (!isElement())
2303
 
        return QString();
2304
 
 
2305
 
    if (!d->loaded)
2306
 
        d->loadChildren();
2307
 
 
2308
 
    return d->attribute(name, QString());
2309
 
}
2310
 
 
2311
 
QString KoXmlElement::attribute(const QString& name,
2312
 
                                const QString& defaultValue) const
2313
 
{
2314
 
    if (!isElement())
2315
 
        return defaultValue;
2316
 
 
2317
 
    if (!d->loaded)
2318
 
        d->loadChildren();
2319
 
 
2320
 
    return d->attribute(name, defaultValue);
2321
 
}
2322
 
 
2323
 
QString KoXmlElement::attributeNS(const QString& namespaceURI,
2324
 
                                  const QString& localName, const QString& defaultValue) const
2325
 
{
2326
 
    if (!isElement())
2327
 
        return defaultValue;
2328
 
 
2329
 
    if (!d->loaded)
2330
 
        d->loadChildren();
2331
 
 
2332
 
    KoXmlStringPair key(namespaceURI, localName);
2333
 
    if (d->attrNS.contains(key))
2334
 
        return d->attrNS[ key ];
2335
 
    else
2336
 
        return defaultValue;
2337
 
 
2338
 
//  return d->attributeNS( namespaceURI, localName, defaultValue );
2339
 
}
2340
 
 
2341
 
bool KoXmlElement::hasAttribute(const QString& name) const
2342
 
{
2343
 
    if (!d->loaded)
2344
 
        d->loadChildren();
2345
 
 
2346
 
    return isElement() ? d->hasAttribute(name) : false;
2347
 
}
2348
 
 
2349
 
bool KoXmlElement::hasAttributeNS(const QString& namespaceURI,
2350
 
                                  const QString& localName) const
2351
 
{
2352
 
    if (!d->loaded)
2353
 
        d->loadChildren();
2354
 
 
2355
 
    return isElement() ? d->hasAttributeNS(namespaceURI, localName) : false;
2356
 
}
2357
 
 
2358
 
// ==================================================================
2359
 
//
2360
 
//         KoXmlText
2361
 
//
2362
 
// ==================================================================
2363
 
 
2364
 
KoXmlText::KoXmlText(): KoXmlNode(new KoXmlNodeData)
2365
 
{
2366
 
    // because referenced also once in KoXmlNode constructor
2367
 
    d->unref();
2368
 
}
2369
 
 
2370
 
KoXmlText::~KoXmlText()
2371
 
{
2372
 
    if (d)
2373
 
        if (d != &KoXmlNodeData::null)
2374
 
            d->unref();
2375
 
 
2376
 
    d = 0;
2377
 
}
2378
 
 
2379
 
KoXmlText::KoXmlText(const KoXmlText& text): KoXmlNode(text.d)
2380
 
{
2381
 
}
2382
 
 
2383
 
KoXmlText::KoXmlText(KoXmlNodeData* data): KoXmlNode(data)
2384
 
{
2385
 
}
2386
 
 
2387
 
bool KoXmlText::isText() const
2388
 
{
2389
 
    return true;
2390
 
}
2391
 
 
2392
 
QString KoXmlText::data() const
2393
 
{
2394
 
    return d->data();
2395
 
}
2396
 
 
2397
 
KoXmlText& KoXmlText::operator=(const KoXmlText & element)
2398
 
{
2399
 
    KoXmlNode::operator=(element);
2400
 
    return *this;
2401
 
}
2402
 
 
2403
 
// ==================================================================
2404
 
//
2405
 
//         KoXmlCDATASection
2406
 
//
2407
 
// ==================================================================
2408
 
 
2409
 
KoXmlCDATASection::KoXmlCDATASection(): KoXmlText()
2410
 
{
2411
 
    d->nodeType = KoXmlNode::CDATASectionNode;
2412
 
}
2413
 
 
2414
 
KoXmlCDATASection::KoXmlCDATASection(const KoXmlCDATASection& cdata)
2415
 
        : KoXmlText(cdata)
2416
 
{
2417
 
    *this = cdata;
2418
 
}
2419
 
 
2420
 
KoXmlCDATASection::~KoXmlCDATASection()
2421
 
{
2422
 
    d->unref();
2423
 
    d = 0;
2424
 
}
2425
 
 
2426
 
KoXmlCDATASection::KoXmlCDATASection(KoXmlNodeData* cdata):
2427
 
        KoXmlText(cdata)
2428
 
{
2429
 
}
2430
 
 
2431
 
bool KoXmlCDATASection::isCDATASection() const
2432
 
{
2433
 
    return true;
2434
 
}
2435
 
 
2436
 
KoXmlCDATASection& KoXmlCDATASection::operator=(const KoXmlCDATASection & cdata)
2437
 
{
2438
 
    KoXmlNode::operator=(cdata);
2439
 
    return *this;
2440
 
}
2441
 
 
2442
 
// ==================================================================
2443
 
//
2444
 
//         KoXmlDocumentType
2445
 
//
2446
 
// ==================================================================
2447
 
 
2448
 
KoXmlDocumentType::KoXmlDocumentType(): KoXmlNode(new KoXmlNodeData)
2449
 
{
2450
 
    // because referenced also once in KoXmlNode constructor
2451
 
    d->unref();
2452
 
}
2453
 
 
2454
 
KoXmlDocumentType::~KoXmlDocumentType()
2455
 
{
2456
 
    d->unref();
2457
 
    d = 0;
2458
 
}
2459
 
 
2460
 
KoXmlDocumentType::KoXmlDocumentType(const KoXmlDocumentType& dt):
2461
 
        KoXmlNode(dt.d)
2462
 
{
2463
 
}
2464
 
 
2465
 
QString KoXmlDocumentType::name() const
2466
 
{
2467
 
    return nodeName();
2468
 
}
2469
 
 
2470
 
KoXmlDocumentType::KoXmlDocumentType(KoXmlNodeData* dt): KoXmlNode(dt)
2471
 
{
2472
 
}
2473
 
 
2474
 
KoXmlDocumentType& KoXmlDocumentType::operator=(const KoXmlDocumentType & dt)
2475
 
{
2476
 
    KoXmlNode::operator=(dt);
2477
 
    return *this;
2478
 
}
2479
 
 
2480
 
// ==================================================================
2481
 
//
2482
 
//         KoXmlDocument
2483
 
//
2484
 
// ==================================================================
2485
 
 
2486
 
KoXmlDocument::KoXmlDocument(): KoXmlNode()
2487
 
{
2488
 
    d->emptyDocument = false;
2489
 
}
2490
 
 
2491
 
KoXmlDocument::~KoXmlDocument()
2492
 
{
2493
 
    if (d)
2494
 
        if (d != &KoXmlNodeData::null)
2495
 
            d->unref();
2496
 
 
2497
 
    d = 0;
2498
 
}
2499
 
 
2500
 
KoXmlDocument::KoXmlDocument(KoXmlNodeData* data): KoXmlNode(data)
2501
 
{
2502
 
    d->emptyDocument = true;
2503
 
}
2504
 
 
2505
 
// Creates a copy of another document
2506
 
KoXmlDocument::KoXmlDocument(const KoXmlDocument& doc): KoXmlNode(doc.d)
2507
 
{
2508
 
}
2509
 
 
2510
 
// Creates a shallow copy of another document
2511
 
KoXmlDocument& KoXmlDocument::operator=(const KoXmlDocument & doc)
2512
 
{
2513
 
    KoXmlNode::operator=(doc);
2514
 
    return *this;
2515
 
}
2516
 
 
2517
 
// Checks if this document and doc are equals
2518
 
bool KoXmlDocument::operator==(const KoXmlDocument& doc) const
2519
 
{
2520
 
    return(d == doc.d);
2521
 
}
2522
 
 
2523
 
// Checks if this document and doc are not equals
2524
 
bool KoXmlDocument::operator!=(const KoXmlDocument& doc) const
2525
 
{
2526
 
    return(d != doc.d);
2527
 
}
2528
 
 
2529
 
KoXmlElement KoXmlDocument::documentElement() const
2530
 
{
2531
 
    d->loadChildren();
2532
 
 
2533
 
    for (KoXmlNodeData* node = d->first; node;) {
2534
 
        if (node->nodeType == KoXmlNode::ElementNode)
2535
 
            return KoXmlElement(node);
2536
 
        else node = node->next;
2537
 
    }
2538
 
 
2539
 
    return KoXmlElement();
2540
 
}
2541
 
 
2542
 
KoXmlDocumentType KoXmlDocument::doctype() const
2543
 
{
2544
 
    return dt;
2545
 
}
2546
 
 
2547
 
QString KoXmlDocument::nodeName() const
2548
 
{
2549
 
    if (d->emptyDocument)
2550
 
        return QLatin1String("#document");
2551
 
    else
2552
 
        return QString();
2553
 
}
2554
 
 
2555
 
void KoXmlDocument::clear()
2556
 
{
2557
 
    KoXmlNode::clear();
2558
 
    d->emptyDocument = false;
2559
 
}
2560
 
 
2561
 
bool KoXmlDocument::setContent(QXmlInputSource *source, QXmlReader *reader,
2562
 
                               QString* errorMsg, int* errorLine, int* errorColumn)
2563
 
{
2564
 
    if (d->nodeType != KoXmlNode::DocumentNode) {
2565
 
        d->unref();
2566
 
        d = new KoXmlNodeData;
2567
 
        d->nodeType = KoXmlNode::DocumentNode;
2568
 
    }
2569
 
 
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;
2575
 
        dt.d->parent = d;
2576
 
    }
2577
 
 
2578
 
    return result;
2579
 
}
2580
 
 
2581
 
// no namespace processing
2582
 
bool KoXmlDocument::setContent(QIODevice* device, QString* errorMsg,
2583
 
                               int* errorLine, int* errorColumn)
2584
 
{
2585
 
    return setContent(device, false, errorMsg, errorLine, errorColumn);
2586
 
}
2587
 
 
2588
 
bool KoXmlDocument::setContent(QIODevice* device, bool namespaceProcessing,
2589
 
                               QString* errorMsg, int* errorLine, int* errorColumn)
2590
 
{
2591
 
    if (d->nodeType != KoXmlNode::DocumentNode) {
2592
 
        d->unref();
2593
 
        d = new KoXmlNodeData;
2594
 
        d->nodeType = KoXmlNode::DocumentNode;
2595
 
    }
2596
 
 
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);
2601
 
 
2602
 
    // FIXME this hack is apparently private
2603
 
    //reader.setUndefEntityInAttrHack(true);
2604
 
 
2605
 
    QXmlInputSource source(device);
2606
 
 
2607
 
    dt = KoXmlDocumentType();
2608
 
    bool result = d->setContent(&source, &reader, errorMsg, errorLine, errorColumn);
2609
 
    {
2610
 
        dt.d->nodeType = KoXmlNode::DocumentTypeNode;
2611
 
        dt.d->tagName = d->packedDoc->docType;
2612
 
        dt.d->parent = d;
2613
 
    }
2614
 
 
2615
 
    return result;
2616
 
}
2617
 
 
2618
 
bool KoXmlDocument::setContent(const QByteArray& text, bool namespaceProcessing,
2619
 
                               QString *errorMsg, int *errorLine, int *errorColumn)
2620
 
{
2621
 
    QBuffer buffer;
2622
 
    buffer.setData(text);
2623
 
    return setContent(&buffer, namespaceProcessing, errorMsg, errorLine, errorColumn);
2624
 
}
2625
 
 
2626
 
bool KoXmlDocument::setContent(const QString& text, bool namespaceProcessing,
2627
 
                               QString *errorMsg, int *errorLine, int *errorColumn)
2628
 
{
2629
 
    if (d->nodeType != KoXmlNode::DocumentNode) {
2630
 
        d->unref();
2631
 
        d = new KoXmlNodeData;
2632
 
        d->nodeType = KoXmlNode::DocumentNode;
2633
 
    }
2634
 
 
2635
 
    QXmlInputSource source;
2636
 
    source.setData(text);
2637
 
 
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;
2643
 
        dt.d->parent = d;
2644
 
    }
2645
 
 
2646
 
    return result;
2647
 
}
2648
 
 
2649
 
bool KoXmlDocument::setContent(const QString& text,
2650
 
                               QString *errorMsg, int *errorLine, int *errorColumn)
2651
 
{
2652
 
    return setContent(text, false, errorMsg, errorLine, errorColumn);
2653
 
}
2654
 
 
2655
 
#endif
2656
 
 
2657
 
// ==================================================================
2658
 
//
2659
 
//         KoXmlInputSource
2660
 
//
2661
 
// ==================================================================
2662
 
 
2663
 
/*
2664
 
  This is the size of buffer every time we read a chunk of data from the device.
2665
 
 
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.
2669
 
 
2670
 
  Note 2: a much larger buffer won't speed up significantly. This is because
2671
 
  the bottleneck is elsewhere, not here.
2672
 
 
2673
 
*/
2674
 
 
2675
 
#define KOXML_BUFSIZE 16*1024  // should be adequate
2676
 
 
2677
 
KoXmlInputSource::KoXmlInputSource(QIODevice *dev): QXmlInputSource(),
2678
 
        device(dev)
2679
 
{
2680
 
    int mib = 106; // UTF-8
2681
 
    decoder = QTextCodec::codecForMib(mib)->makeDecoder();
2682
 
 
2683
 
    stringLength = 0;
2684
 
    stringIndex = 0;
2685
 
    buffer = new char[KOXML_BUFSIZE];
2686
 
 
2687
 
    reset();
2688
 
}
2689
 
 
2690
 
KoXmlInputSource::~KoXmlInputSource()
2691
 
{
2692
 
    delete decoder;
2693
 
    delete [] buffer;
2694
 
}
2695
 
 
2696
 
void KoXmlInputSource::setData(const QString& dat)
2697
 
{
2698
 
    Q_UNUSED(dat);
2699
 
}
2700
 
 
2701
 
void KoXmlInputSource::setData(const QByteArray& dat)
2702
 
{
2703
 
    Q_UNUSED(dat);
2704
 
}
2705
 
 
2706
 
void KoXmlInputSource::fetchData()
2707
 
{
2708
 
}
2709
 
 
2710
 
QString KoXmlInputSource::data() const
2711
 
{
2712
 
    return QString();
2713
 
}
2714
 
 
2715
 
QChar KoXmlInputSource::next()
2716
 
{
2717
 
    if (stringIndex >= stringLength) {
2718
 
        // read more data first
2719
 
        qint64 bytes = device->read(buffer, KOXML_BUFSIZE);
2720
 
        if (bytes == 0)
2721
 
            return EndOfDocument;
2722
 
 
2723
 
        stringData = decoder->toUnicode(buffer, bytes);
2724
 
        stringLength = stringData.length();
2725
 
        stringIndex = 0;
2726
 
    }
2727
 
 
2728
 
    return stringData[stringIndex++];
2729
 
}
2730
 
 
2731
 
void KoXmlInputSource::reset()
2732
 
{
2733
 
    device->seek(0);
2734
 
}
2735
 
 
2736
 
QString KoXmlInputSource::fromRawData(const QByteArray &data, bool beginning)
2737
 
{
2738
 
    Q_UNUSED(data);
2739
 
    Q_UNUSED(beginning);
2740
 
    return QString();
2741
 
}
2742
 
 
2743
 
// ==================================================================
2744
 
//
2745
 
//         functions in KoXml namespace
2746
 
//
2747
 
// ==================================================================
2748
 
 
2749
 
KoXmlElement KoXml::namedItemNS(const KoXmlNode& node, const char* nsURI,
2750
 
                                const char* localName)
2751
 
{
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();
2759
 
    }
2760
 
    return KoXmlElement();
2761
 
#else
2762
 
    return node.namedItemNS(nsURI, localName).toElement();
2763
 
#endif
2764
 
}
2765
 
 
2766
 
void KoXml::load(KoXmlNode& node, int depth)
2767
 
{
2768
 
#ifdef KOXML_USE_QDOM
2769
 
    // do nothing, QDom has no on-demand loading
2770
 
    Q_UNUSED(node);
2771
 
    Q_UNUSED(depth);
2772
 
#else
2773
 
    node.load(depth);
2774
 
#endif
2775
 
}
2776
 
 
2777
 
 
2778
 
void KoXml::unload(KoXmlNode& node)
2779
 
{
2780
 
#ifdef KOXML_USE_QDOM
2781
 
    // do nothing, QDom has no on-demand unloading
2782
 
    Q_UNUSED(node);
2783
 
#else
2784
 
    node.unload();
2785
 
#endif
2786
 
}
2787
 
 
2788
 
int KoXml::childNodesCount(const KoXmlNode& node)
2789
 
{
2790
 
#ifdef KOXML_USE_QDOM
2791
 
    return node.childNodes().count();
2792
 
#else
2793
 
    // compatibility function, because no need to implement
2794
 
    // a class like QDomNodeList
2795
 
    return node.childNodesCount();
2796
 
#endif
2797
 
}
2798
 
 
2799
 
QStringList KoXml::attributeNames(const KoXmlNode& node)
2800
 
{
2801
 
#ifdef KOXML_USE_QDOM
2802
 
    QStringList result;
2803
 
 
2804
 
    QDomNamedNodeMap attrMap = node.attributes();
2805
 
    for (int i = 0; i < attrMap.count(); i++)
2806
 
        result += attrMap.item(i).toAttr().name();
2807
 
 
2808
 
    return result;
2809
 
#else
2810
 
    // compatibility function, because no need to implement
2811
 
    // a class like QDomNamedNodeMap
2812
 
    return node.attributeNames();
2813
 
#endif
2814
 
}
2815
 
 
2816
 
QDomNode KoXml::asQDomNode(QDomDocument ownerDoc, const KoXmlNode& node)
2817
 
{
2818
 
#ifdef KOXML_USE_QDOM
2819
 
    Q_UNUSED(ownerDoc);
2820
 
    return node;
2821
 
#else
2822
 
    return node.asQDomNode(ownerDoc);
2823
 
#endif
2824
 
}
2825
 
 
2826
 
QDomElement KoXml::asQDomElement(QDomDocument ownerDoc, const KoXmlElement& element)
2827
 
{
2828
 
    return KoXml::asQDomNode(ownerDoc, element).toElement();
2829
 
}
2830
 
 
2831
 
QDomDocument KoXml::asQDomDocument(QDomDocument ownerDoc, const KoXmlDocument& document)
2832
 
{
2833
 
    return KoXml::asQDomNode(ownerDoc, document).toDocument();
2834
 
}
2835
 
 
2836
 
bool KoXml::setDocument(KoXmlDocument& doc, QIODevice* device,
2837
 
                        bool namespaceProcessing, QString* errorMsg, int* errorLine,
2838
 
                        int* errorColumn)
2839
 
{
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);
2844
 
 
2845
 
    KoXmlInputSource* source = new KoXmlInputSource(device);
2846
 
    bool result = doc.setContent(source, &reader, errorMsg, errorLine, errorColumn);
2847
 
    delete source;
2848
 
    return result;
2849
 
}
2850
 
 
2851
 
bool KoXml::setDocument(KoXmlDocument& doc, QIODevice* device,
2852
 
                        QXmlSimpleReader* reader, QString* errorMsg, int* errorLine, int* errorColumn)
2853
 
{
2854
 
    KoXmlInputSource* source = new KoXmlInputSource(device);
2855
 
    bool result = doc.setContent(source, reader, errorMsg, errorLine, errorColumn);
2856
 
    delete source;
2857
 
    return result;
2858
 
}
2859