File indexing completed on 2024-09-01 03:51:05
0001 /* 0002 This file is part of the syndication library 0003 SPDX-FileCopyrightText: 2006 Frank Osterfeld <osterfeld@kde.org> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "parser.h" 0009 #include "document.h" 0010 #include "model.h" 0011 #include "modelmaker.h" 0012 #include "property.h" 0013 #include "rdfvocab.h" 0014 #include "resource.h" 0015 #include "rssvocab.h" 0016 #include "statement.h" 0017 0018 #include <documentsource.h> 0019 0020 #include <QDomDocument> 0021 #include <QDomNodeList> 0022 #include <QHash> 0023 #include <QList> 0024 #include <QMap> 0025 #include <QString> 0026 #include <QStringList> 0027 0028 namespace Syndication 0029 { 0030 namespace RDF 0031 { 0032 class SYNDICATION_NO_EXPORT Parser::ParserPrivate 0033 { 0034 public: 0035 QDomDocument addEnumeration(const QDomDocument &doc); 0036 void map09to10(Model model); 0037 void addSequenceFor09(Model model); 0038 0039 QString strInternalNs; 0040 QString strItemIndex; 0041 }; 0042 0043 bool Parser::accept(const DocumentSource &source) const 0044 { 0045 QDomDocument doc = source.asDomDocument(); 0046 0047 if (doc.isNull()) { 0048 return false; 0049 } 0050 QDomElement root = doc.documentElement(); 0051 0052 if (!root.isElement()) { 0053 return false; 0054 } 0055 0056 return root.namespaceURI() == RDFVocab::self()->namespaceURI(); 0057 } 0058 0059 SpecificDocumentPtr Parser::parse(const DocumentSource &source) const 0060 { 0061 QDomDocument doc = source.asDomDocument(); 0062 0063 if (doc.isNull()) { 0064 return Syndication::SpecificDocumentPtr(new Document()); 0065 } 0066 0067 doc = d->addEnumeration(doc); 0068 0069 ModelMaker maker; 0070 Model model = maker.createFromXML(doc); 0071 0072 bool is09 = !model.resourcesWithType(RSS09Vocab::self()->channel()).isEmpty(); 0073 0074 if (is09) { 0075 d->map09to10(model); 0076 d->addSequenceFor09(model); 0077 } 0078 0079 QList<ResourcePtr> channels = model.resourcesWithType(RSSVocab::self()->channel()); 0080 0081 if (channels.isEmpty()) { 0082 return Syndication::SpecificDocumentPtr(new Document()); 0083 } 0084 0085 return DocumentPtr(new Document(*(channels.begin()))); 0086 } 0087 0088 QDomDocument Parser::ParserPrivate::addEnumeration(const QDomDocument &docp) 0089 { 0090 QDomDocument doc(docp); 0091 0092 const QDomNodeList list = doc.elementsByTagNameNS(RSS09Vocab::self()->namespaceURI(), QStringLiteral("item")); 0093 0094 for (int i = 0; i < list.size(); ++i) { 0095 QDomElement item = list.item(i).toElement(); 0096 if (!item.isNull()) { 0097 QDomElement ie = doc.createElementNS(strInternalNs, strItemIndex); 0098 item.appendChild(ie); 0099 ie.appendChild(doc.createTextNode(QString::number(i))); 0100 } 0101 } 0102 0103 return doc; 0104 } 0105 0106 void Parser::ParserPrivate::map09to10(Model model) 0107 { 0108 QHash<QString, PropertyPtr> hash; 0109 0110 hash.insert(RSS09Vocab::self()->title()->uri(), RSSVocab::self()->title()); 0111 hash.insert(RSS09Vocab::self()->description()->uri(), RSSVocab::self()->description()); 0112 hash.insert(RSS09Vocab::self()->link()->uri(), RSSVocab::self()->link()); 0113 hash.insert(RSS09Vocab::self()->name()->uri(), RSSVocab::self()->name()); 0114 hash.insert(RSS09Vocab::self()->url()->uri(), RSSVocab::self()->url()); 0115 hash.insert(RSS09Vocab::self()->image()->uri(), RSSVocab::self()->image()); 0116 hash.insert(RSS09Vocab::self()->textinput()->uri(), RSSVocab::self()->textinput()); 0117 0118 QStringList uris09 = RSS09Vocab::self()->properties(); 0119 0120 // map statement predicates to RSS 1.0 0121 0122 const QList<StatementPtr> &statements = model.statements(); 0123 0124 for (const auto &stmt : statements) { 0125 const QString predUri = stmt->predicate()->uri(); 0126 if (uris09.contains(predUri)) { 0127 model.addStatement(stmt->subject(), hash[predUri], stmt->object()); 0128 } 0129 } 0130 // map channel type 0131 QList<ResourcePtr> channels = model.resourcesWithType(RSS09Vocab::self()->channel()); 0132 0133 ResourcePtr channel; 0134 0135 if (!channels.isEmpty()) { 0136 channel = *(channels.begin()); 0137 0138 model.removeStatement(channel, RDFVocab::self()->type(), RSS09Vocab::self()->channel()); 0139 model.addStatement(channel, RDFVocab::self()->type(), RSSVocab::self()->channel()); 0140 } 0141 } 0142 0143 void Parser::ParserPrivate::addSequenceFor09(Model model) 0144 { 0145 // RDF 0.9 doesn't contain an item sequence, and the items don't have rdf:about, so add both 0146 0147 const QList<ResourcePtr> items = model.resourcesWithType(RSS09Vocab::self()->item()); 0148 0149 if (items.isEmpty()) { 0150 return; 0151 } 0152 0153 const QList<ResourcePtr> channels = model.resourcesWithType(RSSVocab::self()->channel()); 0154 0155 if (channels.isEmpty()) { 0156 return; 0157 } 0158 0159 PropertyPtr itemIndex = model.createProperty(strInternalNs + strItemIndex); 0160 0161 // use QMap here, not QHash. as we need the sorting functionality 0162 QMap<uint, ResourcePtr> sorted; 0163 0164 for (const ResourcePtr &i : items) { 0165 QString numstr = i->property(itemIndex)->asString(); 0166 bool ok = false; 0167 uint num = numstr.toUInt(&ok); 0168 if (ok) { 0169 sorted[num] = i; 0170 } 0171 } 0172 0173 SequencePtr seq = model.createSequence(); 0174 model.addStatement(channels.first(), RSSVocab::self()->items(), seq); 0175 0176 for (const ResourcePtr &i : std::as_const(sorted)) { 0177 seq->append(i); 0178 // add rdf:about (type) 0179 model.addStatement(i, RDFVocab::self()->type(), RSSVocab::self()->item()); 0180 0181 // add to items sequence 0182 model.addStatement(seq, RDFVocab::self()->li(), i); 0183 } 0184 } 0185 0186 Parser::Parser() 0187 : d(new ParserPrivate) 0188 { 0189 d->strInternalNs = QStringLiteral("http://akregator.sf.net/libsyndication/internal#"); 0190 d->strItemIndex = QStringLiteral("itemIndex"); 0191 } 0192 0193 Parser::~Parser() = default; 0194 0195 Parser::Parser(const Parser &other) 0196 : AbstractParser(other) 0197 , d(nullptr) 0198 { 0199 } 0200 Parser &Parser::operator=(const Parser & /*other*/) 0201 { 0202 return *this; 0203 } 0204 0205 QString Parser::format() const 0206 { 0207 return QStringLiteral("rdf"); 0208 } 0209 0210 } // namespace RDF 0211 } // namespace Syndication