LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/kml - kml.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 286 366 78.1 %
Date: 2024-11-21 22:18:42 Functions: 23 32 71.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  KML Driver
       4             :  * Purpose:  Class for reading, parsing and handling a kmlfile.
       5             :  * Author:   Jens Oberender, j.obi@troja.net
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2007, Jens Oberender
       9             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : #include "kmlnode.h"
      14             : #include "kml.h"
      15             : 
      16             : #include <cstring>
      17             : #include <cstdio>
      18             : #include <exception>
      19             : #include <iostream>
      20             : #include <string>
      21             : 
      22             : #include "cpl_conv.h"
      23             : #include "cpl_error.h"
      24             : #ifdef HAVE_EXPAT
      25             : #include "expat.h"
      26             : #endif
      27             : 
      28             : constexpr int PARSER_BUF_SIZE = 8192;
      29             : 
      30          25 : KML::KML()
      31             :     : poTrunk_(nullptr), nNumLayers_(-1), papoLayers_(nullptr), nDepth_(0),
      32             :       validity(KML_VALIDITY_UNKNOWN), pKMLFile_(nullptr), poCurrent_(nullptr),
      33          25 :       oCurrentParser(nullptr), nDataHandlerCounter(0), nWithoutEventCounter(0)
      34             : {
      35          25 : }
      36             : 
      37          25 : KML::~KML()
      38             : {
      39          25 :     if (nullptr != pKMLFile_)
      40          25 :         VSIFCloseL(pKMLFile_);
      41          25 :     CPLFree(papoLayers_);
      42             : 
      43          25 :     delete poTrunk_;
      44          25 : }
      45             : 
      46          25 : bool KML::open(const char *pszFilename)
      47             : {
      48          25 :     if (nullptr != pKMLFile_)
      49           0 :         VSIFCloseL(pKMLFile_);
      50             : 
      51          25 :     pKMLFile_ = VSIFOpenL(pszFilename, "r");
      52          25 :     return pKMLFile_ != nullptr;
      53             : }
      54             : 
      55          24 : bool KML::parse()
      56             : {
      57          24 :     if (nullptr == pKMLFile_)
      58             :     {
      59           0 :         sError_ = "No file given";
      60           0 :         return false;
      61             :     }
      62             : 
      63          24 :     if (poTrunk_ != nullptr)
      64             :     {
      65           0 :         delete poTrunk_;
      66           0 :         poTrunk_ = nullptr;
      67             :     }
      68             : 
      69          24 :     if (poCurrent_ != nullptr)
      70             :     {
      71           0 :         delete poCurrent_;
      72           0 :         poCurrent_ = nullptr;
      73             :     }
      74             : 
      75          24 :     XML_Parser oParser = OGRCreateExpatXMLParser();
      76          24 :     XML_SetUserData(oParser, this);
      77          24 :     XML_SetElementHandler(oParser, startElement, endElement);
      78          24 :     XML_SetCharacterDataHandler(oParser, dataHandler);
      79          24 :     oCurrentParser = oParser;
      80          24 :     nWithoutEventCounter = 0;
      81             : 
      82          24 :     int nDone = 0;
      83          24 :     unsigned nLen = 0;
      84          48 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
      85          24 :     bool bError = false;
      86             : 
      87          45 :     do
      88             :     {
      89          69 :         nDataHandlerCounter = 0;
      90          69 :         nLen = (unsigned)VSIFReadL(aBuf.data(), 1, aBuf.size(), pKMLFile_);
      91          69 :         nDone = nLen < aBuf.size();
      92          69 :         if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
      93             :         {
      94           1 :             CPLError(CE_Failure, CPLE_AppDefined,
      95             :                      "XML parsing of KML file failed : %s at line %d, "
      96             :                      "column %d",
      97             :                      XML_ErrorString(XML_GetErrorCode(oParser)),
      98           1 :                      static_cast<int>(XML_GetCurrentLineNumber(oParser)),
      99           1 :                      static_cast<int>(XML_GetCurrentColumnNumber(oParser)));
     100           1 :             bError = true;
     101           1 :             break;
     102             :         }
     103          68 :         nWithoutEventCounter++;
     104          68 :     } while (!nDone && nLen > 0 && nWithoutEventCounter < 10);
     105             : 
     106          24 :     XML_ParserFree(oParser);
     107          24 :     VSIRewindL(pKMLFile_);
     108             : 
     109          24 :     if (nWithoutEventCounter == 10)
     110             :     {
     111           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     112             :                  "Too much data inside one element. File probably corrupted");
     113           0 :         bError = true;
     114             :     }
     115             : 
     116          24 :     if (bError)
     117             :     {
     118           1 :         if (poCurrent_ != nullptr)
     119             :         {
     120           0 :             while (poCurrent_)
     121             :             {
     122           0 :                 KMLNode *poTemp = poCurrent_->getParent();
     123           0 :                 delete poCurrent_;
     124           0 :                 poCurrent_ = poTemp;
     125             :             }
     126             :             // No need to destroy poTrunk_ : it has been destroyed in
     127             :             // the last iteration
     128             :         }
     129             :         else
     130             :         {
     131             :             // Case of invalid content after closing element matching
     132             :             // first <kml> element
     133           1 :             delete poTrunk_;
     134             :         }
     135           1 :         poTrunk_ = nullptr;
     136           1 :         return false;
     137             :     }
     138             : 
     139          23 :     poCurrent_ = nullptr;
     140          23 :     return true;
     141             : }
     142             : 
     143          25 : void KML::checkValidity()
     144             : {
     145          25 :     if (poTrunk_ != nullptr)
     146             :     {
     147           0 :         delete poTrunk_;
     148           0 :         poTrunk_ = nullptr;
     149             :     }
     150             : 
     151          25 :     if (poCurrent_ != nullptr)
     152             :     {
     153           0 :         delete poCurrent_;
     154           0 :         poCurrent_ = nullptr;
     155             :     }
     156             : 
     157          25 :     if (pKMLFile_ == nullptr)
     158             :     {
     159           0 :         sError_ = "No file given";
     160           1 :         return;
     161             :     }
     162             : 
     163          25 :     XML_Parser oParser = OGRCreateExpatXMLParser();
     164          25 :     XML_SetUserData(oParser, this);
     165          25 :     XML_SetElementHandler(oParser, startElementValidate, nullptr);
     166          25 :     XML_SetCharacterDataHandler(oParser, dataHandlerValidate);
     167          25 :     int nCount = 0;
     168             : 
     169          25 :     oCurrentParser = oParser;
     170             : 
     171          25 :     int nDone = 0;
     172          25 :     unsigned nLen = 0;
     173          50 :     std::vector<char> aBuf(PARSER_BUF_SIZE);
     174             : 
     175             :     // Parses the file until we find the first element.
     176           0 :     do
     177             :     {
     178          25 :         nDataHandlerCounter = 0;
     179          25 :         nLen = static_cast<unsigned>(
     180          25 :             VSIFReadL(aBuf.data(), 1, aBuf.size(), pKMLFile_));
     181          25 :         nDone = nLen < aBuf.size();
     182          25 :         if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
     183             :         {
     184           1 :             if (nLen <= PARSER_BUF_SIZE - 1)
     185           1 :                 aBuf[nLen] = 0;
     186             :             else
     187           0 :                 aBuf[PARSER_BUF_SIZE - 1] = 0;
     188           2 :             if (strstr(aBuf.data(), "<?xml") &&
     189           1 :                 (strstr(aBuf.data(), "<kml") ||
     190           0 :                  (strstr(aBuf.data(), "<Document") &&
     191           0 :                   strstr(aBuf.data(), "/kml/2."))))
     192             :             {
     193           1 :                 CPLError(
     194             :                     CE_Failure, CPLE_AppDefined,
     195             :                     "XML parsing of KML file failed : %s at line %d, column %d",
     196             :                     XML_ErrorString(XML_GetErrorCode(oParser)),
     197           1 :                     (int)XML_GetCurrentLineNumber(oParser),
     198           1 :                     (int)XML_GetCurrentColumnNumber(oParser));
     199             :             }
     200             : 
     201           1 :             validity = KML_VALIDITY_INVALID;
     202           1 :             XML_ParserFree(oParser);
     203           1 :             VSIRewindL(pKMLFile_);
     204           1 :             return;
     205             :         }
     206             : 
     207          24 :         nCount++;
     208             :         /* After reading 50 * PARSER_BUF_SIZE bytes, and not finding whether the file */
     209             :         /* is KML or not, we give up and fail silently */
     210          24 :     } while (!nDone && nLen > 0 && validity == KML_VALIDITY_UNKNOWN &&
     211             :              nCount < 50);
     212             : 
     213          24 :     XML_ParserFree(oParser);
     214          24 :     VSIRewindL(pKMLFile_);
     215          24 :     poCurrent_ = nullptr;
     216             : }
     217             : 
     218        5664 : void XMLCALL KML::startElement(void *pUserData, const char *pszName,
     219             :                                const char **ppszAttr)
     220             : {
     221        5664 :     KML *poKML = static_cast<KML *>(pUserData);
     222             :     try
     223             :     {
     224        5664 :         poKML->nWithoutEventCounter = 0;
     225             : 
     226        5664 :         const char *pszColumn = strchr(pszName, ':');
     227        5664 :         if (pszColumn)
     228           5 :             pszName = pszColumn + 1;
     229             : 
     230       11304 :         if (poKML->poTrunk_ == nullptr ||
     231       11280 :             (poKML->poCurrent_ != nullptr &&
     232        5640 :              poKML->poCurrent_->getName().compare("description") != 0))
     233             :         {
     234        5659 :             if (poKML->nDepth_ == 1024)
     235             :             {
     236           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     237             :                          "Too big depth level (%d) while parsing KML.",
     238             :                          poKML->nDepth_);
     239           0 :                 XML_StopParser(poKML->oCurrentParser, XML_FALSE);
     240           0 :                 return;
     241             :             }
     242             : 
     243        5659 :             KMLNode *poMynew = new KMLNode();
     244        5659 :             poMynew->setName(pszName);
     245        5659 :             poMynew->setLevel(poKML->nDepth_);
     246             : 
     247        7095 :             for (int i = 0; ppszAttr[i]; i += 2)
     248             :             {
     249        1436 :                 Attribute *poAtt = new Attribute();
     250        1436 :                 poAtt->sName = ppszAttr[i];
     251        1436 :                 poAtt->sValue = ppszAttr[i + 1];
     252        1436 :                 poMynew->addAttribute(poAtt);
     253             :             }
     254             : 
     255        5659 :             if (poKML->poTrunk_ == nullptr)
     256          24 :                 poKML->poTrunk_ = poMynew;
     257        5659 :             if (poKML->poCurrent_ != nullptr)
     258        5635 :                 poMynew->setParent(poKML->poCurrent_);
     259        5659 :             poKML->poCurrent_ = poMynew;
     260             : 
     261        5659 :             poKML->nDepth_++;
     262             :         }
     263           5 :         else if (poKML->poCurrent_ != nullptr)
     264             :         {
     265          10 :             std::string sNewContent = "<";
     266           5 :             sNewContent += pszName;
     267           6 :             for (int i = 0; ppszAttr[i]; i += 2)
     268             :             {
     269           1 :                 sNewContent += " ";
     270           1 :                 sNewContent += ppszAttr[i];
     271           1 :                 sNewContent += "=\"";
     272           1 :                 sNewContent += ppszAttr[i + 1];
     273           1 :                 sNewContent += "\"";
     274             :             }
     275           5 :             sNewContent += ">";
     276           5 :             if (poKML->poCurrent_->numContent() == 0)
     277           0 :                 poKML->poCurrent_->addContent(sNewContent);
     278             :             else
     279           5 :                 poKML->poCurrent_->appendContent(sNewContent);
     280             :         }
     281             :     }
     282           0 :     catch (const std::exception &ex)
     283             :     {
     284           0 :         CPLError(CE_Failure, CPLE_AppDefined, "KML: libstdc++ exception : %s",
     285           0 :                  ex.what());
     286           0 :         XML_StopParser(poKML->oCurrentParser, XML_FALSE);
     287             :     }
     288             : }
     289             : 
     290        2053 : void XMLCALL KML::startElementValidate(void *pUserData, const char *pszName,
     291             :                                        const char **ppszAttr)
     292             : {
     293        2053 :     KML *poKML = static_cast<KML *>(pUserData);
     294             : 
     295        2053 :     if (poKML->validity != KML_VALIDITY_UNKNOWN)
     296        2028 :         return;
     297             : 
     298          25 :     poKML->validity = KML_VALIDITY_INVALID;
     299             : 
     300          25 :     const char *pszColumn = strchr(pszName, ':');
     301          25 :     if (pszColumn)
     302           1 :         pszName = pszColumn + 1;
     303             : 
     304          25 :     if (strcmp(pszName, "kml") == 0 || strcmp(pszName, "Document") == 0)
     305             :     {
     306             :         // Check all Attributes
     307          53 :         for (int i = 0; ppszAttr[i]; i += 2)
     308             :         {
     309             :             // Find the namespace and determine the KML version
     310          28 :             if (strcmp(ppszAttr[i], "xmlns") == 0)
     311             :             {
     312             :                 // Is it KML 2.2?
     313          23 :                 if ((strcmp(ppszAttr[i + 1],
     314          23 :                             "http://earth.google.com/kml/2.2") == 0) ||
     315          23 :                     (strcmp(ppszAttr[i + 1],
     316             :                             "http://www.opengis.net/kml/2.2") == 0))
     317             :                 {
     318           9 :                     poKML->validity = KML_VALIDITY_VALID;
     319           9 :                     poKML->sVersion_ = "2.2";
     320             :                 }
     321          14 :                 else if (strcmp(ppszAttr[i + 1],
     322             :                                 "http://earth.google.com/kml/2.1") == 0)
     323             :                 {
     324          14 :                     poKML->validity = KML_VALIDITY_VALID;
     325          14 :                     poKML->sVersion_ = "2.1";
     326             :                 }
     327           0 :                 else if (strcmp(ppszAttr[i + 1],
     328             :                                 "http://earth.google.com/kml/2.0") == 0)
     329             :                 {
     330           0 :                     poKML->validity = KML_VALIDITY_VALID;
     331           0 :                     poKML->sVersion_ = "2.0";
     332             :                 }
     333             :                 else
     334             :                 {
     335           0 :                     CPLDebug("KML",
     336             :                              "Unhandled xmlns value : %s. Going on though...",
     337           0 :                              ppszAttr[i]);
     338           0 :                     poKML->validity = KML_VALIDITY_VALID;
     339           0 :                     poKML->sVersion_ = "?";
     340             :                 }
     341             :             }
     342             :         }
     343             : 
     344          25 :         if (poKML->validity == KML_VALIDITY_INVALID)
     345             :         {
     346           2 :             CPLDebug("KML", "Did not find xmlns attribute in <kml> element. "
     347             :                             "Going on though...");
     348           2 :             poKML->validity = KML_VALIDITY_VALID;
     349           2 :             poKML->sVersion_ = "?";
     350             :         }
     351             :     }
     352             : }
     353             : 
     354        7634 : void XMLCALL KML::dataHandlerValidate(void *pUserData,
     355             :                                       const char * /* pszData */,
     356             :                                       int /* nLen */)
     357             : {
     358        7634 :     KML *poKML = static_cast<KML *>(pUserData);
     359             : 
     360        7634 :     poKML->nDataHandlerCounter++;
     361        7634 :     if (poKML->nDataHandlerCounter >= PARSER_BUF_SIZE)
     362             :     {
     363           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     364             :                  "File probably corrupted (million laugh pattern)");
     365           0 :         XML_StopParser(poKML->oCurrentParser, XML_FALSE);
     366             :     }
     367        7634 : }
     368             : 
     369        5664 : void XMLCALL KML::endElement(void *pUserData, const char *pszName)
     370             : {
     371        5664 :     KML *poKML = static_cast<KML *>(pUserData);
     372             : 
     373             :     try
     374             :     {
     375        5664 :         poKML->nWithoutEventCounter = 0;
     376             : 
     377        5664 :         const char *pszColumn = strchr(pszName, ':');
     378        5664 :         if (pszColumn)
     379           5 :             pszName = pszColumn + 1;
     380             : 
     381       11328 :         if (poKML->poCurrent_ != nullptr &&
     382        5664 :             poKML->poCurrent_->getName().compare(pszName) == 0)
     383             :         {
     384        5659 :             poKML->nDepth_--;
     385        5659 :             KMLNode *poTmp = poKML->poCurrent_;
     386             :             // Split the coordinates
     387        5926 :             if (poKML->poCurrent_->getName().compare("coordinates") == 0 &&
     388         267 :                 poKML->poCurrent_->numContent() == 1)
     389             :             {
     390         524 :                 const std::string sData = poKML->poCurrent_->getContent(0);
     391         262 :                 std::size_t nPos = 0;
     392         262 :                 const std::size_t nLength = sData.length();
     393         262 :                 const char *pszData = sData.c_str();
     394             :                 while (true)
     395             :                 {
     396             :                     // Cut off whitespaces
     397       33866 :                     while (nPos < nLength &&
     398       33604 :                            (pszData[nPos] == ' ' || pszData[nPos] == '\n' ||
     399        2105 :                             pszData[nPos] == '\r' || pszData[nPos] == '\t'))
     400       31499 :                         nPos++;
     401             : 
     402        2367 :                     if (nPos == nLength)
     403         262 :                         break;
     404             : 
     405        2105 :                     const std::size_t nPosBegin = nPos;
     406             : 
     407             :                     // Get content
     408       81106 :                     while (nPos < nLength && pszData[nPos] != ' ' &&
     409      162061 :                            pszData[nPos] != '\n' && pszData[nPos] != '\r' &&
     410       79087 :                            pszData[nPos] != '\t')
     411       79087 :                         nPos++;
     412             : 
     413        2105 :                     if (nPos - nPosBegin > 0)
     414             :                     {
     415        4210 :                         std::string sTmp(pszData + nPosBegin, nPos - nPosBegin);
     416        2105 :                         poKML->poCurrent_->addContent(sTmp);
     417             :                     }
     418        2105 :                 }
     419         262 :                 if (poKML->poCurrent_->numContent() > 1)
     420         262 :                     poKML->poCurrent_->deleteContent(0);
     421             :             }
     422        5397 :             else if (poKML->poCurrent_->numContent() == 1)
     423             :             {
     424        9940 :                 const std::string sData = poKML->poCurrent_->getContent(0);
     425        9940 :                 std::string sDataWithoutNL;
     426        4970 :                 std::size_t nPos = 0;
     427        4970 :                 const std::size_t nLength = sData.length();
     428        4970 :                 const char *pszData = sData.c_str();
     429        4970 :                 std::size_t nLineStartPos = 0;
     430        4970 :                 bool bLineStart = true;
     431             : 
     432             :                 // Re-assemble multi-line content by removing leading spaces for
     433             :                 // each line.  I am not sure why we do that. Should we preserve
     434             :                 // content as such?
     435      173819 :                 while (nPos < nLength)
     436             :                 {
     437      168849 :                     const char ch = pszData[nPos];
     438      168849 :                     if (bLineStart &&
     439       11797 :                         (ch == ' ' || ch == '\t' || ch == '\n' || ch == '\r'))
     440       80456 :                         nLineStartPos++;
     441       88393 :                     else if (ch == '\n' || ch == '\r')
     442             :                     {
     443        1244 :                         if (!bLineStart)
     444             :                         {
     445             :                             std::string sTmp(pszData + nLineStartPos,
     446        1244 :                                              nPos - nLineStartPos);
     447        1244 :                             if (!sDataWithoutNL.empty())
     448        1111 :                                 sDataWithoutNL += " ";
     449        1244 :                             sDataWithoutNL += sTmp;
     450        1244 :                             bLineStart = true;
     451             :                         }
     452        1244 :                         nLineStartPos = nPos + 1;
     453             :                     }
     454             :                     else
     455             :                     {
     456       87149 :                         bLineStart = false;
     457             :                     }
     458      168849 :                     nPos++;
     459             :                 }
     460             : 
     461        4970 :                 if (nLineStartPos > 0)
     462             :                 {
     463        1855 :                     if (nLineStartPos < nPos)
     464             :                     {
     465             :                         std::string sTmp(pszData + nLineStartPos,
     466         242 :                                          nPos - nLineStartPos);
     467         121 :                         if (!sDataWithoutNL.empty())
     468         121 :                             sDataWithoutNL += " ";
     469         121 :                         sDataWithoutNL += sTmp;
     470             :                     }
     471             : 
     472        1855 :                     poKML->poCurrent_->deleteContent(0);
     473        1855 :                     poKML->poCurrent_->addContent(sDataWithoutNL);
     474             :                 }
     475             :             }
     476             : 
     477        5659 :             if (poKML->poCurrent_->getParent() != nullptr)
     478        5635 :                 poKML->poCurrent_ = poKML->poCurrent_->getParent();
     479             :             else
     480          24 :                 poKML->poCurrent_ = nullptr;
     481             : 
     482        5659 :             if (!poKML->isHandled(pszName))
     483             :             {
     484        3553 :                 CPLDebug("KML", "Not handled: %s", pszName);
     485        3553 :                 delete poTmp;
     486        3553 :                 if (poKML->poCurrent_ == poTmp)
     487           0 :                     poKML->poCurrent_ = nullptr;
     488        3553 :                 if (poKML->poTrunk_ == poTmp)
     489           0 :                     poKML->poTrunk_ = nullptr;
     490             :             }
     491             :             else
     492             :             {
     493        2106 :                 if (poKML->poCurrent_ != nullptr)
     494        2082 :                     poKML->poCurrent_->addChildren(poTmp);
     495             :             }
     496             :         }
     497           5 :         else if (poKML->poCurrent_ != nullptr)
     498             :         {
     499          10 :             std::string sNewContent = "</";
     500           5 :             sNewContent += pszName;
     501           5 :             sNewContent += ">";
     502           5 :             if (poKML->poCurrent_->numContent() == 0)
     503           0 :                 poKML->poCurrent_->addContent(sNewContent);
     504             :             else
     505           5 :                 poKML->poCurrent_->appendContent(sNewContent);
     506             :         }
     507             :     }
     508           0 :     catch (const std::exception &ex)
     509             :     {
     510           0 :         CPLError(CE_Failure, CPLE_AppDefined, "KML: libstdc++ exception : %s",
     511           0 :                  ex.what());
     512           0 :         XML_StopParser(poKML->oCurrentParser, XML_FALSE);
     513             :     }
     514        5664 : }
     515             : 
     516       24246 : void XMLCALL KML::dataHandler(void *pUserData, const char *pszData, int nLen)
     517             : {
     518       24246 :     KML *poKML = static_cast<KML *>(pUserData);
     519             : 
     520       24246 :     poKML->nWithoutEventCounter = 0;
     521             : 
     522       24246 :     if (nLen < 1 || poKML->poCurrent_ == nullptr)
     523           0 :         return;
     524             : 
     525       24246 :     poKML->nDataHandlerCounter++;
     526       24246 :     if (poKML->nDataHandlerCounter >= PARSER_BUF_SIZE)
     527             :     {
     528           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     529             :                  "File probably corrupted (million laugh pattern)");
     530           0 :         XML_StopParser(poKML->oCurrentParser, XML_FALSE);
     531             :     }
     532             : 
     533             :     try
     534             :     {
     535       48492 :         std::string sData(pszData, nLen);
     536             : 
     537       24246 :         if (poKML->poCurrent_->numContent() == 0)
     538        5232 :             poKML->poCurrent_->addContent(sData);
     539             :         else
     540       19014 :             poKML->poCurrent_->appendContent(sData);
     541             :     }
     542           0 :     catch (const std::exception &ex)
     543             :     {
     544           0 :         CPLError(CE_Failure, CPLE_AppDefined, "KML: libstdc++ exception : %s",
     545           0 :                  ex.what());
     546           0 :         XML_StopParser(poKML->oCurrentParser, XML_FALSE);
     547             :     }
     548             : }
     549             : 
     550          25 : bool KML::isValid()
     551             : {
     552          25 :     checkValidity();
     553             : 
     554          25 :     if (validity == KML_VALIDITY_VALID)
     555          24 :         CPLDebug("KML", "Valid: 1 Version: %s", sVersion_.c_str());
     556             : 
     557          25 :     return validity == KML_VALIDITY_VALID;
     558             : }
     559             : 
     560           0 : std::string KML::getError() const
     561             : {
     562           0 :     return sError_;
     563             : }
     564             : 
     565          23 : int KML::classifyNodes()
     566             : {
     567          23 :     if (poTrunk_ == nullptr)
     568           0 :         return false;
     569          23 :     return poTrunk_->classify(this);
     570             : }
     571             : 
     572          19 : void KML::eliminateEmpty()
     573             : {
     574          19 :     if (poTrunk_ != nullptr)
     575          19 :         poTrunk_->eliminateEmpty(this);
     576          19 : }
     577             : 
     578           0 : void KML::print(unsigned short nNum)
     579             : {
     580           0 :     if (poTrunk_ != nullptr)
     581           0 :         poTrunk_->print(nNum);
     582           0 : }
     583             : 
     584        5659 : bool KML::isHandled(std::string const &elem) const
     585             : {
     586       10176 :     return isLeaf(elem) || isFeature(elem) || isFeatureContainer(elem) ||
     587       10176 :            isContainer(elem) || isRest(elem);
     588             : }
     589             : 
     590           0 : bool KML::isLeaf(std::string const & /* elem */) const
     591             : {
     592           0 :     return false;
     593             : }
     594             : 
     595           0 : bool KML::isFeature(std::string const & /* elem */) const
     596             : {
     597           0 :     return false;
     598             : }
     599             : 
     600           0 : bool KML::isFeatureContainer(std::string const & /* elem */) const
     601             : {
     602           0 :     return false;
     603             : }
     604             : 
     605           0 : bool KML::isContainer(std::string const & /* elem */) const
     606             : {
     607           0 :     return false;
     608             : }
     609             : 
     610           0 : bool KML::isRest(std::string const & /* elem */) const
     611             : {
     612           0 :     return false;
     613             : }
     614             : 
     615           0 : void KML::findLayers(KMLNode * /* poNode */, int /* bKeepEmptyContainers */)
     616             : {
     617             :     // idle
     618           0 : }
     619             : 
     620          23 : bool KML::hasOnlyEmpty() const
     621             : {
     622          23 :     return poTrunk_->hasOnlyEmpty();
     623             : }
     624             : 
     625          23 : int KML::getNumLayers() const
     626             : {
     627          23 :     return nNumLayers_;
     628             : }
     629             : 
     630         875 : bool KML::selectLayer(int nNum)
     631             : {
     632         875 :     if (nNumLayers_ < 1 || nNum >= nNumLayers_)
     633           0 :         return false;
     634         875 :     poCurrent_ = papoLayers_[nNum];
     635         875 :     return true;
     636             : }
     637             : 
     638          82 : std::string KML::getCurrentName() const
     639             : {
     640          82 :     std::string tmp;
     641          82 :     if (poCurrent_ != nullptr)
     642             :     {
     643          82 :         tmp = poCurrent_->getNameElement();
     644             :     }
     645          82 :     return tmp;
     646             : }
     647             : 
     648         202 : Nodetype KML::getCurrentType() const
     649             : {
     650         202 :     if (poCurrent_ != nullptr)
     651         202 :         return poCurrent_->getType();
     652             : 
     653           0 :     return Unknown;
     654             : }
     655             : 
     656          74 : int KML::is25D() const
     657             : {
     658          74 :     if (poCurrent_ != nullptr)
     659          74 :         return poCurrent_->is25D();
     660             : 
     661           0 :     return Unknown;
     662             : }
     663             : 
     664          61 : int KML::getNumFeatures()
     665             : {
     666          61 :     if (poCurrent_ == nullptr)
     667           0 :         return -1;
     668             : 
     669          61 :     return static_cast<int>(poCurrent_->getNumFeatures());
     670             : }
     671             : 
     672         916 : Feature *KML::getFeature(std::size_t nNum, int &nLastAsked, int &nLastCount)
     673             : {
     674         916 :     if (poCurrent_ == nullptr)
     675           0 :         return nullptr;
     676             : 
     677         916 :     return poCurrent_->getFeature(nNum, nLastAsked, nLastCount);
     678             : }
     679             : 
     680         116 : void KML::unregisterLayerIfMatchingThisNode(KMLNode *poNode)
     681             : {
     682         116 :     for (int i = 0; i < nNumLayers_;)
     683             :     {
     684           2 :         if (papoLayers_[i] == poNode)
     685             :         {
     686           2 :             if (i < nNumLayers_ - 1)
     687             :             {
     688           0 :                 memmove(papoLayers_ + i, papoLayers_ + i + 1,
     689           0 :                         (nNumLayers_ - 1 - i) * sizeof(KMLNode *));
     690             :             }
     691           2 :             nNumLayers_--;
     692           2 :             break;
     693             :         }
     694           0 :         i++;
     695             :     }
     696         116 : }

Generated by: LCOV version 1.14