Syndication

atom/parser.cpp
1/*
2 This file is part of the syndication library
3 SPDX-FileCopyrightText: 2006 Frank Osterfeld <osterfeld@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "parser.h"
9#include "constants.h"
10#include "content.h"
11#include "document.h"
12
13#include <documentsource.h>
14
15#include <QDomAttr>
16#include <QDomDocument>
17#include <QDomElement>
18#include <QDomNamedNodeMap>
19#include <QDomNode>
20#include <QDomNodeList>
21
22#include <QHash>
23#include <QString>
24
25namespace Syndication
26{
27namespace Atom
28{
29class SYNDICATION_NO_EXPORT Parser::ParserPrivate
30{
31public:
32 static QDomDocument convertAtom0_3(const QDomDocument &document);
33 static QDomNode convertNode(QDomDocument &doc, const QDomNode &node, const QHash<QString, QString> &nameMapper);
34};
35
36bool Parser::accept(const Syndication::DocumentSource &source) const
37{
38 QDomElement root = source.asDomDocument().documentElement();
39 return !root.isNull() && (root.namespaceURI() == atom1Namespace() || root.namespaceURI() == atom0_3Namespace());
40}
41
42Syndication::SpecificDocumentPtr Parser::parse(const Syndication::DocumentSource &source) const
43{
44 QDomDocument doc = source.asDomDocument();
45
46 if (doc.isNull()) {
47 // if this is not atom, return an invalid feed document
48 return FeedDocumentPtr(new FeedDocument());
49 }
50
51 QDomElement feed = doc.namedItem(QStringLiteral("feed")).toElement();
52
53 bool feedValid = !feed.isNull();
54
55 if (feedValid //
56 && feed.attribute(QStringLiteral("version")) == QLatin1String("0.3")) {
57 doc = ParserPrivate::convertAtom0_3(doc);
58 feed = doc.namedItem(QStringLiteral("feed")).toElement();
59 }
60
61 feedValid = !feed.isNull() && feed.namespaceURI() == atom1Namespace();
62
63 if (feedValid) {
64 return FeedDocumentPtr(new FeedDocument(feed));
65 }
66
67 QDomElement entry = doc.namedItem(QStringLiteral("entry")).toElement();
68 bool entryValid = !entry.isNull() && entry.namespaceURI() == atom1Namespace();
69
70 if (entryValid) {
71 return EntryDocumentPtr(new EntryDocument(feed));
72 }
73
74 // if this is not atom, return an invalid feed document
75 return FeedDocumentPtr(new FeedDocument());
76}
77
78QString Parser::format() const
79{
80 return QStringLiteral("atom");
81}
82
83QDomNode Parser::ParserPrivate::convertNode(QDomDocument &doc, const QDomNode &node, const QHash<QString, QString> &nameMapper)
84{
85 if (!node.isElement()) {
86 return node.cloneNode(true);
87 }
88
89 bool isAtom03Element = node.namespaceURI() == atom0_3Namespace();
90 QDomElement oldEl = node.toElement();
91
92 // use new namespace
93 QString newNS = isAtom03Element ? atom1Namespace() : node.namespaceURI();
94
95 QString newName = node.localName();
96
97 // rename tags that are listed in the nameMapper
98 if (isAtom03Element && nameMapper.contains(node.localName())) {
99 newName = nameMapper[node.localName()];
100 }
101
102 QDomElement newEl = doc.createElementNS(newNS, newName);
103
104 QDomNamedNodeMap attributes = oldEl.attributes();
105
106 // copy over attributes
107 const int numberOfAttributes(attributes.count());
108 for (int i = 0; i < numberOfAttributes; ++i) {
109 const QDomAttr attr = attributes.item(i).toAttr();
110 if (attr.namespaceURI().isEmpty()) {
111 newEl.setAttribute(attr.name(), attr.value());
112 } else {
113 newEl.setAttributeNS(attr.namespaceURI(), attr.name(), attr.value());
114 }
115 }
116
117 /* clang-format off */
118 bool isTextConstruct = newNS == atom1Namespace()
119 && (newName == QLatin1String("title")
120 || newName == QLatin1String("rights")
121 || newName == QLatin1String("subtitle")
122 || newName == QLatin1String("summary"));
123 /* clang-format on */
124
125 // for atom text constructs, map to new type schema (which only allows text, type, xhtml)
126
127 if (isTextConstruct) {
128 QString oldType = newEl.attribute(QStringLiteral("type"), QStringLiteral("text/plain"));
129 QString newType;
130
131 Content::Format format = Content::mapTypeToFormat(oldType);
132 switch (format) {
133 case Content::XML:
134 newType = QStringLiteral("xhtml");
135 break;
136 case Content::EscapedHTML:
137 newType = QStringLiteral("html");
138 break;
139 case Content::PlainText:
140 case Content::Binary:
141 default:
142 newType = QStringLiteral("text");
143 }
144
145 newEl.setAttribute(QStringLiteral("type"), newType);
146 } else {
147 // for generator, rename the "url" attribute to "uri"
148
149 bool isGenerator = newNS == atom1Namespace() && newName == QLatin1String("generator");
150 if (isGenerator && newEl.hasAttribute(QStringLiteral("url"))) {
151 newEl.setAttribute(QStringLiteral("uri"), newEl.attribute(QStringLiteral("url")));
152 }
153 }
154
155 // process child nodes recursively and append them
156 QDomNodeList children = node.childNodes();
157 for (int i = 0; i < children.count(); ++i) {
158 newEl.appendChild(convertNode(doc, children.item(i), nameMapper));
159 }
160
161 return newEl;
162}
163
164QDomDocument Parser::ParserPrivate::convertAtom0_3(const QDomDocument &doc03)
165{
166 QDomDocument doc = doc03.cloneNode(false).toDocument();
167
168 // these are the tags that were renamed in 1.0
169 QHash<QString, QString> nameMapper;
170 nameMapper.insert(QStringLiteral("issued"), QStringLiteral("published"));
171 nameMapper.insert(QStringLiteral("modified"), QStringLiteral("updated"));
172 nameMapper.insert(QStringLiteral("url"), QStringLiteral("uri"));
173 nameMapper.insert(QStringLiteral("copyright"), QStringLiteral("rights"));
174 nameMapper.insert(QStringLiteral("tagline"), QStringLiteral("subtitle"));
175
176 const QDomNodeList children = doc03.childNodes();
177
178 for (int i = 0; i < children.count(); ++i) {
179 doc.appendChild(convertNode(doc, children.item(i), nameMapper));
180 }
181
182 return doc;
183}
184
185Parser::Parser()
186 : d(nullptr)
187{
188 Q_UNUSED(d) // silence -Wunused-private-field
189}
190
191Parser::~Parser() = default;
192
193Parser::Parser(const Parser &other)
194 : AbstractParser(other)
195 , d(nullptr)
196{
197}
198Parser &Parser::operator=(const Parser & /*other*/)
199{
200 return *this;
201}
202
203} // namespace Atom
204} // namespace Syndication
QString atom0_3Namespace()
namespace used by Atom 0.3 elements
QString atom1Namespace()
namespace used by Atom 1.0 elements
QString name() const const
QString value() const const
QDomElement createElementNS(const QString &nsURI, const QString &qName)
QDomElement documentElement() const const
QString attribute(const QString &name, const QString &defValue) const const
QDomNamedNodeMap attributes() const const
bool hasAttribute(const QString &name) const const
void setAttribute(const QString &name, const QString &value)
void setAttributeNS(const QString nsURI, const QString &qName, const QString &value)
int count() const const
QDomNode item(int index) const const
QDomNode appendChild(const QDomNode &newChild)
QDomNodeList childNodes() const const
QDomNode cloneNode(bool deep) const const
bool isElement() const const
bool isNull() const const
QString localName() const const
QDomNode namedItem(const QString &name) const const
QString namespaceURI() const const
QDomAttr toAttr() const const
QDomDocument toDocument() const const
QDomElement toElement() const const
int count() const const
QDomNode item(int index) const const
bool contains(const Key &key) const const
QHash::iterator insert(const Key &key, const T &value)
bool isEmpty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sun Feb 25 2024 18:39:48 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.