LCOV - code coverage report
Current view: top level - src/parser - SitemapParser.cpp (source / functions) Coverage Total Hit
Test: coverage.info.cleaned Lines: 91.6 % 95 87
Test Date: 2026-03-23 10:19:47 Functions: 100.0 % 4 4

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

Generated by: LCOV version 2.0-1