Line data Source code
1 : #include "NewsParser.h"
2 :
3 : #include <QDebug>
4 : #include <QFile>
5 : #include <QFileInfo>
6 :
7 : #include "../utilities/ErrorHandling.h"
8 : #include "ParserXMLWorker.h"
9 :
10 86 : NewsParser::NewsParser(QObject *parent) :
11 : ParserInterface(parent),
12 86 : feed(nullptr), result(OK), networkError(QNetworkReply::NetworkError::NoError),
13 172 : currentReply(nullptr), redirectReply(nullptr),
14 86 : fromCache(false), noParseIfCached(false)
15 : {
16 : // Connex0r teh siganls.
17 86 : connect(&manager, &FangNetworkAccessManager::finished,
18 86 : this, &NewsParser::netFinished);
19 :
20 : // Setup the worker object.
21 86 : ParserXMLWorker* worker = new ParserXMLWorker();
22 86 : worker->moveToThread(&workerThread);
23 86 : connect(&workerThread, &QThread::finished, worker, &QObject::deleteLater);
24 86 : connect(this, &NewsParser::triggerDocStart, worker, &ParserXMLWorker::documentStart);
25 86 : connect(this, &NewsParser::triggerDocEnd, worker, &ParserXMLWorker::documentEnd);
26 86 : connect(this, &NewsParser::triggerAddXML, worker, &ParserXMLWorker::addXML);
27 86 : connect(worker, &ParserXMLWorker::done, this, &NewsParser::workerDone);
28 :
29 86 : workerThread.start();
30 86 : }
31 :
32 113 : NewsParser::~NewsParser()
33 : {
34 86 : workerThread.quit();
35 86 : workerThread.wait();
36 :
37 86 : delete currentReply;
38 113 : }
39 :
40 1 : void NewsParser::parse(const QUrl& url, bool noParseIfCached)
41 : {
42 1 : initParse(url);
43 :
44 1 : this->noParseIfCached = noParseIfCached;
45 :
46 : // in with the new
47 1 : QNetworkRequest request(url);
48 1 : if (currentReply) {
49 0 : currentReply->disconnect(this);
50 0 : currentReply->deleteLater();
51 : }
52 :
53 1 : currentReply = manager.get(request);
54 1 : connect(currentReply, &QNetworkReply::readyRead, this, &NewsParser::readyRead);
55 1 : connect(currentReply, &QNetworkReply::metaDataChanged, this, &NewsParser::metaDataChanged);
56 1 : connect(currentReply, &QNetworkReply::errorOccurred, this, &NewsParser::error);
57 1 : }
58 :
59 :
60 59 : void NewsParser::parseFile(const QString &filename)
61 : {
62 59 : initParse();
63 :
64 59 : QFile file(filename);
65 :
66 59 : if (!file.exists()) {
67 0 : qCritical() << "NewsParser::parseFile: File does not exist:" << filename;
68 0 : return;
69 : }
70 :
71 59 : if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
72 0 : qCritical() << "NewsParser::parseFile: Cannot open file:" << filename;
73 0 : return;
74 : }
75 :
76 59 : QByteArray data = file.readAll();
77 59 : emit triggerAddXML(data);
78 59 : emit triggerDocEnd();
79 59 : }
80 :
81 0 : void NewsParser::error(QNetworkReply::NetworkError ne)
82 : {
83 : Q_UNUSED(ne);
84 0 : currentReply->disconnect(this);
85 0 : currentReply->deleteLater();
86 0 : currentReply = 0;
87 :
88 0 : result = NewsParser::NETWORK_ERROR;
89 0 : networkError = ne;
90 0 : emit done();
91 0 : }
92 :
93 :
94 0 : void NewsParser::readyRead()
95 : {
96 0 : int statusCode = currentReply->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
97 0 : if (statusCode >= 200 && statusCode < 300) {
98 0 : QByteArray data = currentReply->readAll();
99 0 : emit triggerAddXML(data);
100 0 : }
101 0 : }
102 :
103 0 : void NewsParser::metaDataChanged()
104 : {
105 0 : QUrl redirectionTarget = currentReply->attribute(
106 0 : QNetworkRequest::RedirectionTargetAttribute).toUrl();
107 :
108 : // TODO: prevent endless loooooping!!!
109 0 : if (redirectionTarget.isValid()) {
110 0 : qDebug() << "Redirect: " << redirectionTarget.toString();
111 0 : redirectReply = currentReply;
112 0 : parse(redirectionTarget, noParseIfCached);
113 : }
114 :
115 0 : if (currentReply->attribute(
116 0 : QNetworkRequest::SourceIsFromCacheAttribute).isValid()) {
117 0 : if (currentReply->attribute(
118 0 : QNetworkRequest::SourceIsFromCacheAttribute).toBool()) {
119 0 : if (noParseIfCached) {
120 : // Early exit for cache.
121 0 : currentReply->disconnect(this);
122 0 : currentReply->deleteLater();
123 0 : currentReply = 0;
124 :
125 0 : result = NewsParser::OK;
126 0 : emit done();
127 :
128 0 : return;
129 : }
130 : }
131 : }
132 0 : }
133 :
134 0 : NewsParser::ParseResult NewsParser::getResult()
135 : {
136 0 : return result;
137 : }
138 :
139 59 : RawFeed* NewsParser::getFeed()
140 : {
141 59 : return result == NewsParser::OK ? feed : nullptr;
142 : }
143 :
144 0 : void NewsParser::netFinished(QNetworkReply *reply)
145 : {
146 0 : if (redirectReply == reply) {
147 0 : return; // This was the previous redirect.
148 : }
149 :
150 : // Remember this URL.
151 0 : finalFeedURL = reply->url();
152 :
153 : // Tell the worker that we're done.
154 0 : emit triggerDocEnd();
155 : }
156 :
157 59 : void NewsParser::workerDone(RawFeed* rawFeed)
158 : {
159 59 : if (result != NewsParser::IN_PROGRESS) {
160 : // Already emitted a finished signal. Nothing to dooooo.
161 0 : return;
162 : }
163 :
164 59 : if (rawFeed) {
165 59 : feed = rawFeed;
166 :
167 : // This means we saw... something. Do a sanity check here to
168 : // make sure what we found was an actual feed.
169 59 : if (feed->items.size() > 0 || feed->title != "") {
170 59 : feed->url = finalFeedURL;
171 :
172 59 : result = NewsParser::OK;
173 59 : emit done();
174 :
175 59 : return; // Early exit on SUCCESS!! (yay)
176 : }
177 : }
178 :
179 : // What we found must not have been an RSS/Atom feed.
180 0 : result = NewsParser::PARSE_ERROR;
181 0 : emit done();
182 : }
183 :
184 60 : void NewsParser::initParse(const QUrl& url)
185 : {
186 60 : result = NewsParser::IN_PROGRESS;
187 60 : networkError = QNetworkReply::NetworkError::NoError;
188 60 : finalFeedURL = url;
189 60 : emit triggerDocStart();
190 60 : }
|