LCOV - code coverage report
Current view: top level - lib/FangFeedDiscovery - SitemapParser.cpp (source / functions) Coverage Total Hit
Test: coverage.info.cleaned Lines: 85.6 % 104 89
Test Date: 2026-04-19 00:35:54 Functions: 83.3 % 6 5

            Line data    Source code
       1              : #include "SitemapParser.h"
       2              : 
       3              : #include <QXmlStreamReader>
       4              : 
       5              : #include "FeedDiscoveryLogging.h"
       6              : 
       7           12 : SitemapParser::SitemapParser()
       8           12 :     : _hasNewsEntries(false)
       9              : {
      10           12 : }
      11              : 
      12           12 : SitemapParser::SitemapType SitemapParser::parse(const QString& xml)
      13              : {
      14           12 :     _entries.clear();
      15           12 :     _subSitemaps.clear();
      16           12 :     _hasNewsEntries = false;
      17              : 
      18           12 :     QXmlStreamReader reader(xml);
      19           24 :     return parseImpl(reader);
      20           12 : }
      21              : 
      22            0 : SitemapParser::SitemapType SitemapParser::parse(const QByteArray& data)
      23              : {
      24            0 :     _entries.clear();
      25            0 :     _subSitemaps.clear();
      26            0 :     _hasNewsEntries = false;
      27              : 
      28            0 :     QXmlStreamReader reader(data);
      29            0 :     return parseImpl(reader);
      30            0 : }
      31              : 
      32           12 : SitemapParser::SitemapType SitemapParser::parseImpl(QXmlStreamReader& reader)
      33              : {
      34              :     // Find the root element.
      35           25 :     while (!reader.atEnd()) {
      36           23 :         reader.readNext();
      37           23 :         if (reader.isStartElement()) {
      38           10 :             QString root = reader.name().toString().toLower();
      39           10 :             if (root == "urlset") {
      40            7 :                 parseUrlSet(reader);
      41            7 :                 return UrlSet;
      42            3 :             } else if (root == "sitemapindex") {
      43            1 :                 parseSitemapIndex(reader);
      44            1 :                 return SitemapIndex;
      45              :             } else {
      46            4 :                 qCDebug(logFeedDiscovery) << "SitemapParser: unexpected root element:" << root;
      47            2 :                 return Invalid;
      48              :             }
      49           10 :         }
      50              :     }
      51              : 
      52            4 :     qCDebug(logFeedDiscovery) << "SitemapParser: no root element found";
      53            2 :     return Invalid;
      54              : }
      55              : 
      56            7 : void SitemapParser::parseUrlSet(QXmlStreamReader& xml)
      57              : {
      58              :     // We're inside <urlset>. Look for <url> children.
      59           69 :     while (!xml.atEnd()) {
      60           55 :         xml.readNext();
      61              : 
      62           55 :         if (xml.isStartElement() && xml.name().toString().toLower() == "url") {
      63           17 :             SitemapEntry entry;
      64              : 
      65              :             // Parse children of <url>. This includes both standard sitemap
      66              :             // elements (<loc>, <lastmod>) and Google News extension elements
      67              :             // nested inside <news:news>.
      68           17 :             int depth = 1; // Track nesting depth within <url>
      69          339 :             while (!xml.atEnd() && depth > 0) {
      70          322 :                 xml.readNext();
      71              : 
      72          322 :                 if (xml.isEndElement()) {
      73           50 :                     if (xml.name().toString().toLower() == "url") {
      74           17 :                         depth = 0;
      75              :                     }
      76              :                 }
      77              : 
      78          322 :                 if (xml.isStartElement()) {
      79              :                     // The namespace-qualified local names come through as just
      80              :                     // the local part (e.g. "title" for news:title), so we use
      81              :                     // the qualified name to distinguish.
      82          111 :                     QString localName = xml.name().toString().toLower();
      83          111 :                     QString qualifiedName = xml.qualifiedName().toString().toLower();
      84              : 
      85          111 :                     if (localName == "loc" && !qualifiedName.contains(':')) {
      86           17 :                         entry.url = QUrl(xml.readElementText().trimmed());
      87           94 :                     } else if (localName == "lastmod" && !qualifiedName.contains(':')) {
      88           24 :                         entry.lastmod = QDateTime::fromString(
      89           36 :                             xml.readElementText().trimmed(), Qt::ISODate);
      90            0 :                     } else if (qualifiedName == "news:title"
      91           82 :                                || (localName == "title"
      92           82 :                                    && xml.namespaceUri().toString().contains("sitemap-news"))) {
      93           12 :                         entry.newsTitle = xml.readElementText().trimmed();
      94            0 :                     } else if (qualifiedName == "news:publication_date"
      95           70 :                                || (localName == "publication_date"
      96           70 :                                    && xml.namespaceUri().toString().contains("sitemap-news"))) {
      97           24 :                         entry.publicationDate = QDateTime::fromString(
      98           36 :                             xml.readElementText().trimmed(), Qt::ISODate);
      99            0 :                     } else if (qualifiedName == "news:language"
     100           58 :                                || (localName == "language"
     101           58 :                                    && xml.namespaceUri().toString().contains("sitemap-news"))) {
     102           12 :                         entry.language = xml.readElementText().trimmed().toLower();
     103            0 :                     } else if (qualifiedName == "news:name"
     104           46 :                                || (localName == "name"
     105           46 :                                    && xml.namespaceUri().toString().contains("sitemap-news"))) {
     106           12 :                         entry.publicationName = xml.readElementText().trimmed();
     107            0 :                     } else if (qualifiedName == "image:loc"
     108           34 :                                || (localName == "loc"
     109           34 :                                    && xml.namespaceUri().toString().contains("sitemap-image"))) {
     110            1 :                         entry.imageUrl = QUrl(xml.readElementText().trimmed());
     111              :                     }
     112          111 :                 }
     113              :             }
     114              : 
     115           17 :             if (entry.url.isValid()) {
     116           17 :                 if (!entry.newsTitle.isEmpty()) {
     117           12 :                     _hasNewsEntries = true;
     118              :                 }
     119           17 :                 _entries.append(entry);
     120              :             }
     121           17 :         }
     122              :     }
     123              : 
     124           14 :     qCDebug(logFeedDiscovery) << "SitemapParser: parsed" << _entries.size() << "URL entries"
     125            7 :                         << (_hasNewsEntries ? "(with news extensions)" : "(no news extensions)");
     126            7 : }
     127              : 
     128            1 : void SitemapParser::parseSitemapIndex(QXmlStreamReader& xml)
     129              : {
     130              :     // We're inside <sitemapindex>. Look for <sitemap> children.
     131           11 :     while (!xml.atEnd()) {
     132            9 :         xml.readNext();
     133              : 
     134            9 :         if (xml.isStartElement() && xml.name().toString().toLower() == "sitemap") {
     135            3 :             SubSitemap sub;
     136              : 
     137              :             // Parse children of <sitemap>.
     138           15 :             while (!xml.atEnd()) {
     139           12 :                 xml.readNext();
     140              : 
     141           12 :                 if (xml.isEndElement() && xml.name().toString().toLower() == "sitemap") {
     142            3 :                     break;
     143              :                 }
     144              : 
     145            9 :                 if (xml.isStartElement()) {
     146            3 :                     QString tag = xml.name().toString().toLower();
     147            3 :                     if (tag == "loc") {
     148            3 :                         sub.url = QUrl(xml.readElementText().trimmed());
     149            0 :                     } else if (tag == "lastmod") {
     150            0 :                         sub.lastmod = QDateTime::fromString(
     151            0 :                             xml.readElementText().trimmed(), Qt::ISODate);
     152              :                     }
     153            3 :                 }
     154              :             }
     155              : 
     156            3 :             if (sub.url.isValid()) {
     157            3 :                 _subSitemaps.append(sub);
     158              :             }
     159            3 :         }
     160              :     }
     161              : 
     162            2 :     qCDebug(logFeedDiscovery) << "SitemapParser: parsed" << _subSitemaps.size() << "sub-sitemaps";
     163            1 : }
        

Generated by: LCOV version 2.0-1