1 /*
2 * %kadu copyright begin%
3 * Copyright 2012, 2013, 2014 Rafał Przemysław Malinowski (rafal.przemyslaw.malinowski@gmail.com)
4 * %kadu copyright end%
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License as
8 * published by the Free Software Foundation; either version 2 of
9 * the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 */
19
20 #include "dom-processor.h"
21
22 #include "dom/dom-visitor.h"
23
24 #include <QtXml/QDomNode>
25
DomProcessor(QDomDocument & domDocument)26 DomProcessor::DomProcessor(QDomDocument &domDocument) :
27 m_domDocument{domDocument}
28 {
29 }
30
acceptNode(const DomVisitor * visitor,QDomNode node)31 QDomNode DomProcessor::acceptNode(const DomVisitor *visitor, QDomNode node)
32 {
33 switch (node.nodeType())
34 {
35 case QDomNode::TextNode:
36 node = visitor->visit(node.toText());
37 break;
38 case QDomNode::ElementNode:
39 node = visitor->beginVisit(node.toElement());
40 break;
41 default:
42 break;
43 }
44
45 auto childNode = node.firstChild();
46 while (!childNode.isNull())
47 childNode = acceptNode(visitor, childNode);
48
49 switch (node.nodeType())
50 {
51 case QDomNode::ElementNode:
52 node = visitor->endVisit(node.toElement());
53 break;
54 default:
55 node = node.nextSibling();
56 break;
57 }
58
59 return node;
60 }
61
accept(const DomVisitor * visitor)62 void DomProcessor::accept(const DomVisitor *visitor)
63 {
64 Q_ASSERT(visitor);
65
66 acceptNode(visitor, m_domDocument.documentElement());
67 }
68
toDomDocument(const QString & xml)69 QDomDocument toDomDocument(const QString &xml)
70 {
71 auto domDocument = QDomDocument{};
72 auto preparedXml = QString{xml}.replace("<", "-<").replace(">", ">-");
73 // force content to be valid HTML with only one root
74 if (domDocument.setContent(QString(R"(<div>%1</div>)").arg(preparedXml)))
75 return domDocument;
76
77 throw invalid_xml{};
78 }
79
toString(const QDomDocument & domDocument)80 QString toString(const QDomDocument &domDocument)
81 {
82 if (domDocument.documentElement().childNodes().isEmpty())
83 return QString();
84
85 auto result = domDocument.toString(-1).trimmed();
86 // remove <div></div>
87 Q_ASSERT(result.startsWith(QStringLiteral(R"(<div>)")));
88 Q_ASSERT(result.endsWith(QStringLiteral("</div>")));
89 result = result.mid(static_cast<int>(qstrlen(R"(<div>)")), result.length() - static_cast<int>(qstrlen(R"(<div></div>)")));
90 return result.replace("-<", "<").replace(">-", ">");
91 }
92
processDom(const QString & xml,const DomVisitor & domVisitor)93 QString processDom(const QString &xml, const DomVisitor &domVisitor)
94 {
95 auto domDocument = toDomDocument(xml);
96 auto domProcessor = DomProcessor{domDocument};
97 domProcessor.accept(&domVisitor);
98 return toString(domDocument);
99 }
100