LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/kml - kml.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 284 364 78.0 %
Date: 2025-05-31 00:00:17 Functions: 22 31 71.0 %

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

Generated by: LCOV version 1.14