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
37{
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
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
132 switch (format) {
133 case Content::XML:
134 newType = QStringLiteral("xhtml");
135 break;
137 newType = QStringLiteral("html");
138 break;
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
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
Interface for all parsers.
static Format mapTypeToFormat(const QString &type, const QString &src=QString())
maps a mimetype to Format enum according to the Atom 1.0 specification
Definition content.cpp:79
Format
format of the content.
Definition content.h:36
@ XML
the content is embedded XML
Definition content.h:42
@ EscapedHTML
the content is escaped HTML, (i.e., "<", ">" etc.
Definition content.h:40
@ PlainText
the content is plain text (i.e.
Definition content.h:37
@ Binary
the content is base64-encoded binary content
Definition content.h:43
An Atom 1.0 Entry Document, containing a single Atom entry outside of the context of a feed.
An Atom 1.0 Feed Document, containing metadata describing the feed and a number of entries.
parser implementation for Atom 1.0 and 0.3.
Definition atom/parser.h:32
Parser()
default constructor
~Parser() override
destructor
QString format() const override
returns the format string for this parser implementation, which is "atom"
Syndication::SpecificDocumentPtr parse(const Syndication::DocumentSource &source) const override
parses either an EntryDocument or a FeedDocument from a document source.
bool accept(const Syndication::DocumentSource &source) const override
returns whether the source looks like an Atom 1.0 or 0.3 document, by checking the root element.
Represents the source of a syndication document, as read from the downloaded file.
QDomDocument asDomDocument() const
Returns the feed source as DOM document.
Atom parser and model classes, representing Atom 1.0 documents (Atom 0.3 documents are converted by t...
Definition atom.h:30
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
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
iterator insert(const Key &key, const T &value)
bool isEmpty() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Jan 24 2025 11:48:38 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.