Warning, file /office/calligra/libs/store/KoXmlReader.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* This file is part of the KDE project 0002 Copyright (C) 2005-2006 Ariya Hidayat <ariya@kde.org> 0003 0004 This library is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU Library General Public 0006 License as published by the Free Software Foundation; either 0007 version 2 of the License, or (at your option) any later version. 0008 0009 This library is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 Library General Public License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this library; see the file COPYING.LIB. If not, write to 0016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 Boston, MA 02110-1301, USA. 0018 */ 0019 0020 #include "KoXmlReader.h" 0021 #include "KoXmlNS.h" 0022 0023 /* 0024 This is a memory-efficient DOM implementation for Calligra. See the API 0025 documentation for details. 0026 0027 IMPORTANT ! 0028 0029 * When you change this stuff, make sure it DOES NOT BREAK the test suite. 0030 Build tests/koxmlreadertest.cpp and verify it. Many sleepless nights 0031 have been sacrificed for this piece of code, do not let those precious 0032 hours wasted! 0033 0034 * Run koxmlreadertest.cpp WITH Valgrind and make sure NO illegal 0035 memory read/write and any type of leak occurs. If you are not familiar 0036 with Valgrind then RTFM first and come back again later on. 0037 0038 * The public API shall remain as compatible as QDom. 0039 0040 * All QDom-compatible methods should behave the same. All QDom-compatible 0041 functions should return the same result. In case of doubt, run 0042 koxmlreadertest.cpp but uncomment KOXML_USE_QDOM in koxmlreader.h 0043 so that the tests are performed with standard QDom. 0044 0045 Some differences compared to QDom: 0046 0047 - DOM tree in KoXmlDocument is read-only, you can not modify it. This is 0048 sufficient for Calligra since the tree is only accessed when loading 0049 a document to the application. For saving the document to XML file, 0050 use KoXmlWriter. 0051 0052 - Because the dynamic loading and unloading, you have to use the 0053 nodes (and therefore also elements) carefully since the whole API 0054 (just like QDom) is reference-based, not pointer-based. If the 0055 parent node is unloaded from memory, the reference is not valid 0056 anymore and may give unpredictable result. 0057 The easiest way: use the node/element in very short time only. 0058 0059 - Comment node (like QDomComment) is not implemented as comments are 0060 simply ignored. 0061 0062 - DTD, entity and entity reference are not handled. Thus, the associated 0063 nodes (like QDomDocumentType, QDomEntity, QDomEntityReference) are also 0064 not implemented. 0065 0066 - Attribute mapping node is not implemented. But of course, functions to 0067 query attributes of an element are available. 0068 0069 0070 */ 0071 0072 #include <QTextCodec> 0073 #include <QTextDecoder> 0074 0075 #ifndef KOXML_USE_QDOM 0076 0077 #include <QtXml> 0078 #include <QDomDocument> 0079 #include <QXmlStreamReader> 0080 #include <QXmlStreamEntityResolver> 0081 0082 #include <QBuffer> 0083 #include <QByteArray> 0084 #include <QDataStream> 0085 #include <QHash> 0086 #include <QPair> 0087 #include <QStringList> 0088 #include <QVector> 0089 0090 /* 0091 Use more compact representation of in-memory nodes. 0092 0093 Advantages: faster iteration, can facilitate real-time compression. 0094 Disadvantages: still buggy, eat slightly more memory. 0095 */ 0096 #define KOXML_COMPACT 0097 0098 /* 0099 Use real-time compression. Only works in conjuction with KOXML_COMPACT 0100 above because otherwise the non-compact layout will slow down everything. 0101 */ 0102 #define KOXML_COMPRESS 0103 0104 0105 // prevent mistake, see above 0106 #ifdef KOXML_COMPRESS 0107 #ifndef KOXML_COMPACT 0108 #error Please enable also KOXML_COMPACT 0109 #endif 0110 #endif 0111 0112 // this is used to quickly get namespaced attribute(s) 0113 typedef QPair<QString, QString> KoXmlStringPair; 0114 0115 class KoQName { 0116 public: 0117 QString nsURI; 0118 QString name; 0119 0120 explicit KoQName(const QString& nsURI_, const QString& name_) 0121 : nsURI(nsURI_), name(name_) {} 0122 bool operator==(const KoQName& qname) const { 0123 // local name is more likely to differ, so compare that first 0124 return name == qname.name && nsURI == qname.nsURI; 0125 } 0126 }; 0127 0128 uint qHash(const KoQName& qname) 0129 { 0130 // possibly add a faster hash function that only includes some trailing 0131 // part of the nsURI 0132 0133 // in case of doubt, use this: 0134 // return qHash(qname.nsURI)^qHash(qname.name); 0135 return qHash(qname.nsURI)^qHash(qname.name); 0136 } 0137 0138 // this simplistic hash is rather fast-and-furious. it works because 0139 // likely there is very few namespaced attributes per element 0140 static inline uint qHash(const KoXmlStringPair &p, uint /*seed*/ = 0) 0141 { 0142 return qHash(p.second[0].unicode()) ^ 0x1477; 0143 0144 // in case of doubt, use this: 0145 // return qHash(p.first)^qHash(p.second); 0146 } 0147 0148 static inline bool operator==(const KoXmlStringPair &a, const KoXmlStringPair &b) 0149 { 0150 return a.second == b.second && a.first == b.first; 0151 } 0152 0153 // Older versions of OpenOffice.org used different namespaces. This function 0154 // does translate the old namespaces into the new ones. 0155 static QString fixNamespace(const QString &nsURI) 0156 { 0157 static QString office = QString::fromLatin1("http://openoffice.org/2000/office"); 0158 static QString text = QString::fromLatin1("http://openoffice.org/2000/text"); 0159 static QString style = QString::fromLatin1("http://openoffice.org/2000/style"); 0160 static QString fo = QString::fromLatin1("http://www.w3.org/1999/XSL/Format"); 0161 static QString table = QString::fromLatin1("http://openoffice.org/2000/table"); 0162 static QString drawing = QString::fromLatin1("http://openoffice.org/2000/drawing"); 0163 static QString datastyle = QString::fromLatin1("http://openoffice.org/2000/datastyle"); 0164 static QString svg = QString::fromLatin1("http://www.w3.org/2000/svg"); 0165 static QString chart = QString::fromLatin1("http://openoffice.org/2000/chart"); 0166 static QString dr3d = QString::fromLatin1("http://openoffice.org/2000/dr3d"); 0167 static QString form = QString::fromLatin1("http://openoffice.org/2000/form"); 0168 static QString script = QString::fromLatin1("http://openoffice.org/2000/script"); 0169 static QString meta = QString::fromLatin1("http://openoffice.org/2000/meta"); 0170 static QString config = QString::fromLatin1("http://openoffice.org/2001/config"); 0171 static QString pres = QString::fromLatin1("http://openoffice.org/2000/presentation"); 0172 static QString manifest = QString::fromLatin1("http://openoffice.org/2001/manifest"); 0173 if (nsURI == text) 0174 return KoXmlNS::text; 0175 if (nsURI == style) 0176 return KoXmlNS::style; 0177 if (nsURI == office) 0178 return KoXmlNS::office; 0179 if (nsURI == fo) 0180 return KoXmlNS::fo; 0181 if (nsURI == table) 0182 return KoXmlNS::table; 0183 if (nsURI == drawing) 0184 return KoXmlNS::draw; 0185 if (nsURI == datastyle) 0186 return KoXmlNS::number; 0187 if (nsURI == svg) 0188 return KoXmlNS::svg; 0189 if (nsURI == chart) 0190 return KoXmlNS::chart; 0191 if (nsURI == dr3d) 0192 return KoXmlNS::dr3d; 0193 if (nsURI == form) 0194 return KoXmlNS::form; 0195 if (nsURI == script) 0196 return KoXmlNS::script; 0197 if (nsURI == meta) 0198 return KoXmlNS::meta; 0199 if (nsURI == config) 0200 return KoXmlNS::config; 0201 if (nsURI == pres) 0202 return KoXmlNS::presentation; 0203 if (nsURI == manifest) 0204 return KoXmlNS::manifest; 0205 return nsURI; 0206 } 0207 0208 // ================================================================== 0209 // 0210 // KoXmlPackedItem 0211 // 0212 // ================================================================== 0213 0214 // 12 bytes on most system 32 bit systems, 16 bytes on 64 bit systems 0215 class KoXmlPackedItem 0216 { 0217 public: 0218 bool attr: 1; 0219 KoXmlNode::NodeType type: 3; 0220 0221 #ifdef KOXML_COMPACT 0222 quint32 childStart: 28; 0223 #else 0224 unsigned depth: 28; 0225 #endif 0226 0227 unsigned qnameIndex; 0228 QString value; 0229 0230 // it is important NOT to have a copy constructor, so that growth is optimal 0231 // see https://doc.qt.io/qt-5/containers.html#growth-strategies 0232 #if 0 0233 KoXmlPackedItem(): attr(false), type(KoXmlNode::NullNode), childStart(0), depth(0) {} 0234 #endif 0235 }; 0236 0237 Q_DECLARE_TYPEINFO(KoXmlPackedItem, Q_MOVABLE_TYPE); 0238 0239 #ifdef KOXML_COMPRESS 0240 static QDataStream& operator<<(QDataStream& s, const KoXmlPackedItem& item) 0241 { 0242 quint8 flag = item.attr ? 1 : 0; 0243 0244 s << flag; 0245 s << (quint8) item.type; 0246 s << item.childStart; 0247 s << item.qnameIndex; 0248 s << item.value; 0249 0250 return s; 0251 } 0252 0253 static QDataStream& operator>>(QDataStream& s, KoXmlPackedItem& item) 0254 { 0255 quint8 flag; 0256 quint8 type; 0257 quint32 child; 0258 QString value; 0259 0260 s >> flag; 0261 s >> type; 0262 s >> child; 0263 s >> item.qnameIndex; 0264 s >> value; 0265 0266 item.attr = (flag != 0); 0267 item.type = (KoXmlNode::NodeType) type; 0268 item.childStart = child; 0269 item.value = value; 0270 0271 return s; 0272 } 0273 #endif 0274 0275 // ================================================================== 0276 // 0277 // KoXmlPackedDocument 0278 // 0279 // ================================================================== 0280 0281 #ifdef KOXML_COMPRESS 0282 0283 #include "KoXmlVector.h" 0284 0285 // when number of buffered items reach this, compression will start 0286 // small value will give better memory usage at the cost of speed 0287 // bigger value will be better in term of speed, but use more memory 0288 #define ITEMS_FULL (1*256) 0289 0290 typedef KoXmlVector<KoXmlPackedItem, ITEMS_FULL> KoXmlPackedGroup; 0291 #else 0292 typedef QVector<KoXmlPackedItem> KoXmlPackedGroup; 0293 #endif 0294 0295 // growth strategy: increase every GROUP_GROW_SIZE items 0296 // this will override standard QVector's growth strategy 0297 #define GROUP_GROW_SHIFT 3 0298 #define GROUP_GROW_SIZE (1 << GROUP_GROW_SHIFT) 0299 0300 class KoXmlPackedDocument 0301 { 0302 public: 0303 bool processNamespace; 0304 #ifdef KOXML_COMPACT 0305 // map given depth to the list of items 0306 QHash<int, KoXmlPackedGroup> groups; 0307 #else 0308 QVector<KoXmlPackedItem> items; 0309 #endif 0310 0311 QList<KoQName> qnameList; 0312 QString docType; 0313 0314 private: 0315 QHash<KoQName, unsigned> qnameHash; 0316 0317 unsigned cacheQName(const QString& name, const QString& nsURI) { 0318 KoQName qname(nsURI, name); 0319 0320 const unsigned ii = qnameHash.value(qname, (unsigned)-1); 0321 if (ii != (unsigned)-1) 0322 return ii; 0323 0324 // not yet declared, so we add it 0325 unsigned i = qnameList.count(); 0326 qnameList.append(qname); 0327 qnameHash.insert(qname, i); 0328 0329 return i; 0330 } 0331 0332 QHash<QString, unsigned> valueHash; 0333 QStringList valueList; 0334 0335 QString cacheValue(const QString& value) { 0336 if (value.isEmpty()) 0337 return 0; 0338 0339 const unsigned& ii = valueHash[value]; 0340 if (ii > 0) 0341 return valueList[ii]; 0342 0343 // not yet declared, so we add it 0344 unsigned i = valueList.count(); 0345 valueList.append(value); 0346 valueHash.insert(value, i); 0347 0348 return valueList[i]; 0349 } 0350 0351 #ifdef KOXML_COMPACT 0352 public: 0353 const KoXmlPackedItem& itemAt(unsigned depth, unsigned index) { 0354 const KoXmlPackedGroup& group = groups[depth]; 0355 return group[index]; 0356 } 0357 0358 unsigned itemCount(unsigned depth) { 0359 const KoXmlPackedGroup& group = groups[depth]; 0360 return group.count(); 0361 } 0362 0363 /* 0364 NOTE: 0365 Function clear, newItem, addElement, addAttribute, addText, 0366 addCData, addProcessing are all related. These are all necessary 0367 for stateful manipulation of the document. See also the calls 0368 to these function from parseDocument(). 0369 0370 The state itself is defined by the member variables 0371 currentDepth and the groups (see above). 0372 */ 0373 0374 unsigned currentDepth; 0375 0376 KoXmlPackedItem& newItem(unsigned depth) { 0377 KoXmlPackedGroup& group = groups[depth]; 0378 0379 #ifdef KOXML_COMPRESS 0380 KoXmlPackedItem& item = group.newItem(); 0381 #else 0382 // reserve up front 0383 if ((groups.size() % GROUP_GROW_SIZE) == 0) 0384 group.reserve(GROUP_GROW_SIZE * (1 + (groups.size() >> GROUP_GROW_SHIFT))); 0385 group.resize(group.count() + 1); 0386 0387 KoXmlPackedItem& item = group[group.count()-1]; 0388 #endif 0389 0390 // this is necessary, because intentionally we don't want to have 0391 // a constructor for KoXmlPackedItem 0392 item.attr = false; 0393 item.type = KoXmlNode::NullNode; 0394 item.qnameIndex = 0; 0395 item.childStart = itemCount(depth + 1); 0396 item.value.clear(); 0397 0398 return item; 0399 } 0400 0401 void clear() { 0402 currentDepth = 0; 0403 qnameHash.clear(); 0404 qnameList.clear(); 0405 valueHash.clear(); 0406 valueList.clear(); 0407 groups.clear(); 0408 docType.clear(); 0409 0410 // first node is root 0411 KoXmlPackedItem& rootItem = newItem(0); 0412 rootItem.type = KoXmlNode::DocumentNode; 0413 } 0414 0415 void finish() { 0416 // won't be needed anymore 0417 qnameHash.clear(); 0418 valueHash.clear(); 0419 valueList.clear(); 0420 0421 // optimize, see documentation on QVector::squeeze 0422 for (int d = 0; d < groups.count(); ++d) { 0423 KoXmlPackedGroup& group = groups[d]; 0424 group.squeeze(); 0425 } 0426 } 0427 0428 // in case namespace processing, 'name' contains the prefix already 0429 void addElement(const QString& name, const QString& nsURI) { 0430 KoXmlPackedItem& item = newItem(currentDepth + 1); 0431 item.type = KoXmlNode::ElementNode; 0432 item.qnameIndex = cacheQName(name, nsURI); 0433 0434 ++currentDepth; 0435 } 0436 0437 void closeElement() { 0438 --currentDepth; 0439 } 0440 0441 void addDTD(const QString& dt) { 0442 docType = dt; 0443 } 0444 0445 void addAttribute(const QString& name, const QString& nsURI, const QString& value) { 0446 KoXmlPackedItem& item = newItem(currentDepth + 1); 0447 item.attr = true; 0448 item.qnameIndex = cacheQName(name, nsURI); 0449 //item.value = cacheValue( value ); 0450 item.value = value; 0451 } 0452 0453 void addText(const QString& text) { 0454 KoXmlPackedItem& item = newItem(currentDepth + 1); 0455 item.type = KoXmlNode::TextNode; 0456 item.value = text; 0457 } 0458 0459 void addCData(const QString& text) { 0460 KoXmlPackedItem& item = newItem(currentDepth + 1); 0461 item.type = KoXmlNode::CDATASectionNode; 0462 item.value = text; 0463 } 0464 0465 void addProcessingInstruction() { 0466 KoXmlPackedItem& item = newItem(currentDepth + 1); 0467 item.type = KoXmlNode::ProcessingInstructionNode; 0468 } 0469 0470 public: 0471 KoXmlPackedDocument(): processNamespace(false), currentDepth(0) { 0472 clear(); 0473 } 0474 0475 #else 0476 0477 private: 0478 unsigned elementDepth; 0479 0480 public: 0481 0482 KoXmlPackedItem& newItem() { 0483 unsigned count = items.count() + 512; 0484 count = 1024 * (count >> 10); 0485 items.reserve(count); 0486 0487 items.resize(items.count() + 1); 0488 0489 // this is necessary, because intentionally we don't want to have 0490 // a constructor for KoXmlPackedItem 0491 KoXmlPackedItem& item = items[items.count()-1]; 0492 item.attr = false; 0493 item.type = KoXmlNode::NullNode; 0494 item.qnameIndex = 0; 0495 item.depth = 0; 0496 0497 return item; 0498 } 0499 0500 void addElement(const QString& name, const QString& nsURI) { 0501 // we are going one level deeper 0502 ++elementDepth; 0503 0504 KoXmlPackedItem& item = newItem(); 0505 0506 item.attr = false; 0507 item.type = KoXmlNode::ElementNode; 0508 item.depth = elementDepth; 0509 item.qnameIndex = cacheQName(name, nsURI); 0510 } 0511 0512 void closeElement() { 0513 // we are going up one level 0514 --elementDepth; 0515 } 0516 0517 void addDTD(const QString& dt) { 0518 docType = dt; 0519 } 0520 0521 void addAttribute(const QString& name, const QString& nsURI, const QString& value) { 0522 KoXmlPackedItem& item = newItem(); 0523 0524 item.attr = true; 0525 item.type = KoXmlNode::NullNode; 0526 item.depth = elementDepth; 0527 item.qnameIndex = cacheQName(name, nsURI); 0528 //item.value = cacheValue( value ); 0529 item.value = value; 0530 } 0531 0532 void addText(const QString& str) { 0533 KoXmlPackedItem& item = newItem(); 0534 0535 item.attr = false; 0536 item.type = KoXmlNode::TextNode; 0537 item.depth = elementDepth + 1; 0538 item.qnameIndex = 0; 0539 item.value = str; 0540 } 0541 0542 void addCData(const QString& str) { 0543 KoXmlPackedItem& item = newItem(); 0544 0545 item.attr = false; 0546 item.type = KoXmlNode::CDATASectionNode; 0547 item.depth = elementDepth + 1; 0548 item.qnameIndex = 0; 0549 item.value = str; 0550 } 0551 0552 void addProcessingInstruction() { 0553 KoXmlPackedItem& item = newItem(); 0554 0555 item.attr = false; 0556 item.type = KoXmlNode::ProcessingInstructionNode; 0557 item.depth = elementDepth + 1; 0558 item.qnameIndex = 0; 0559 item.value.clear(); 0560 } 0561 0562 void clear() { 0563 qnameHash.clear(); 0564 qnameList.clear(); 0565 valueHash.clear(); 0566 valueList.clear(); 0567 items.clear(); 0568 elementDepth = 0; 0569 0570 KoXmlPackedItem& rootItem = newItem(); 0571 rootItem.attr = false; 0572 rootItem.type = KoXmlNode::DocumentNode; 0573 rootItem.depth = 0; 0574 rootItem.qnameIndex = 0; 0575 } 0576 0577 void finish() { 0578 qnameHash.clear(); 0579 valueList.clear(); 0580 valueHash.clear(); 0581 items.squeeze(); 0582 } 0583 0584 KoXmlPackedDocument(): processNamespace(false), elementDepth(0) { 0585 } 0586 0587 #endif 0588 0589 }; 0590 0591 namespace { 0592 0593 class ParseError { 0594 public: 0595 QString errorMsg; 0596 int errorLine; 0597 int errorColumn; 0598 bool error; 0599 0600 ParseError() :errorLine(-1), errorColumn(-1), error(false) {} 0601 }; 0602 0603 void parseElement(QXmlStreamReader &xml, KoXmlPackedDocument &doc, bool stripSpaces = true); 0604 0605 // parse one element as if this were a standalone xml document 0606 ParseError parseDocument(QXmlStreamReader &xml, KoXmlPackedDocument &doc, bool stripSpaces = true) 0607 { 0608 doc.clear(); 0609 ParseError error; 0610 xml.readNext(); 0611 while (!xml.atEnd() && xml.tokenType() != QXmlStreamReader::EndDocument && !xml.hasError()) { 0612 switch (xml.tokenType()) { 0613 case QXmlStreamReader::StartElement: 0614 parseElement(xml, doc, stripSpaces); 0615 break; 0616 case QXmlStreamReader::DTD: 0617 doc.addDTD(xml.dtdName().toString()); 0618 break; 0619 case QXmlStreamReader::StartDocument: 0620 if (!xml.documentEncoding().isEmpty() || !xml.documentVersion().isEmpty()) { 0621 doc.addProcessingInstruction(); 0622 } 0623 break; 0624 case QXmlStreamReader::ProcessingInstruction: 0625 doc.addProcessingInstruction(); 0626 break; 0627 default: 0628 break; 0629 } 0630 xml.readNext(); 0631 } 0632 if (xml.hasError()) { 0633 error.error = true; 0634 error.errorMsg = xml.errorString(); 0635 error.errorColumn = xml.columnNumber(); 0636 error.errorLine = xml.lineNumber(); 0637 } else { 0638 doc.finish(); 0639 } 0640 return error; 0641 } 0642 0643 void parseElementContents(QXmlStreamReader &xml, KoXmlPackedDocument &doc) 0644 { 0645 xml.readNext(); 0646 QString ws; 0647 while (!xml.atEnd()) { 0648 switch (xml.tokenType()) { 0649 case QXmlStreamReader::EndElement: 0650 // if an element contains only whitespace, put it in the dom 0651 if (!ws.isEmpty()) { 0652 doc.addText(ws); 0653 } 0654 return; 0655 case QXmlStreamReader::StartElement: 0656 // The whitespaces between > and < are also a text element 0657 if (!ws.isEmpty()) { 0658 doc.addText(ws); 0659 ws.clear(); 0660 } 0661 // Do not strip spaces 0662 parseElement(xml, doc, false); 0663 break; 0664 case QXmlStreamReader::Characters: 0665 if (xml.isCDATA()) { 0666 doc.addCData(xml.text().toString()); 0667 } else if (!xml.isWhitespace()) { 0668 doc.addText(xml.text().toString()); 0669 } else { 0670 ws += xml.text(); 0671 } 0672 break; 0673 case QXmlStreamReader::ProcessingInstruction: 0674 doc.addProcessingInstruction(); 0675 break; 0676 default: 0677 break; 0678 } 0679 xml.readNext(); 0680 } 0681 } 0682 0683 void parseElementContentsStripSpaces(QXmlStreamReader &xml, KoXmlPackedDocument &doc) 0684 { 0685 xml.readNext(); 0686 QString ws; 0687 bool sawElement = false; 0688 while (!xml.atEnd()) { 0689 switch (xml.tokenType()) { 0690 case QXmlStreamReader::EndElement: 0691 // if an element contains only whitespace, put it in the dom 0692 if (!ws.isEmpty() && !sawElement) { 0693 doc.addText(ws); 0694 } 0695 return; 0696 case QXmlStreamReader::StartElement: 0697 sawElement = true; 0698 // Do strip spaces 0699 parseElement(xml, doc, true); 0700 break; 0701 case QXmlStreamReader::Characters: 0702 if (xml.isCDATA()) { 0703 doc.addCData(xml.text().toString()); 0704 } else if (!xml.isWhitespace()) { 0705 doc.addText(xml.text().toString()); 0706 } else if (!sawElement) { 0707 ws += xml.text(); 0708 } 0709 break; 0710 case QXmlStreamReader::ProcessingInstruction: 0711 doc.addProcessingInstruction(); 0712 break; 0713 default: 0714 break; 0715 } 0716 xml.readNext(); 0717 } 0718 } 0719 0720 void parseElement(QXmlStreamReader &xml, KoXmlPackedDocument &doc, bool stripSpaces) 0721 { 0722 // Unfortunately MSVC fails using QXmlStreamReader::const_iterator 0723 // so we apply a for loop instead. https://bugreports.qt.io/browse/QTBUG-45368 0724 doc.addElement(xml.qualifiedName().toString(), 0725 fixNamespace(xml.namespaceUri().toString())); 0726 QXmlStreamAttributes attr = xml.attributes(); 0727 for (int a = 0; a < attr.count(); a++) { 0728 doc.addAttribute(attr[a].qualifiedName().toString(), 0729 attr[a].namespaceUri().toString(), 0730 attr[a].value().toString()); 0731 } 0732 if (stripSpaces) 0733 parseElementContentsStripSpaces(xml, doc); 0734 else 0735 parseElementContents(xml, doc); 0736 // reader.tokenType() is now QXmlStreamReader::EndElement 0737 doc.closeElement(); 0738 } 0739 } 0740 0741 0742 // ================================================================== 0743 // 0744 // KoXmlNodeData 0745 // 0746 // ================================================================== 0747 0748 class KoXmlNodeData 0749 { 0750 public: 0751 0752 explicit KoXmlNodeData(unsigned long initialRefCount = 1); 0753 ~KoXmlNodeData(); 0754 0755 // generic properties 0756 KoXmlNode::NodeType nodeType; 0757 bool loaded; 0758 0759 #ifdef KOXML_COMPACT 0760 unsigned nodeDepth; 0761 #endif 0762 0763 QString tagName; 0764 QString namespaceURI; 0765 QString prefix; 0766 QString localName; 0767 0768 void ref() { 0769 ++refCount; 0770 } 0771 void unref() { 0772 if (!--refCount) { 0773 delete this; 0774 } 0775 } 0776 0777 // type information 0778 QString nodeName() const; 0779 0780 // for tree and linked-list 0781 KoXmlNodeData* parent; 0782 KoXmlNodeData* prev; 0783 KoXmlNodeData* next; 0784 KoXmlNodeData* first; 0785 KoXmlNodeData* last; 0786 0787 QString text(); 0788 0789 // node manipulation 0790 void clear(); 0791 0792 // attributes 0793 inline void setAttribute(const QString& name, const QString& value); 0794 inline QString attribute(const QString& name, const QString& def) const; 0795 inline bool hasAttribute(const QString& name) const; 0796 inline void setAttributeNS(const QString& nsURI, const QString& name, const QString& value); 0797 inline QString attributeNS(const QString& nsURI, const QString& name, const QString& def) const; 0798 inline bool hasAttributeNS(const QString& nsURI, const QString& name) const; 0799 inline void clearAttributes(); 0800 inline QStringList attributeNames() const; 0801 inline QList< QPair<QString, QString> > attributeFullNames() const; 0802 0803 0804 // for text and CDATA 0805 QString data() const; 0806 0807 // reference from within the packed doc 0808 KoXmlPackedDocument* packedDoc; 0809 unsigned long nodeIndex; 0810 0811 // used when doing on-demand (re)parse 0812 void loadChildren(int depth = 1); 0813 void unloadChildren(); 0814 0815 void dump(); 0816 0817 static KoXmlNodeData null; 0818 0819 // compatibility 0820 void asQDomNode(QDomDocument& ownerDoc) const; 0821 0822 private: 0823 QHash<QString, QString> attr; 0824 QHash<KoXmlStringPair, QString> attrNS; 0825 QString textData; 0826 // reference counting 0827 unsigned long refCount; 0828 friend class KoXmlElement; 0829 }; 0830 0831 KoXmlNodeData KoXmlNodeData::null; 0832 0833 0834 KoXmlNodeData::KoXmlNodeData(unsigned long initialRefCount) 0835 : nodeType(KoXmlNode::NullNode) 0836 , loaded(false) 0837 #ifdef KOXML_COMPACT 0838 , nodeDepth(0) 0839 #endif 0840 , parent(0), prev(0), next(0), first(0), last(0) 0841 , packedDoc(0), nodeIndex(0) 0842 , refCount(initialRefCount) 0843 { 0844 } 0845 0846 KoXmlNodeData::~KoXmlNodeData() 0847 { 0848 clear(); 0849 } 0850 0851 void KoXmlNodeData::clear() 0852 { 0853 if (first) 0854 for (KoXmlNodeData* node = first; node ;) { 0855 KoXmlNodeData* next = node->next; 0856 node->unref(); 0857 node = next; 0858 } 0859 0860 // only document can delete these 0861 // normal nodes don't "own" them 0862 if (nodeType == KoXmlNode::DocumentNode) 0863 delete packedDoc; 0864 0865 nodeType = KoXmlNode::NullNode; 0866 tagName.clear(); 0867 prefix.clear(); 0868 namespaceURI.clear(); 0869 textData.clear(); 0870 packedDoc = 0; 0871 0872 attr.clear(); 0873 attrNS.clear(); 0874 0875 parent = 0; 0876 prev = next = 0; 0877 first = last = 0; 0878 0879 loaded = false; 0880 } 0881 0882 QString KoXmlNodeData::text() 0883 { 0884 QString t; 0885 0886 loadChildren(); 0887 0888 KoXmlNodeData* node = first; 0889 while (node) { 0890 switch (node->nodeType) { 0891 case KoXmlNode::ElementNode: 0892 t += node->text(); break; 0893 case KoXmlNode::TextNode: 0894 t += node->data(); break; 0895 case KoXmlNode::CDATASectionNode: 0896 t += node->data(); break; 0897 default: break; 0898 } 0899 node = node->next; 0900 } 0901 0902 return t; 0903 } 0904 0905 QString KoXmlNodeData::nodeName() const 0906 { 0907 switch (nodeType) { 0908 case KoXmlNode::ElementNode: { 0909 QString n(tagName); 0910 if (!prefix.isEmpty()) 0911 n.prepend(':').prepend(prefix); 0912 return n; 0913 } 0914 break; 0915 0916 case KoXmlNode::TextNode: return QLatin1String("#text"); 0917 case KoXmlNode::CDATASectionNode: return QLatin1String("#cdata-section"); 0918 case KoXmlNode::DocumentNode: return QLatin1String("#document"); 0919 case KoXmlNode::DocumentTypeNode: return tagName; 0920 0921 default: return QString(); break; 0922 } 0923 0924 // should not happen 0925 return QString(); 0926 } 0927 0928 void KoXmlNodeData::setAttribute(const QString& name, const QString& value) 0929 { 0930 attr.insert(name, value); 0931 } 0932 0933 QString KoXmlNodeData::attribute(const QString& name, const QString& def) const 0934 { 0935 return attr.value(name, def); 0936 } 0937 0938 bool KoXmlNodeData::hasAttribute(const QString& name) const 0939 { 0940 return attr.contains(name); 0941 } 0942 0943 void KoXmlNodeData::setAttributeNS(const QString& nsURI, 0944 const QString& name, const QString& value) 0945 { 0946 int i = name.indexOf(':'); 0947 if (i != -1) { 0948 QString localName(name.mid(i + 1)); 0949 KoXmlStringPair key(nsURI, localName); 0950 attrNS.insert(key, value); 0951 } 0952 } 0953 0954 QString KoXmlNodeData::attributeNS(const QString& nsURI, const QString& name, 0955 const QString& def) const 0956 { 0957 KoXmlStringPair key(nsURI, name); 0958 return attrNS.value(key, def); 0959 } 0960 0961 bool KoXmlNodeData::hasAttributeNS(const QString& nsURI, const QString& name) const 0962 { 0963 KoXmlStringPair key(nsURI, name); 0964 return attrNS.contains(key); 0965 } 0966 0967 void KoXmlNodeData::clearAttributes() 0968 { 0969 attr.clear(); 0970 attrNS.clear(); 0971 } 0972 0973 // FIXME how about namespaced attributes ? 0974 QStringList KoXmlNodeData::attributeNames() const 0975 { 0976 QStringList result; 0977 result = attr.keys(); 0978 0979 return result; 0980 } 0981 0982 0983 QList< QPair<QString, QString> > KoXmlNodeData::attributeFullNames() const 0984 { 0985 QList< QPair<QString, QString> > result; 0986 result = attrNS.keys(); 0987 0988 return result; 0989 } 0990 0991 QString KoXmlNodeData::data() const 0992 { 0993 return textData; 0994 } 0995 0996 #ifdef KOXML_COMPACT 0997 0998 void KoXmlNodeData::loadChildren(int depth) 0999 { 1000 // sanity check 1001 if (!packedDoc) return; 1002 1003 // already loaded ? 1004 if (loaded && (depth <= 1)) return; 1005 1006 // in case depth is different 1007 unloadChildren(); 1008 1009 1010 KoXmlNodeData* lastDat = 0; 1011 1012 unsigned childStop = 0; 1013 if (nodeIndex == packedDoc->itemCount(nodeDepth) - 1) 1014 childStop = packedDoc->itemCount(nodeDepth + 1); 1015 else { 1016 const KoXmlPackedItem& next = packedDoc->itemAt(nodeDepth, nodeIndex + 1); 1017 childStop = next.childStart; 1018 } 1019 1020 const KoXmlPackedItem& self = packedDoc->itemAt(nodeDepth, nodeIndex); 1021 1022 for (unsigned i = self.childStart; i < childStop; ++i) { 1023 const KoXmlPackedItem& item = packedDoc->itemAt(nodeDepth + 1, i); 1024 bool textItem = (item.type == KoXmlNode::TextNode); 1025 textItem |= (item.type == KoXmlNode::CDATASectionNode); 1026 1027 // attribute belongs to this node 1028 if (item.attr) { 1029 KoQName qname = packedDoc->qnameList[item.qnameIndex]; 1030 QString value = item.value; 1031 1032 QString prefix; 1033 1034 QString qName; // with prefix 1035 QString localName; // without prefix, i.e. local name 1036 1037 localName = qName = qname.name; 1038 int i = qName.indexOf(':'); 1039 if (i != -1) prefix = qName.left(i); 1040 if (i != -1) localName = qName.mid(i + 1); 1041 1042 if (packedDoc->processNamespace) { 1043 setAttributeNS(qname.nsURI, qName, value); 1044 setAttribute(localName, value); 1045 } else 1046 setAttribute(qName, value); 1047 } else { 1048 KoQName qname = packedDoc->qnameList[item.qnameIndex]; 1049 QString value = item.value; 1050 1051 QString nodeName = qname.name; 1052 QString localName; 1053 QString prefix; 1054 1055 if (packedDoc->processNamespace) { 1056 localName = qname.name; 1057 int di = qname.name.indexOf(':'); 1058 if (di != -1) { 1059 localName = qname.name.mid(di + 1); 1060 prefix = qname.name.left(di); 1061 } 1062 nodeName = localName; 1063 } 1064 1065 // make a node out of this item 1066 KoXmlNodeData* dat = new KoXmlNodeData; 1067 dat->nodeIndex = i; 1068 dat->packedDoc = packedDoc; 1069 dat->nodeDepth = nodeDepth + 1; 1070 dat->nodeType = item.type; 1071 dat->tagName = nodeName; 1072 dat->localName = localName; 1073 dat->prefix = prefix; 1074 dat->namespaceURI = qname.nsURI; 1075 dat->parent = this; 1076 dat->prev = lastDat; 1077 dat->next = 0; 1078 dat->first = 0; 1079 dat->last = 0; 1080 dat->loaded = false; 1081 dat->textData = (textItem) ? value : QString(); 1082 1083 // adjust our linked-list 1084 first = (first) ? first : dat; 1085 last = dat; 1086 if (lastDat) 1087 lastDat->next = dat; 1088 lastDat = dat; 1089 1090 // recursive 1091 if (depth > 1) 1092 dat->loadChildren(depth - 1); 1093 } 1094 } 1095 1096 loaded = true; 1097 } 1098 1099 #else 1100 1101 void KoXmlNodeData::loadChildren(int depth) 1102 { 1103 // sanity check 1104 if (!packedDoc) return; 1105 1106 // already loaded ? 1107 if (loaded && (depth <= 1)) return; 1108 1109 // cause we don't know how deep this node's children already loaded are 1110 unloadChildren(); 1111 1112 KoXmlNodeData* lastDat = 0; 1113 int nodeDepth = packedDoc->items[nodeIndex].depth; 1114 1115 for (int i = nodeIndex + 1; i < packedDoc->items.count(); ++i) { 1116 KoXmlPackedItem& item = packedDoc->items[i]; 1117 bool textItem = (item.type == KoXmlNode::TextNode); 1118 textItem |= (item.type == KoXmlNode::CDATASectionNode); 1119 1120 // element already outside our depth 1121 if (!item.attr && (item.type == KoXmlNode::ElementNode)) 1122 if (item.depth <= (unsigned)nodeDepth) 1123 break; 1124 1125 // attribute belongs to this node 1126 if (item.attr && (item.depth == (unsigned)nodeDepth)) { 1127 KoQName qname = packedDoc->qnameList[item.qnameIndex]; 1128 QString value = item.value; 1129 1130 QString prefix; 1131 1132 QString qName; // with prefix 1133 QString localName; // without prefix, i.e. local name 1134 1135 localName = qName = qname.name; 1136 int i = qName.indexOf(':'); 1137 if (i != -1) prefix = qName.left(i); 1138 if (i != -1) localName = qName.mid(i + 1); 1139 1140 if (packedDoc->processNamespace) { 1141 setAttributeNS(qname.nsURI, qName, value); 1142 setAttribute(localName, value); 1143 } else 1144 setAttribute(qname.name, value); 1145 } 1146 1147 // the child node 1148 if (!item.attr) { 1149 bool instruction = (item.type == KoXmlNode::ProcessingInstructionNode); 1150 bool ok = (textItem || instruction) ? (item.depth == (unsigned)nodeDepth) : (item.depth == (unsigned)nodeDepth + 1); 1151 1152 ok = (item.depth == (unsigned)nodeDepth + 1); 1153 1154 if (ok) { 1155 KoQName qname = packedDoc->qnameList[item.qnameIndex]; 1156 QString value = item.value; 1157 1158 QString nodeName = qname.name; 1159 QString localName; 1160 QString prefix; 1161 1162 if (packedDoc->processNamespace) { 1163 localName = qname.name; 1164 int di = qname.name.indexOf(':'); 1165 if (di != -1) { 1166 localName = qname.name.mid(di + 1); 1167 prefix = qname.name.left(di); 1168 } 1169 nodeName = localName; 1170 } 1171 1172 // make a node out of this item 1173 KoXmlNodeData* dat = new KoXmlNodeData; 1174 dat->nodeIndex = i; 1175 dat->packedDoc = packedDoc; 1176 dat->nodeType = item.type; 1177 dat->tagName = nodeName; 1178 dat->localName = localName; 1179 dat->prefix = prefix; 1180 dat->namespaceURI = qname.nsURI; 1181 dat->count = 1; 1182 dat->parent = this; 1183 dat->prev = lastDat; 1184 dat->next = 0; 1185 dat->first = 0; 1186 dat->last = 0; 1187 dat->loaded = false; 1188 dat->textData = (textItem) ? value : QString(); 1189 1190 // adjust our linked-list 1191 first = (first) ? first : dat; 1192 last = dat; 1193 if (lastDat) 1194 lastDat->next = dat; 1195 lastDat = dat; 1196 1197 // recursive 1198 if (depth > 1) 1199 dat->loadChildren(depth - 1); 1200 } 1201 } 1202 } 1203 1204 loaded = true; 1205 } 1206 #endif 1207 1208 void KoXmlNodeData::unloadChildren() 1209 { 1210 // sanity check 1211 if (!packedDoc) return; 1212 1213 if (!loaded) return; 1214 1215 if (first) 1216 for (KoXmlNodeData* node = first; node ;) { 1217 KoXmlNodeData* next = node->next; 1218 node->unloadChildren(); 1219 node->unref(); 1220 node = next; 1221 } 1222 1223 clearAttributes(); 1224 loaded = false; 1225 first = last = 0; 1226 } 1227 1228 #ifdef KOXML_COMPACT 1229 1230 1231 static void itemAsQDomNode(QDomDocument& ownerDoc, KoXmlPackedDocument* packedDoc, 1232 unsigned nodeDepth, unsigned nodeIndex, QDomNode parentNode = QDomNode()) 1233 { 1234 // sanity check 1235 if (!packedDoc) 1236 return; 1237 1238 const KoXmlPackedItem& self = packedDoc->itemAt(nodeDepth, nodeIndex); 1239 1240 unsigned childStop = 0; 1241 if (nodeIndex == packedDoc->itemCount(nodeDepth) - 1) 1242 childStop = packedDoc->itemCount(nodeDepth + 1); 1243 else { 1244 const KoXmlPackedItem& next = packedDoc->itemAt(nodeDepth, nodeIndex + 1); 1245 childStop = next.childStart; 1246 } 1247 1248 // nothing to do here 1249 if (self.type == KoXmlNode::NullNode) 1250 return; 1251 1252 // create the element properly 1253 if (self.type == KoXmlNode::ElementNode) { 1254 QDomElement element; 1255 1256 KoQName qname = packedDoc->qnameList[self.qnameIndex]; 1257 qname.nsURI = fixNamespace(qname.nsURI); 1258 1259 if (packedDoc->processNamespace) 1260 element = ownerDoc.createElementNS(qname.nsURI, qname.name); 1261 else 1262 element = ownerDoc.createElement(qname.name); 1263 1264 if ( parentNode.isNull() ) { 1265 ownerDoc.appendChild( element ); 1266 } else { 1267 parentNode.appendChild( element ); 1268 } 1269 // check all subnodes for attributes 1270 for (unsigned i = self.childStart; i < childStop; ++i) { 1271 const KoXmlPackedItem& item = packedDoc->itemAt(nodeDepth + 1, i); 1272 bool textItem = (item.type == KoXmlNode::TextNode); 1273 textItem |= (item.type == KoXmlNode::CDATASectionNode); 1274 1275 // attribute belongs to this node 1276 if (item.attr) { 1277 KoQName qname = packedDoc->qnameList[item.qnameIndex]; 1278 qname.nsURI = fixNamespace(qname.nsURI ); 1279 QString value = item.value; 1280 1281 QString prefix; 1282 1283 QString qName; // with prefix 1284 QString localName; // without prefix, i.e. local name 1285 1286 localName = qName = qname.name; 1287 int i = qName.indexOf(':'); 1288 if (i != -1) prefix = qName.left(i); 1289 if (i != -1) localName = qName.mid(i + 1); 1290 1291 if (packedDoc->processNamespace) { 1292 element.setAttributeNS(qname.nsURI, qName, value); 1293 element.setAttribute(localName, value); 1294 } else 1295 element.setAttribute(qname.name, value); 1296 } else { 1297 // add it recursively 1298 itemAsQDomNode(ownerDoc, packedDoc, nodeDepth + 1, i, element); 1299 } 1300 } 1301 return; 1302 } 1303 1304 // create the text node 1305 if (self.type == KoXmlNode::TextNode) { 1306 QString text = self.value; 1307 1308 // FIXME: choose CDATA when the value contains special characters 1309 QDomText textNode = ownerDoc.createTextNode(text); 1310 if ( parentNode.isNull() ) { 1311 ownerDoc.appendChild( textNode ); 1312 } else { 1313 parentNode.appendChild( textNode ); 1314 } 1315 return; 1316 } 1317 // nothing matches? strange... 1318 } 1319 1320 void KoXmlNodeData::asQDomNode(QDomDocument& ownerDoc) const 1321 { 1322 itemAsQDomNode(ownerDoc, packedDoc, nodeDepth, nodeIndex); 1323 } 1324 1325 #else 1326 1327 static void itemAsQDomNode(QDomDocument& ownerDoc, KoXmlPackedDocument* packedDoc, 1328 unsigned nodeIndex, QDomNode parentNode = QDomNode()) 1329 { 1330 // sanity check 1331 if (!packedDoc) 1332 return; 1333 1334 KoXmlPackedItem& item = packedDoc->items[nodeIndex]; 1335 1336 // nothing to do here 1337 if (item.type == KoXmlNode::NullNode) 1338 return; 1339 1340 // create the element properly 1341 if (item.type == KoXmlNode::ElementNode) { 1342 QDomElement element; 1343 1344 KoQName qname = packedDoc->qnameList[item.qnameIndex]; 1345 qname.nsURI = fixNamespace(qname.nsURI); 1346 1347 if (packedDoc->processNamespace) 1348 element = ownerDoc.createElementNS(qname.nsURI, qname.name); 1349 else 1350 element = ownerDoc.createElement(qname.name); 1351 1352 if ( parentNode.isNull() ) { 1353 ownerDoc.appendChild( element ); 1354 } else { 1355 parentNode.appendChild( element ); 1356 } 1357 // check all subnodes for attributes 1358 int nodeDepth = item.depth; 1359 for (int i = nodeIndex + 1; i < packedDoc->items.count(); ++i) { 1360 KoXmlPackedItem& item = packedDoc->items[i]; 1361 bool textItem = (item.type == KoXmlNode::TextNode); 1362 textItem |= (item.type == KoXmlNode::CDATASectionNode); 1363 1364 // element already outside our depth 1365 if (!item.attr && (item.type == KoXmlNode::ElementNode)) 1366 if (item.depth <= (unsigned)nodeDepth) 1367 break; 1368 1369 // attribute belongs to this node 1370 if (item.attr && (item.depth == (unsigned)nodeDepth)) { 1371 KoQName qname = packedDoc->qnameList[item.qnameIndex]; 1372 qname.nsURI = fixNamespace(qname.nsURI); 1373 QString value = item.value; 1374 QString prefix; 1375 1376 QString qName; // with prefix 1377 QString localName; // without prefix, i.e. local name 1378 1379 localName = qName = qname.name; 1380 int i = qName.indexOf(':'); 1381 if (i != -1) prefix = qName.left(i); 1382 if (i != -1) localName = qName.mid(i + 1); 1383 1384 if (packedDoc->processNamespace) { 1385 element.setAttributeNS(qname.nsURI, qName, value); 1386 element.setAttribute(localName, value); 1387 } else 1388 element.setAttribute(qname.name, value); 1389 } 1390 1391 // direct child of this node 1392 if (!item.attr && (item.depth == (unsigned)nodeDepth + 1)) { 1393 // add it recursively 1394 itemAsQDomNode(ownerDoc, packedDoc, i, element); 1395 } 1396 } 1397 return; 1398 } 1399 1400 // create the text node 1401 if (item.type == KoXmlNode::TextNode) { 1402 QString text = item.value; 1403 // FIXME: choose CDATA when the value contains special characters 1404 QDomText textNode = ownerDoc.createTextNode(text); 1405 if ( parentNode.isNull() ) { 1406 ownerDoc.appendChild( textNode ); 1407 } else { 1408 parentNode.appendChild( textNode ); 1409 } 1410 return; 1411 } 1412 1413 // nothing matches? strange... 1414 } 1415 1416 void KoXmlNodeData::asQDomNode(QDomDocument& ownerDoc) const 1417 { 1418 itemAsQDomNode(ownerDoc, packedDoc, nodeIndex); 1419 } 1420 1421 #endif 1422 1423 void KoXmlNodeData::dump() 1424 { 1425 printf("NodeData %p\n", (void*)this); 1426 1427 printf(" nodeIndex: %d\n", (int)nodeIndex); 1428 printf(" packedDoc: %p\n", (void*)packedDoc); 1429 1430 printf(" nodeType : %d\n", (int)nodeType); 1431 printf(" tagName: %s\n", qPrintable(tagName)); 1432 printf(" namespaceURI: %s\n", qPrintable(namespaceURI)); 1433 printf(" prefix: %s\n", qPrintable(prefix)); 1434 printf(" localName: %s\n", qPrintable(localName)); 1435 1436 printf(" parent : %p\n", (void*)parent); 1437 printf(" prev : %p\n", (void*)prev); 1438 printf(" next : %p\n", (void*)next); 1439 printf(" first : %p\n", (void*)first); 1440 printf(" last : %p\n", (void*)last); 1441 1442 printf(" refCount: %ld\n", refCount); 1443 1444 if (loaded) 1445 printf(" loaded: TRUE\n"); 1446 else 1447 printf(" loaded: FALSE\n"); 1448 } 1449 1450 1451 // ================================================================== 1452 // 1453 // KoXmlNodeData 1454 // 1455 // ================================================================== 1456 1457 class KoXmlDocumentData : public KoXmlNodeData 1458 { 1459 public: 1460 1461 KoXmlDocumentData(unsigned long initialRefCount = 1); 1462 ~KoXmlDocumentData(); 1463 1464 bool setContent(QXmlStreamReader *reader, 1465 QString* errorMsg = 0, int* errorLine = 0, int* errorColumn = 0); 1466 1467 KoXmlDocumentType dt; 1468 1469 bool emptyDocument :1; 1470 // to read the xml with or without spaces 1471 bool stripSpaces :1; 1472 }; 1473 1474 #define KOXMLDOCDATA(d) static_cast<KoXmlDocumentData*>(d) 1475 1476 1477 KoXmlDocumentData::KoXmlDocumentData(unsigned long initialRefCount) 1478 : KoXmlNodeData(initialRefCount) 1479 , emptyDocument(true) 1480 , stripSpaces(true) 1481 { 1482 } 1483 1484 KoXmlDocumentData::~KoXmlDocumentData() 1485 { 1486 } 1487 1488 bool KoXmlDocumentData::setContent(QXmlStreamReader* reader, QString* errorMsg, int* errorLine, int* errorColumn) 1489 { 1490 // sanity checks 1491 if (!reader) return false; 1492 1493 if (nodeType != KoXmlNode::DocumentNode) 1494 return false; 1495 1496 clear(); 1497 nodeType = KoXmlNode::DocumentNode; 1498 1499 packedDoc = new KoXmlPackedDocument; 1500 packedDoc->processNamespace = reader->namespaceProcessing(); 1501 1502 ParseError error = parseDocument(*reader, *packedDoc, stripSpaces); 1503 if (error.error) { 1504 // parsing error has occurred 1505 if (errorMsg) *errorMsg = error.errorMsg; 1506 if (errorLine) *errorLine = error.errorLine; 1507 if (errorColumn) *errorColumn = error.errorColumn; 1508 return false; 1509 } 1510 1511 // initially load 1512 loadChildren(); 1513 1514 KoXmlNodeData *typeData = new KoXmlNodeData(0); 1515 typeData->nodeType = KoXmlNode::DocumentTypeNode; 1516 typeData->tagName = packedDoc->docType; 1517 typeData->parent = this; 1518 dt = KoXmlDocumentType(typeData); 1519 1520 return true; 1521 } 1522 1523 // ================================================================== 1524 // 1525 // KoXmlNode 1526 // 1527 // ================================================================== 1528 1529 // Creates a null node 1530 KoXmlNode::KoXmlNode() 1531 { 1532 d = &KoXmlNodeData::null; 1533 d->ref(); 1534 } 1535 1536 // Destroys this node 1537 KoXmlNode::~KoXmlNode() 1538 { 1539 d->unref(); 1540 } 1541 1542 // Creates a copy of another node 1543 KoXmlNode::KoXmlNode(const KoXmlNode& node) 1544 { 1545 d = node.d; 1546 d->ref(); 1547 } 1548 1549 // Creates a node for specific implementation 1550 KoXmlNode::KoXmlNode(KoXmlNodeData* data) 1551 { 1552 d = data; 1553 data->ref(); 1554 } 1555 1556 // Creates a shallow copy of another node 1557 KoXmlNode& KoXmlNode::operator=(const KoXmlNode & node) 1558 { 1559 if (this != &node) { 1560 d->unref(); 1561 d = node.d; 1562 d->ref(); 1563 } 1564 return *this; 1565 } 1566 1567 // Note: two null nodes are always equal 1568 bool KoXmlNode::operator==(const KoXmlNode& node) const 1569 { 1570 if (isNull() && node.isNull()) return true; 1571 return(d == node.d); 1572 } 1573 1574 // Note: two null nodes are always equal 1575 bool KoXmlNode::operator!=(const KoXmlNode& node) const 1576 { 1577 if (isNull() && !node.isNull()) return true; 1578 if (!isNull() && node.isNull()) return true; 1579 if (isNull() && node.isNull()) return false; 1580 return(d != node.d); 1581 } 1582 1583 KoXmlNode::NodeType KoXmlNode::nodeType() const 1584 { 1585 return d->nodeType; 1586 } 1587 1588 bool KoXmlNode::isNull() const 1589 { 1590 return d->nodeType == NullNode; 1591 } 1592 1593 bool KoXmlNode::isElement() const 1594 { 1595 return d->nodeType == ElementNode; 1596 } 1597 1598 bool KoXmlNode::isText() const 1599 { 1600 return (d->nodeType == TextNode) || isCDATASection(); 1601 } 1602 1603 bool KoXmlNode::isCDATASection() const 1604 { 1605 return d->nodeType == CDATASectionNode; 1606 } 1607 1608 bool KoXmlNode::isDocument() const 1609 { 1610 return d->nodeType == DocumentNode; 1611 } 1612 1613 bool KoXmlNode::isDocumentType() const 1614 { 1615 return d->nodeType == DocumentTypeNode; 1616 } 1617 1618 void KoXmlNode::clear() 1619 { 1620 d->unref(); 1621 d = new KoXmlNodeData; 1622 } 1623 1624 QString KoXmlNode::nodeName() const 1625 { 1626 return d->nodeName(); 1627 } 1628 1629 QString KoXmlNode::prefix() const 1630 { 1631 return isElement() ? d->prefix : QString(); 1632 } 1633 1634 QString KoXmlNode::namespaceURI() const 1635 { 1636 return isElement() ? d->namespaceURI : QString(); 1637 } 1638 1639 QString KoXmlNode::localName() const 1640 { 1641 return isElement() ? d->localName : QString(); 1642 } 1643 1644 KoXmlDocument KoXmlNode::ownerDocument() const 1645 { 1646 KoXmlNodeData* node = d; 1647 while (node->parent) node = node->parent; 1648 1649 if (node->nodeType == DocumentNode) { 1650 return KoXmlDocument(static_cast<KoXmlDocumentData*>(node)); 1651 } 1652 return KoXmlDocument(); 1653 } 1654 1655 KoXmlNode KoXmlNode::parentNode() const 1656 { 1657 return d->parent ? KoXmlNode(d->parent) : KoXmlNode(); 1658 } 1659 1660 bool KoXmlNode::hasChildNodes() const 1661 { 1662 if (isText()) 1663 return false; 1664 1665 if (!d->loaded) 1666 d->loadChildren(); 1667 1668 return d->first != 0 ; 1669 } 1670 1671 int KoXmlNode::childNodesCount() const 1672 { 1673 if (isText()) 1674 return 0; 1675 1676 if (!d->loaded) 1677 d->loadChildren(); 1678 1679 KoXmlNodeData* node = d->first; 1680 int count = 0; 1681 while (node) { 1682 ++count; 1683 node = node->next; 1684 } 1685 1686 return count; 1687 } 1688 1689 QStringList KoXmlNode::attributeNames() const 1690 { 1691 if (!d->loaded) 1692 d->loadChildren(); 1693 1694 return d->attributeNames(); 1695 } 1696 1697 QList< QPair<QString, QString> > KoXmlNode::attributeFullNames() const 1698 { 1699 if (!d->loaded) 1700 d->loadChildren(); 1701 1702 return d->attributeFullNames(); 1703 } 1704 1705 KoXmlNode KoXmlNode::firstChild() const 1706 { 1707 if (!d->loaded) 1708 d->loadChildren(); 1709 return d->first ? KoXmlNode(d->first) : KoXmlNode(); 1710 } 1711 1712 KoXmlElement KoXmlNode::firstChildElement() const 1713 { 1714 KoXmlElement element; 1715 forEachElement (element, (*this)) { 1716 return element; 1717 } 1718 return KoXmlElement(); 1719 } 1720 1721 KoXmlNode KoXmlNode::lastChild() const 1722 { 1723 if (!d->loaded) 1724 d->loadChildren(); 1725 return d->last ? KoXmlNode(d->last) : KoXmlNode(); 1726 } 1727 1728 KoXmlNode KoXmlNode::nextSibling() const 1729 { 1730 return d->next ? KoXmlNode(d->next) : KoXmlNode(); 1731 } 1732 1733 KoXmlNode KoXmlNode::previousSibling() const 1734 { 1735 return d->prev ? KoXmlNode(d->prev) : KoXmlNode(); 1736 } 1737 1738 KoXmlNode KoXmlNode::namedItem(const QString& name) const 1739 { 1740 if (!d->loaded) 1741 d->loadChildren(); 1742 1743 for (KoXmlNodeData* node = d->first; node; node = node->next) { 1744 if (node->nodeName() == name) 1745 return KoXmlNode(node); 1746 } 1747 1748 // not found 1749 return KoXmlNode(); 1750 } 1751 1752 KoXmlNode KoXmlNode::namedItemNS(const QString& nsURI, const QString& name) const 1753 { 1754 if (!d->loaded) 1755 d->loadChildren(); 1756 1757 for (KoXmlNodeData* node = d->first; node; node = node->next) { 1758 if (node->nodeType == KoXmlNode::ElementNode 1759 && node->localName == name 1760 && node->namespaceURI == nsURI 1761 ) { 1762 return KoXmlNode(node); 1763 } 1764 } 1765 1766 // not found 1767 return KoXmlNode(); 1768 } 1769 1770 KoXmlNode KoXmlNode::namedItemNS(const QString& nsURI, const QString& name, KoXmlNamedItemType type) const 1771 { 1772 if (!d->loaded) 1773 d->loadChildren(); 1774 1775 for (KoXmlNodeData* node = d->first; node; node = node->next) { 1776 if (node->nodeType != KoXmlNode::ElementNode) 1777 continue; 1778 if (node->localName == name && node->namespaceURI == nsURI) { 1779 return KoXmlNode(node); 1780 } 1781 bool isPrelude = false; 1782 switch (type) { 1783 case KoXmlTextContentPrelude: 1784 isPrelude = 1785 (node->localName == "tracked-changes" && node->namespaceURI == KoXmlNS::text) || 1786 (node->localName == "variable-decls" && node->namespaceURI == KoXmlNS::text) || 1787 (node->localName == "user-field-decls" && node->namespaceURI == KoXmlNS::text) || 1788 (node->localName == "user-field-decl" && node->namespaceURI == KoXmlNS::text) || 1789 (node->localName == "sequence-decls" && node->namespaceURI == KoXmlNS::text) || 1790 (node->localName == "sequence-decl" && node->namespaceURI == KoXmlNS::text) || 1791 (node->localName == "dde-connection-decls" && node->namespaceURI == KoXmlNS::text) || 1792 (node->localName == "alphabetical-index-auto-mark-file" && node->namespaceURI == KoXmlNS::text) || 1793 (node->localName == "forms" && node->namespaceURI == KoXmlNS::office); 1794 break; 1795 } 1796 if (!isPrelude) { 1797 return KoXmlNode(); // no TextContentPrelude means it follows TextContentMain, so stop here. 1798 } 1799 } 1800 1801 // not found 1802 return KoXmlNode(); 1803 } 1804 1805 KoXmlElement KoXmlNode::toElement() const 1806 { 1807 return isElement() ? KoXmlElement(d) : KoXmlElement(); 1808 } 1809 1810 KoXmlText KoXmlNode::toText() const 1811 { 1812 return isText() ? KoXmlText(d) : KoXmlText(); 1813 } 1814 1815 KoXmlCDATASection KoXmlNode::toCDATASection() const 1816 { 1817 return isCDATASection() ? KoXmlCDATASection(d) : KoXmlCDATASection(); 1818 } 1819 1820 KoXmlDocument KoXmlNode::toDocument() const 1821 { 1822 if (isDocument()) { 1823 return KoXmlDocument(static_cast<KoXmlDocumentData*>(d)); 1824 } 1825 return KoXmlDocument(); 1826 } 1827 1828 void KoXmlNode::load(int depth) 1829 { 1830 d->loadChildren(depth); 1831 } 1832 1833 void KoXmlNode::unload() 1834 { 1835 d->unloadChildren(); 1836 } 1837 1838 void KoXmlNode::asQDomNode(QDomDocument& ownerDoc) const 1839 { 1840 Q_ASSERT(!isDocument()); 1841 d->asQDomNode(ownerDoc); 1842 } 1843 1844 // ================================================================== 1845 // 1846 // KoXmlElement 1847 // 1848 // ================================================================== 1849 1850 // Creates an empty element 1851 KoXmlElement::KoXmlElement(): KoXmlNode() 1852 { 1853 } 1854 1855 KoXmlElement::~KoXmlElement() 1856 { 1857 } 1858 1859 // Creates a shallow copy of another element 1860 KoXmlElement::KoXmlElement(const KoXmlElement& element): KoXmlNode(element.d) 1861 { 1862 } 1863 1864 KoXmlElement::KoXmlElement(KoXmlNodeData* data): KoXmlNode(data) 1865 { 1866 } 1867 1868 // Copies another element 1869 KoXmlElement& KoXmlElement::operator=(const KoXmlElement & element) 1870 { 1871 KoXmlNode::operator=(element); 1872 return *this; 1873 } 1874 1875 bool KoXmlElement::operator== (const KoXmlElement& element) const 1876 { 1877 if (isNull() || element.isNull()) return false; 1878 return (d == element.d); 1879 } 1880 1881 bool KoXmlElement::operator!= (const KoXmlElement& element) const 1882 { 1883 if (isNull() && element.isNull()) return false; 1884 if (isNull() || element.isNull()) return true; 1885 return (d != element.d); 1886 } 1887 1888 QString KoXmlElement::tagName() const 1889 { 1890 return isElement() ? d->tagName : QString(); 1891 } 1892 1893 QString KoXmlElement::text() const 1894 { 1895 return d->text(); 1896 } 1897 1898 QString KoXmlElement::attribute(const QString& name) const 1899 { 1900 if (!isElement()) 1901 return QString(); 1902 1903 if (!d->loaded) 1904 d->loadChildren(); 1905 1906 return d->attribute(name, QString()); 1907 } 1908 1909 QString KoXmlElement::attribute(const QString& name, 1910 const QString& defaultValue) const 1911 { 1912 if (!isElement()) 1913 return defaultValue; 1914 1915 if (!d->loaded) 1916 d->loadChildren(); 1917 1918 return d->attribute(name, defaultValue); 1919 } 1920 1921 QString KoXmlElement::attributeNS(const QString& namespaceURI, 1922 const QString& localName, const QString& defaultValue) const 1923 { 1924 if (!isElement()) 1925 return defaultValue; 1926 1927 if (!d->loaded) 1928 d->loadChildren(); 1929 1930 KoXmlStringPair key(namespaceURI, localName); 1931 return d->attrNS.value(key, defaultValue); 1932 1933 // return d->attributeNS( namespaceURI, localName, defaultValue ); 1934 } 1935 1936 bool KoXmlElement::hasAttribute(const QString& name) const 1937 { 1938 if (!d->loaded) 1939 d->loadChildren(); 1940 1941 return isElement() ? d->hasAttribute(name) : false; 1942 } 1943 1944 bool KoXmlElement::hasAttributeNS(const QString& namespaceURI, 1945 const QString& localName) const 1946 { 1947 if (!d->loaded) 1948 d->loadChildren(); 1949 1950 return isElement() ? d->hasAttributeNS(namespaceURI, localName) : false; 1951 } 1952 1953 // ================================================================== 1954 // 1955 // KoXmlText 1956 // 1957 // ================================================================== 1958 1959 KoXmlText::KoXmlText(): KoXmlNode() 1960 { 1961 } 1962 1963 KoXmlText::~KoXmlText() 1964 { 1965 } 1966 1967 KoXmlText::KoXmlText(const KoXmlText& text): KoXmlNode(text.d) 1968 { 1969 } 1970 1971 KoXmlText::KoXmlText(KoXmlNodeData* data): KoXmlNode(data) 1972 { 1973 } 1974 1975 bool KoXmlText::isText() const 1976 { 1977 return true; 1978 } 1979 1980 QString KoXmlText::data() const 1981 { 1982 return d->data(); 1983 } 1984 1985 KoXmlText& KoXmlText::operator=(const KoXmlText & element) 1986 { 1987 KoXmlNode::operator=(element); 1988 return *this; 1989 } 1990 1991 // ================================================================== 1992 // 1993 // KoXmlCDATASection 1994 // 1995 // ================================================================== 1996 1997 KoXmlCDATASection::KoXmlCDATASection(): KoXmlText() 1998 { 1999 } 2000 2001 KoXmlCDATASection::KoXmlCDATASection(const KoXmlCDATASection& cdata) 2002 : KoXmlText(cdata) 2003 { 2004 } 2005 2006 KoXmlCDATASection::~KoXmlCDATASection() 2007 { 2008 } 2009 2010 KoXmlCDATASection::KoXmlCDATASection(KoXmlNodeData* cdata): 2011 KoXmlText(cdata) 2012 { 2013 } 2014 2015 bool KoXmlCDATASection::isCDATASection() const 2016 { 2017 return true; 2018 } 2019 2020 KoXmlCDATASection& KoXmlCDATASection::operator=(const KoXmlCDATASection & cdata) 2021 { 2022 KoXmlNode::operator=(cdata); 2023 return *this; 2024 } 2025 2026 // ================================================================== 2027 // 2028 // KoXmlDocumentType 2029 // 2030 // ================================================================== 2031 2032 KoXmlDocumentType::KoXmlDocumentType(): KoXmlNode() 2033 { 2034 } 2035 2036 KoXmlDocumentType::~KoXmlDocumentType() 2037 { 2038 } 2039 2040 KoXmlDocumentType::KoXmlDocumentType(const KoXmlDocumentType& dt): 2041 KoXmlNode(dt.d) 2042 { 2043 } 2044 2045 QString KoXmlDocumentType::name() const 2046 { 2047 return nodeName(); 2048 } 2049 2050 KoXmlDocumentType::KoXmlDocumentType(KoXmlNodeData* dt): KoXmlNode(dt) 2051 { 2052 } 2053 2054 KoXmlDocumentType& KoXmlDocumentType::operator=(const KoXmlDocumentType & dt) 2055 { 2056 KoXmlNode::operator=(dt); 2057 return *this; 2058 } 2059 2060 // ================================================================== 2061 // 2062 // KoXmlDocument 2063 // 2064 // ================================================================== 2065 2066 KoXmlDocument::KoXmlDocument(bool stripSpaces): KoXmlNode(new KoXmlDocumentData(0)) 2067 { 2068 KOXMLDOCDATA(d)->emptyDocument = false; 2069 KOXMLDOCDATA(d)->stripSpaces = stripSpaces; 2070 } 2071 2072 KoXmlDocument::~KoXmlDocument() 2073 { 2074 } 2075 2076 KoXmlDocument::KoXmlDocument(KoXmlDocumentData* data): KoXmlNode(data) 2077 { 2078 KOXMLDOCDATA(d)->emptyDocument = true; 2079 } 2080 2081 // Creates a copy of another document 2082 KoXmlDocument::KoXmlDocument(const KoXmlDocument& doc): KoXmlNode(doc.d) 2083 { 2084 } 2085 2086 // Creates a shallow copy of another document 2087 KoXmlDocument& KoXmlDocument::operator=(const KoXmlDocument & doc) 2088 { 2089 KoXmlNode::operator=(doc); 2090 return *this; 2091 } 2092 2093 // Checks if this document and doc are equals 2094 bool KoXmlDocument::operator==(const KoXmlDocument& doc) const 2095 { 2096 return(d == doc.d); 2097 } 2098 2099 // Checks if this document and doc are not equals 2100 bool KoXmlDocument::operator!=(const KoXmlDocument& doc) const 2101 { 2102 return(d != doc.d); 2103 } 2104 2105 KoXmlElement KoXmlDocument::documentElement() const 2106 { 2107 if (!d->loaded) 2108 d->loadChildren(); 2109 2110 for (KoXmlNodeData* node = d->first; node; node = node->next) { 2111 if (node->nodeType == KoXmlNode::ElementNode) { 2112 return KoXmlElement(node); 2113 } 2114 } 2115 2116 return KoXmlElement(); 2117 } 2118 2119 KoXmlDocumentType KoXmlDocument::doctype() const 2120 { 2121 return KOXMLDOCDATA(d)->dt; 2122 } 2123 2124 QString KoXmlDocument::nodeName() const 2125 { 2126 return (KOXMLDOCDATA(d)->emptyDocument) ? QString::fromLatin1("#document") : QString(); 2127 } 2128 2129 void KoXmlDocument::clear() 2130 { 2131 d->unref(); 2132 KoXmlDocumentData *dat = new KoXmlDocumentData; 2133 dat->emptyDocument = false; 2134 d = dat; 2135 } 2136 2137 namespace { 2138 /* Use an entity resolver that ignores undefined entities and simply 2139 returns an empty string for them. 2140 */ 2141 class DumbEntityResolver : public QXmlStreamEntityResolver { 2142 public: 2143 QString resolveUndeclaredEntity ( const QString &) override { return ""; } 2144 }; 2145 2146 } 2147 2148 bool KoXmlDocument::setContent(QXmlStreamReader *reader, 2149 QString* errorMsg, int* errorLine, int* errorColumn) 2150 { 2151 if (d->nodeType != KoXmlNode::DocumentNode) { 2152 const bool stripSpaces = KOXMLDOCDATA(d)->stripSpaces; 2153 d->unref(); 2154 KoXmlDocumentData *dat = new KoXmlDocumentData; 2155 dat->nodeType = KoXmlNode::DocumentNode; 2156 dat->stripSpaces = stripSpaces; 2157 d = dat; 2158 } 2159 2160 const bool result = KOXMLDOCDATA(d)->setContent(reader, errorMsg, errorLine, errorColumn); 2161 2162 return result; 2163 } 2164 2165 // no namespace processing 2166 bool KoXmlDocument::setContent(QIODevice* device, QString* errorMsg, 2167 int* errorLine, int* errorColumn) 2168 { 2169 return setContent(device, false, errorMsg, errorLine, errorColumn); 2170 } 2171 2172 bool KoXmlDocument::setContent(QIODevice* device, bool namespaceProcessing, 2173 QString* errorMsg, int* errorLine, int* errorColumn) 2174 { 2175 if (d->nodeType != KoXmlNode::DocumentNode) { 2176 const bool stripSpaces = KOXMLDOCDATA(d)->stripSpaces; 2177 d->unref(); 2178 KoXmlDocumentData *dat = new KoXmlDocumentData; 2179 dat->nodeType = KoXmlNode::DocumentNode; 2180 dat->stripSpaces = stripSpaces; 2181 d = dat; 2182 } 2183 2184 if (!device->isOpen()) device->open(QIODevice::ReadOnly); 2185 QXmlStreamReader reader(device); 2186 reader.setNamespaceProcessing(namespaceProcessing); 2187 DumbEntityResolver entityResolver; 2188 reader.setEntityResolver(&entityResolver); 2189 2190 const bool result = KOXMLDOCDATA(d)->setContent(&reader, errorMsg, errorLine, errorColumn); 2191 2192 return result; 2193 } 2194 2195 bool KoXmlDocument::setContent(const QByteArray& text, bool namespaceProcessing, 2196 QString *errorMsg, int *errorLine, int *errorColumn) 2197 { 2198 QBuffer buffer; 2199 buffer.setData(text); 2200 return setContent(&buffer, namespaceProcessing, errorMsg, errorLine, errorColumn); 2201 } 2202 2203 bool KoXmlDocument::setContent(const QString& text, bool namespaceProcessing, 2204 QString *errorMsg, int *errorLine, int *errorColumn) 2205 { 2206 if (d->nodeType != KoXmlNode::DocumentNode) { 2207 const bool stripSpaces = KOXMLDOCDATA(d)->stripSpaces; 2208 d->unref(); 2209 KoXmlDocumentData *dat = new KoXmlDocumentData; 2210 dat->nodeType = KoXmlNode::DocumentNode; 2211 dat->stripSpaces = stripSpaces; 2212 d = dat; 2213 } 2214 2215 QXmlStreamReader reader(text); 2216 reader.setNamespaceProcessing(namespaceProcessing); 2217 DumbEntityResolver entityResolver; 2218 reader.setEntityResolver(&entityResolver); 2219 2220 const bool result = KOXMLDOCDATA(d)->setContent(&reader, errorMsg, errorLine, errorColumn); 2221 2222 return result; 2223 } 2224 2225 bool KoXmlDocument::setContent(const QString& text, 2226 QString *errorMsg, int *errorLine, int *errorColumn) 2227 { 2228 return setContent(text, false, errorMsg, errorLine, errorColumn); 2229 } 2230 2231 void KoXmlDocument::setWhitespaceStripping(bool stripSpaces) 2232 { 2233 KOXMLDOCDATA(d)->stripSpaces = stripSpaces; 2234 } 2235 2236 2237 #endif 2238 2239 // ================================================================== 2240 // 2241 // functions in KoXml namespace 2242 // 2243 // ================================================================== 2244 2245 KoXmlElement KoXml::namedItemNS(const KoXmlNode& node, const QString& nsURI, 2246 const QString& localName) 2247 { 2248 #ifdef KOXML_USE_QDOM 2249 // David's solution for namedItemNS, only for QDom stuff 2250 KoXmlNode n = node.firstChild(); 2251 for (; !n.isNull(); n = n.nextSibling()) { 2252 if (n.isElement() && n.localName() == localName && 2253 n.namespaceURI() == nsURI) 2254 return n.toElement(); 2255 } 2256 return KoXmlElement(); 2257 #else 2258 return node.namedItemNS(nsURI, localName).toElement(); 2259 #endif 2260 } 2261 2262 KoXmlElement KoXml::namedItemNS(const KoXmlNode& node, const QString& nsURI, 2263 const QString& localName, KoXmlNamedItemType type) 2264 { 2265 #ifdef KOXML_USE_QDOM 2266 Q_ASSERT(false); 2267 return namedItemNS(node, nsURI, localName); 2268 #else 2269 return node.namedItemNS(nsURI, localName, type).toElement(); 2270 #endif 2271 } 2272 2273 void KoXml::load(KoXmlNode& node, int depth) 2274 { 2275 #ifdef KOXML_USE_QDOM 2276 // do nothing, QDom has no on-demand loading 2277 Q_UNUSED(node); 2278 Q_UNUSED(depth); 2279 #else 2280 node.load(depth); 2281 #endif 2282 } 2283 2284 2285 void KoXml::unload(KoXmlNode& node) 2286 { 2287 #ifdef KOXML_USE_QDOM 2288 // do nothing, QDom has no on-demand unloading 2289 Q_UNUSED(node); 2290 #else 2291 node.unload(); 2292 #endif 2293 } 2294 2295 int KoXml::childNodesCount(const KoXmlNode& node) 2296 { 2297 #ifdef KOXML_USE_QDOM 2298 return node.childNodes().count(); 2299 #else 2300 // compatibility function, because no need to implement 2301 // a class like QDomNodeList 2302 return node.childNodesCount(); 2303 #endif 2304 } 2305 2306 QStringList KoXml::attributeNames(const KoXmlNode& node) 2307 { 2308 #ifdef KOXML_USE_QDOM 2309 QStringList result; 2310 2311 QDomNamedNodeMap attrMap = node.attributes(); 2312 for (int i = 0; i < attrMap.count(); ++i) 2313 result += attrMap.item(i).toAttr().name(); 2314 2315 return result; 2316 #else 2317 // compatibility function, because no need to implement 2318 // a class like QDomNamedNodeMap 2319 return node.attributeNames(); 2320 #endif 2321 } 2322 2323 void KoXml::asQDomNode(QDomDocument& ownerDoc, const KoXmlNode& node) 2324 { 2325 Q_ASSERT(!node.isDocument()); 2326 #ifdef KOXML_USE_QDOM 2327 ownerDoc.appendChild(ownerDoc.importNode(node)); 2328 #else 2329 node.asQDomNode(ownerDoc); 2330 #endif 2331 } 2332 2333 void KoXml::asQDomElement(QDomDocument &ownerDoc, const KoXmlElement& element) 2334 { 2335 KoXml::asQDomNode(ownerDoc, element); 2336 } 2337 2338 QDomDocument KoXml::asQDomDocument(const KoXmlDocument& document) 2339 { 2340 #ifdef KOXML_USE_QDOM 2341 return document; 2342 #else 2343 QDomDocument qdoc( document.nodeName() ); 2344 if ( document.hasChildNodes() ) { 2345 for ( KoXmlNode n = document.firstChild(); ! n.isNull(); n = n.nextSibling() ) { 2346 KoXml::asQDomNode(qdoc, n); 2347 } 2348 } 2349 return qdoc; 2350 #endif 2351 } 2352 2353 bool KoXml::setDocument(KoXmlDocument& doc, QIODevice* device, 2354 bool namespaceProcessing, QString* errorMsg, int* errorLine, 2355 int* errorColumn) 2356 { 2357 QXmlStreamReader reader(device); 2358 reader.setNamespaceProcessing(namespaceProcessing); 2359 bool result = doc.setContent(&reader, errorMsg, errorLine, errorColumn); 2360 return result; 2361 }