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