LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gml - gmlhandler.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 856 939 91.2 %
Date: 2025-12-05 02:43:06 Functions: 50 52 96.2 %

          Line data    Source code
       1             : /**********************************************************************
       2             :  *
       3             :  * Project:  GML Reader
       4             :  * Purpose:  Implementation of GMLHandler class.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  **********************************************************************
       8             :  * Copyright (c) 2002, Frank Warmerdam
       9             :  * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "gmlreader.h"
      16             : #include "gmlreaderp.h"
      17             : 
      18             : #include <algorithm>
      19             : #include <climits>
      20             : #include <cstddef>
      21             : #include <cstdlib>
      22             : #include <cstring>
      23             : #include <memory>
      24             : #include <string>
      25             : #include <vector>
      26             : 
      27             : #include "cpl_conv.h"
      28             : #include "cpl_error.h"
      29             : #include "cpl_hash_set.h"
      30             : #include "cpl_minixml.h"
      31             : #include "cpl_string.h"
      32             : #include "cpl_vsi.h"
      33             : #ifdef HAVE_EXPAT
      34             : #include "expat.h"
      35             : #include "expat_external.h"
      36             : #endif
      37             : #include "ogr_core.h"
      38             : #ifdef HAVE_XERCES
      39             : #include "ogr_xerces.h"
      40             : #endif
      41             : 
      42             : #ifdef HAVE_XERCES
      43             : 
      44             : /************************************************************************/
      45             : /*                        GMLXercesHandler()                            */
      46             : /************************************************************************/
      47             : 
      48           2 : GMLXercesHandler::GMLXercesHandler(GMLReader *poReader)
      49           2 :     : GMLHandler(poReader), m_nEntityCounter(0)
      50             : {
      51           2 : }
      52             : 
      53             : /************************************************************************/
      54             : /*                    GMLXercesHandlerDealWithError()                   */
      55             : /************************************************************************/
      56             : 
      57         939 : static void GMLXercesHandlerDealWithError(OGRErr eErr)
      58             : {
      59         939 :     if (eErr == OGRERR_NOT_ENOUGH_MEMORY)
      60             :     {
      61           0 :         throw SAXNotSupportedException("Out of memory");
      62             :     }
      63         939 :     else if (eErr != OGRERR_NONE)
      64             :     {
      65           0 :         throw SAXNotSupportedException("Other error during parsing");
      66             :     }
      67         939 : }
      68             : 
      69             : /************************************************************************/
      70             : /*                            startElement()                            */
      71             : /************************************************************************/
      72             : 
      73          19 : void GMLXercesHandler::startElement(const XMLCh *const /*uri*/,
      74             :                                     const XMLCh *const localname,
      75             :                                     const XMLCh *const /*qname*/,
      76             :                                     const Attributes &attrs)
      77             : {
      78          19 :     m_nEntityCounter = 0;
      79             : 
      80          19 :     transcode(localname, m_osElement);
      81             : 
      82          19 :     GMLXercesHandlerDealWithError(GMLHandler::startElement(
      83          19 :         m_osElement.c_str(), static_cast<int>(m_osElement.size()),
      84             :         const_cast<Attributes *>(&attrs)));
      85          19 : }
      86             : 
      87             : /************************************************************************/
      88             : /*                             endElement()                             */
      89             : /************************************************************************/
      90          15 : void GMLXercesHandler::endElement(const XMLCh *const /*uri*/,
      91             :                                   const XMLCh *const /*localname*/,
      92             :                                   const XMLCh *const /*qname */)
      93             : {
      94          15 :     m_nEntityCounter = 0;
      95             : 
      96          15 :     GMLXercesHandlerDealWithError(GMLHandler::endElement());
      97          15 : }
      98             : 
      99             : /************************************************************************/
     100             : /*                             characters()                             */
     101             : /************************************************************************/
     102             : 
     103         905 : void GMLXercesHandler::characters(const XMLCh *const chars_in,
     104             :                                   const XMLSize_t length)
     105             : 
     106             : {
     107         905 :     transcode(chars_in, m_osCharacters, static_cast<int>(length));
     108         905 :     GMLXercesHandlerDealWithError(GMLHandler::dataHandler(
     109         905 :         m_osCharacters.c_str(), static_cast<int>(m_osCharacters.size())));
     110         905 : }
     111             : 
     112             : /************************************************************************/
     113             : /*                             fatalError()                             */
     114             : /************************************************************************/
     115             : 
     116           0 : void GMLXercesHandler::fatalError(const SAXParseException &exception)
     117             : 
     118             : {
     119           0 :     CPLString osMsg;
     120           0 :     transcode(exception.getMessage(), osMsg);
     121           0 :     CPLError(CE_Failure, CPLE_AppDefined,
     122             :              "XML Parsing Error: %s at line %d, column %d\n", osMsg.c_str(),
     123           0 :              static_cast<int>(exception.getLineNumber()),
     124           0 :              static_cast<int>(exception.getColumnNumber()));
     125           0 : }
     126             : 
     127             : /************************************************************************/
     128             : /*                             startEntity()                            */
     129             : /************************************************************************/
     130             : 
     131        1001 : void GMLXercesHandler::startEntity(const XMLCh *const /* name */)
     132             : {
     133        1001 :     m_nEntityCounter++;
     134        1001 :     if (m_nEntityCounter > 1000 && !m_poReader->HasStoppedParsing())
     135             :     {
     136             :         throw SAXNotSupportedException(
     137           1 :             "File probably corrupted (million laugh pattern)");
     138             :     }
     139        1000 : }
     140             : 
     141             : /************************************************************************/
     142             : /*                               GetFID()                               */
     143             : /************************************************************************/
     144             : 
     145           1 : const char *GMLXercesHandler::GetFID(void *attr)
     146             : {
     147           1 :     const Attributes *attrs = static_cast<const Attributes *>(attr);
     148           1 :     const XMLCh achFID[] = {'f', 'i', 'd', '\0'};
     149           1 :     int nFIDIndex = attrs->getIndex(achFID);
     150           1 :     if (nFIDIndex != -1)
     151             :     {
     152           1 :         transcode(attrs->getValue(nFIDIndex), m_osFID);
     153           1 :         return m_osFID.c_str();
     154             :     }
     155             :     else
     156             :     {
     157           0 :         const XMLCh achGMLID[] = {'g', 'm', 'l', ':', 'i', 'd', '\0'};
     158           0 :         nFIDIndex = attrs->getIndex(achGMLID);
     159           0 :         if (nFIDIndex != -1)
     160             :         {
     161           0 :             transcode(attrs->getValue(nFIDIndex), m_osFID);
     162           0 :             return m_osFID.c_str();
     163             :         }
     164             :     }
     165             : 
     166           0 :     m_osFID.resize(0);
     167           0 :     return nullptr;
     168             : }
     169             : 
     170             : /************************************************************************/
     171             : /*                        AddAttributes()                               */
     172             : /************************************************************************/
     173             : 
     174           2 : CPLXMLNode *GMLXercesHandler::AddAttributes(CPLXMLNode *psNode, void *attr)
     175             : {
     176           2 :     const Attributes *attrs = static_cast<const Attributes *>(attr);
     177             : 
     178           2 :     CPLXMLNode *psLastChild = nullptr;
     179             : 
     180           5 :     for (unsigned int i = 0; i < attrs->getLength(); i++)
     181             :     {
     182           3 :         transcode(attrs->getQName(i), m_osAttrName);
     183           3 :         transcode(attrs->getValue(i), m_osAttrValue);
     184             : 
     185             :         CPLXMLNode *psChild =
     186           3 :             CPLCreateXMLNode(nullptr, CXT_Attribute, m_osAttrName.c_str());
     187           3 :         CPLCreateXMLNode(psChild, CXT_Text, m_osAttrValue.c_str());
     188             : 
     189           3 :         if (psLastChild == nullptr)
     190           1 :             psNode->psChild = psChild;
     191             :         else
     192           2 :             psLastChild->psNext = psChild;
     193           3 :         psLastChild = psChild;
     194             :     }
     195             : 
     196           2 :     return psLastChild;
     197             : }
     198             : 
     199             : /************************************************************************/
     200             : /*                    GetAttributeValue()                               */
     201             : /************************************************************************/
     202             : 
     203           8 : char *GMLXercesHandler::GetAttributeValue(void *attr,
     204             :                                           const char *pszAttributeName)
     205             : {
     206           8 :     const Attributes *attrs = static_cast<const Attributes *>(attr);
     207           8 :     for (unsigned int i = 0; i < attrs->getLength(); i++)
     208             :     {
     209           0 :         transcode(attrs->getQName(i), m_osAttrName);
     210           0 :         if (m_osAttrName == pszAttributeName)
     211             :         {
     212           0 :             transcode(attrs->getValue(i), m_osAttrValue);
     213           0 :             return CPLStrdup(m_osAttrValue);
     214             :         }
     215             :     }
     216           8 :     return nullptr;
     217             : }
     218             : 
     219             : /************************************************************************/
     220             : /*                    GetAttributeByIdx()                               */
     221             : /************************************************************************/
     222             : 
     223           9 : char *GMLXercesHandler::GetAttributeByIdx(void *attr, unsigned int idx,
     224             :                                           char **ppszKey)
     225             : {
     226           9 :     const Attributes *attrs = static_cast<const Attributes *>(attr);
     227           9 :     if (idx >= attrs->getLength())
     228             :     {
     229           9 :         *ppszKey = nullptr;
     230           9 :         return nullptr;
     231             :     }
     232           0 :     transcode(attrs->getQName(idx), m_osAttrName);
     233           0 :     transcode(attrs->getValue(idx), m_osAttrValue);
     234             : 
     235           0 :     *ppszKey = CPLStrdup(m_osAttrName);
     236           0 :     return CPLStrdup(m_osAttrValue);
     237             : }
     238             : 
     239             : #endif
     240             : 
     241             : #ifdef HAVE_EXPAT
     242             : 
     243             : /************************************************************************/
     244             : /*                            GMLExpatHandler()                         */
     245             : /************************************************************************/
     246             : 
     247         603 : GMLExpatHandler::GMLExpatHandler(GMLReader *poReader, XML_Parser oParser)
     248             :     : GMLHandler(poReader), m_oParser(oParser), m_bStopParsing(false),
     249         603 :       m_nDataHandlerCounter(0)
     250             : {
     251         603 : }
     252             : 
     253             : /************************************************************************/
     254             : /*                  GMLExpatHandler::DealWithError()                    */
     255             : /************************************************************************/
     256             : 
     257      368770 : void GMLExpatHandler::DealWithError(OGRErr eErr)
     258             : {
     259      368770 :     if (eErr != OGRERR_NONE)
     260             :     {
     261           2 :         m_bStopParsing = true;
     262           2 :         XML_StopParser(m_oParser, static_cast<XML_Bool>(false));
     263           2 :         if (eErr == OGRERR_NOT_ENOUGH_MEMORY)
     264           0 :             CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory");
     265             :     }
     266      368770 : }
     267             : 
     268             : /************************************************************************/
     269             : /*                           startElementCbk()                          */
     270             : /************************************************************************/
     271             : 
     272       61103 : void XMLCALL GMLExpatHandler::startElementCbk(void *pUserData,
     273             :                                               const char *pszName,
     274             :                                               const char **ppszAttr)
     275             : 
     276             : {
     277       61103 :     GMLExpatHandler *pThis = static_cast<GMLExpatHandler *>(pUserData);
     278       61103 :     if (pThis->m_bStopParsing)
     279           0 :         return;
     280             : 
     281       61103 :     const char *pszIter = pszName;
     282       61103 :     char ch = '\0';
     283      801218 :     while ((ch = *pszIter) != '\0')
     284             :     {
     285      740115 :         if (ch == ':')
     286       47923 :             pszName = pszIter + 1;
     287      740115 :         pszIter++;
     288             :     }
     289             : 
     290       61103 :     pThis->DealWithError(pThis->GMLHandler::startElement(
     291       61103 :         pszName, static_cast<int>(pszIter - pszName), ppszAttr));
     292             : }
     293             : 
     294             : /************************************************************************/
     295             : /*                            endElementCbk()                           */
     296             : /************************************************************************/
     297       60985 : void XMLCALL GMLExpatHandler::endElementCbk(void *pUserData,
     298             :                                             const char * /* pszName */)
     299             : {
     300       60985 :     GMLExpatHandler *pThis = static_cast<GMLExpatHandler *>(pUserData);
     301       60985 :     if (pThis->m_bStopParsing)
     302           1 :         return;
     303             : 
     304       60984 :     pThis->DealWithError(pThis->GMLHandler::endElement());
     305             : }
     306             : 
     307             : /************************************************************************/
     308             : /*                            dataHandlerCbk()                          */
     309             : /************************************************************************/
     310             : 
     311      246684 : void XMLCALL GMLExpatHandler::dataHandlerCbk(void *pUserData, const char *data,
     312             :                                              int nLen)
     313             : 
     314             : {
     315      246684 :     GMLExpatHandler *pThis = static_cast<GMLExpatHandler *>(pUserData);
     316      246684 :     if (pThis->m_bStopParsing)
     317           0 :         return;
     318             : 
     319      246684 :     pThis->m_nDataHandlerCounter++;
     320             :     // The size of the buffer that is fetched and that Expat parses is
     321             :     // PARSER_BUF_SIZE bytes. If the dataHandlerCbk() callback is called
     322             :     // more than PARSER_BUF_SIZE times, this means that one byte in the
     323             :     // file expands to more XML text fragments, which is the sign of a
     324             :     // likely abuse of <!ENTITY>
     325             :     // Note: the counter is zeroed by ResetDataHandlerCounter() before each
     326             :     // new XML parsing.
     327      246684 :     if (pThis->m_nDataHandlerCounter >= PARSER_BUF_SIZE)
     328             :     {
     329           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     330             :                  "File probably corrupted (million laugh pattern)");
     331           1 :         pThis->m_bStopParsing = true;
     332           1 :         XML_StopParser(pThis->m_oParser, static_cast<XML_Bool>(false));
     333           1 :         return;
     334             :     }
     335             : 
     336      246683 :     pThis->DealWithError(pThis->GMLHandler::dataHandler(data, nLen));
     337             : }
     338             : 
     339             : /************************************************************************/
     340             : /*                               GetFID()                               */
     341             : /************************************************************************/
     342             : 
     343        3336 : const char *GMLExpatHandler::GetFID(void *attr)
     344             : {
     345        3336 :     const char *const *papszIter = static_cast<const char *const *>(attr);
     346        3354 :     while (*papszIter)
     347             :     {
     348        1840 :         if (strcmp(*papszIter, "fid") == 0 || strcmp(*papszIter, "gml:id") == 0)
     349             :         {
     350        1822 :             return papszIter[1];
     351             :         }
     352          18 :         papszIter += 2;
     353             :     }
     354        1514 :     return nullptr;
     355             : }
     356             : 
     357             : /************************************************************************/
     358             : /*                        AddAttributes()                               */
     359             : /************************************************************************/
     360             : 
     361       34108 : CPLXMLNode *GMLExpatHandler::AddAttributes(CPLXMLNode *psNode, void *attr)
     362             : {
     363       34108 :     const char **papszIter = static_cast<const char **>(attr);
     364             : 
     365       34108 :     CPLXMLNode *psLastChild = nullptr;
     366             : 
     367       49220 :     while (*papszIter)
     368             :     {
     369             :         CPLXMLNode *psChild =
     370       15112 :             CPLCreateXMLNode(nullptr, CXT_Attribute, papszIter[0]);
     371       15112 :         CPLCreateXMLNode(psChild, CXT_Text, papszIter[1]);
     372             : 
     373       15112 :         if (psLastChild == nullptr)
     374       14079 :             psNode->psChild = psChild;
     375             :         else
     376        1033 :             psLastChild->psNext = psChild;
     377       15112 :         psLastChild = psChild;
     378             : 
     379       15112 :         papszIter += 2;
     380             :     }
     381             : 
     382       34108 :     return psLastChild;
     383             : }
     384             : 
     385             : /************************************************************************/
     386             : /*                    GetAttributeValue()                               */
     387             : /************************************************************************/
     388             : 
     389       11174 : char *GMLExpatHandler::GetAttributeValue(void *attr,
     390             :                                          const char *pszAttributeName)
     391             : {
     392       11174 :     const char *const *papszIter = static_cast<const char *const *>(attr);
     393       11788 :     while (*papszIter)
     394             :     {
     395         861 :         if (strcmp(*papszIter, pszAttributeName) == 0)
     396             :         {
     397         247 :             return CPLStrdup(papszIter[1]);
     398             :         }
     399         614 :         papszIter += 2;
     400             :     }
     401       10927 :     return nullptr;
     402             : }
     403             : 
     404             : /************************************************************************/
     405             : /*                    GetAttributeByIdx()                               */
     406             : /************************************************************************/
     407             : 
     408             : // CAUTION: should be called with increasing idx starting from 0 and
     409             : // no attempt to read beyond end of list.
     410       13926 : char *GMLExpatHandler::GetAttributeByIdx(void *attr, unsigned int idx,
     411             :                                          char **ppszKey)
     412             : {
     413       13926 :     const char *const *papszIter = static_cast<const char *const *>(attr);
     414       13926 :     if (papszIter[2 * idx] == nullptr)
     415             :     {
     416       13304 :         *ppszKey = nullptr;
     417       13304 :         return nullptr;
     418             :     }
     419         622 :     *ppszKey = CPLStrdup(papszIter[2 * idx]);
     420         622 :     return CPLStrdup(papszIter[2 * idx + 1]);
     421             : }
     422             : 
     423             : #endif
     424             : 
     425             : static const char *const apszGMLGeometryElements[] = {
     426             :     "BoundingBox", /* ows:BoundingBox */
     427             :     "CompositeCurve",
     428             :     "CompositeSurface",
     429             :     "Shell", /* CityGML 3 */
     430             :     "Curve",
     431             :     "GeometryCollection", /* OGR < 1.8.0 bug... */
     432             :     "LineString",
     433             :     "MultiCurve",
     434             :     "MultiGeometry",
     435             :     "MultiLineString",
     436             :     "MultiPoint",
     437             :     "MultiPolygon",
     438             :     "MultiSurface",
     439             :     "Point",
     440             :     "Polygon",
     441             :     "PolygonPatch",
     442             :     "PolyhedralSurface",
     443             :     "SimplePolygon",    /* GML 3.3 compact encoding */
     444             :     "SimpleRectangle",  /* GML 3.3 compact encoding */
     445             :     "SimpleTriangle",   /* GML 3.3 compact encoding */
     446             :     "SimpleMultiPoint", /* GML 3.3 compact encoding */
     447             :     "Solid",
     448             :     "Surface",
     449             :     "Tin",
     450             :     "TopoCurve",
     451             :     "TopoSurface",
     452             :     "Triangle",
     453             :     "TriangulatedSurface"};
     454             : 
     455             : #define GML_GEOMETRY_TYPE_COUNT                                                \
     456             :     static_cast<int>(sizeof(apszGMLGeometryElements) /                         \
     457             :                      sizeof(apszGMLGeometryElements[0]))
     458             : 
     459         107 : bool OGRGMLIsGeometryElement(const char *pszElement)
     460             : {
     461        3089 :     for (const auto &pszGMLElement : apszGMLGeometryElements)
     462             :     {
     463        2983 :         if (strcmp(pszElement, pszGMLElement) == 0)
     464           1 :             return true;
     465             :     }
     466         106 :     return false;
     467             : }
     468             : 
     469             : struct _GeometryNamesStruct
     470             : {
     471             :     unsigned long nHash;
     472             :     const char *pszName;
     473             : };
     474             : 
     475             : /************************************************************************/
     476             : /*                            GMLHandler()                              */
     477             : /************************************************************************/
     478             : 
     479         605 : GMLHandler::GMLHandler(GMLReader *poReader)
     480             :     : pasGeometryNames(static_cast<GeometryNamesStruct *>(
     481        1210 :           CPLMalloc(GML_GEOMETRY_TYPE_COUNT * sizeof(GeometryNamesStruct)))),
     482             :       m_nSRSDimensionIfMissing(
     483         605 :           atoi(CPLGetConfigOption("GML_SRS_DIMENSION_IF_MISSING", "0"))),
     484        1210 :       m_poReader(poReader), eAppSchemaType(APPSCHEMA_GENERIC), nStackDepth(0)
     485             : {
     486       17545 :     for (int i = 0; i < GML_GEOMETRY_TYPE_COUNT; i++)
     487             :     {
     488       16940 :         pasGeometryNames[i].pszName = apszGMLGeometryElements[i];
     489       33880 :         pasGeometryNames[i].nHash =
     490       16940 :             CPLHashSetHashStr(pasGeometryNames[i].pszName);
     491             :     }
     492         605 :     std::sort(pasGeometryNames, pasGeometryNames + GML_GEOMETRY_TYPE_COUNT,
     493      102850 :               [](const GeometryNamesStruct &a, const GeometryNamesStruct &b)
     494      102850 :               { return a.nHash < b.nHash; });
     495             : 
     496         605 :     stateStack[0] = STATE_TOP;
     497         605 : }
     498             : 
     499             : /************************************************************************/
     500             : /*                            ~GMLHandler()                             */
     501             : /************************************************************************/
     502             : 
     503         605 : GMLHandler::~GMLHandler()
     504             : 
     505             : {
     506         605 :     if (apsXMLNode.size() >= 2 && apsXMLNode[1].psNode != nullptr)
     507           4 :         CPLDestroyXMLNode(apsXMLNode[1].psNode);
     508             : 
     509         605 :     CPLFree(m_pszCurField);
     510         605 :     CPLFree(m_pszGeometry);
     511         605 :     CPLFree(m_pszCityGMLGenericAttrName);
     512         605 :     CPLFree(m_pszHref);
     513         605 :     CPLFree(m_pszUom);
     514         605 :     CPLFree(m_pszValue);
     515         605 :     CPLFree(m_pszKieli);
     516         605 :     CPLFree(pasGeometryNames);
     517         605 : }
     518             : 
     519             : /************************************************************************/
     520             : /*                             startElement()                           */
     521             : /************************************************************************/
     522             : 
     523       61122 : OGRErr GMLHandler::startElement(const char *pszName, int nLenName, void *attr)
     524             : {
     525             :     OGRErr eRet;
     526       61122 :     switch (stateStack[nStackDepth])
     527             :     {
     528         603 :         case STATE_TOP:
     529         603 :             eRet = startElementTop(pszName, nLenName, attr);
     530         603 :             break;
     531        7265 :         case STATE_DEFAULT:
     532        7265 :             eRet = startElementDefault(pszName, nLenName, attr);
     533        7265 :             break;
     534       13443 :         case STATE_FEATURE:
     535       13443 :             eRet = startElementFeatureAttribute(pszName, nLenName, attr);
     536       13443 :             break;
     537        3425 :         case STATE_PROPERTY:
     538        3425 :             eRet = startElementFeatureAttribute(pszName, nLenName, attr);
     539        3425 :             break;
     540         165 :         case STATE_FEATUREPROPERTY:
     541         165 :             eRet = startElementFeatureProperty(pszName, nLenName, attr);
     542         165 :             break;
     543       29892 :         case STATE_GEOMETRY:
     544       29892 :             eRet = startElementGeometry(pszName, nLenName, attr);
     545       29892 :             break;
     546        4003 :         case STATE_IGNORED_FEATURE:
     547        4003 :             eRet = OGRERR_NONE;
     548        4003 :             break;
     549        1194 :         case STATE_BOUNDED_BY:
     550        1194 :             eRet = startElementBoundedBy(pszName, nLenName, attr);
     551        1194 :             break;
     552        1090 :         case STATE_BOUNDED_BY_IN_FEATURE:
     553        1090 :             eRet = startElementGeometry(pszName, nLenName, attr);
     554        1090 :             break;
     555          42 :         case STATE_CITYGML_ATTRIBUTE:
     556          42 :             eRet = startElementCityGMLGenericAttr(pszName, nLenName, attr);
     557          42 :             break;
     558           0 :         default:
     559           0 :             eRet = OGRERR_NONE;
     560           0 :             break;
     561             :     }
     562       61122 :     m_nDepth++;
     563       61122 :     if (m_nDepth == 64)
     564             :     {
     565             :         // Avoid performance issues on files like
     566             :         // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=21737
     567           3 :         if (m_nUnlimitedDepth < 0)
     568             :         {
     569           3 :             m_nUnlimitedDepth = EQUAL(
     570             :                 CPLGetConfigOption("OGR_GML_NESTING_LEVEL", ""), "UNLIMITED");
     571             :         }
     572           3 :         if (!m_nUnlimitedDepth)
     573             :         {
     574           1 :             CPLError(CE_Failure, CPLE_NotSupported,
     575             :                      "Too deep XML nesting level (%d). "
     576             :                      "Set the OGR_GML_NESTING_LEVEL configuration option to "
     577             :                      "UNLIMITED to remove that limitation.",
     578             :                      m_nDepth);
     579           1 :             eRet = OGRERR_FAILURE;
     580             :         }
     581             :     }
     582       61122 :     return eRet;
     583             : }
     584             : 
     585             : /************************************************************************/
     586             : /*                              endElement()                            */
     587             : /************************************************************************/
     588             : 
     589       60999 : OGRErr GMLHandler::endElement()
     590             : {
     591       60999 :     m_nDepth--;
     592       60999 :     switch (stateStack[nStackDepth])
     593             :     {
     594           0 :         case STATE_TOP:
     595           0 :             break;
     596        4009 :         case STATE_DEFAULT:
     597        4009 :             return endElementDefault();
     598        6007 :         case STATE_FEATURE:
     599        6007 :             return endElementFeature();
     600       10730 :         case STATE_PROPERTY:
     601       10730 :             return endElementAttribute();
     602         189 :         case STATE_FEATUREPROPERTY:
     603         189 :             return endElementFeatureProperty();
     604       32989 :         case STATE_GEOMETRY:
     605       32989 :             return endElementGeometry();
     606        4348 :         case STATE_IGNORED_FEATURE:
     607        4348 :             return endElementIgnoredFeature();
     608        1477 :         case STATE_BOUNDED_BY:
     609        1477 :             return endElementBoundedBy();
     610        1166 :         case STATE_BOUNDED_BY_IN_FEATURE:
     611        1166 :             return endElementBoundedByInFeature();
     612          84 :         case STATE_CITYGML_ATTRIBUTE:
     613          84 :             return endElementCityGMLGenericAttr();
     614           0 :         default:
     615           0 :             break;
     616             :     }
     617           0 :     return OGRERR_NONE;
     618             : }
     619             : 
     620             : /************************************************************************/
     621             : /*                              dataHandler()                           */
     622             : /************************************************************************/
     623             : 
     624      247588 : OGRErr GMLHandler::dataHandler(const char *data, int nLen)
     625             : {
     626      247588 :     switch (stateStack[nStackDepth])
     627             :     {
     628      131872 :         case STATE_TOP:
     629             :         case STATE_DEFAULT:
     630             :         case STATE_FEATURE:
     631      131872 :             break;
     632       14364 :         case STATE_PROPERTY:
     633       14364 :             return dataHandlerAttribute(data, nLen);
     634         625 :         case STATE_FEATUREPROPERTY:
     635         625 :             break;
     636       92299 :         case STATE_GEOMETRY:
     637       92299 :             return dataHandlerGeometry(data, nLen);
     638        5876 :         case STATE_IGNORED_FEATURE:
     639             :         case STATE_BOUNDED_BY:
     640        5876 :             break;
     641        2414 :         case STATE_BOUNDED_BY_IN_FEATURE:
     642        2414 :             return dataHandlerGeometry(data, nLen);
     643         138 :         case STATE_CITYGML_ATTRIBUTE:
     644         138 :             return dataHandlerAttribute(data, nLen);
     645           0 :         default:
     646           0 :             break;
     647             :     }
     648      138373 :     return OGRERR_NONE;
     649             : }
     650             : 
     651             : #define PUSH_STATE(val)                                                        \
     652             :     do                                                                         \
     653             :     {                                                                          \
     654             :         nStackDepth++;                                                         \
     655             :         CPLAssert(nStackDepth < STACK_SIZE);                                   \
     656             :         stateStack[nStackDepth] = val;                                         \
     657             :     } while (false)
     658             : #define POP_STATE() nStackDepth--
     659             : 
     660             : /************************************************************************/
     661             : /*                       startElementBoundedBy()                        */
     662             : /************************************************************************/
     663             : 
     664        1194 : OGRErr GMLHandler::startElementBoundedBy(const char *pszName, int /*nLenName*/,
     665             :                                          void *attr)
     666             : {
     667        1194 :     if (m_nDepth == 2 && strcmp(pszName, "Envelope") == 0)
     668             :     {
     669         136 :         char *pszGlobalSRSName = GetAttributeValue(attr, "srsName");
     670         136 :         m_poReader->SetGlobalSRSName(pszGlobalSRSName);
     671         136 :         CPLFree(pszGlobalSRSName);
     672             : 
     673         136 :         if (m_nSRSDimensionIfMissing == 0)
     674             :         {
     675             :             char *pszGlobalSRSDimension =
     676         132 :                 GetAttributeValue(attr, "srsDimension");
     677         132 :             if (pszGlobalSRSDimension)
     678          15 :                 m_nSRSDimensionIfMissing = atoi(pszGlobalSRSDimension);
     679         132 :             CPLFree(pszGlobalSRSDimension);
     680             :         }
     681             :     }
     682             : 
     683        1194 :     return OGRERR_NONE;
     684             : }
     685             : 
     686             : /************************************************************************/
     687             : /*                       startElementGeometry()                         */
     688             : /************************************************************************/
     689             : 
     690       34111 : OGRErr GMLHandler::startElementGeometry(const char *pszName, int nLenName,
     691             :                                         void *attr)
     692             : {
     693       35201 :     if (stateStack[nStackDepth] == STATE_BOUNDED_BY_IN_FEATURE &&
     694        1090 :         apsXMLNode.empty())
     695             :     {
     696           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid <boundedBy> construct");
     697           1 :         return OGRERR_FAILURE;
     698             :     }
     699             : 
     700             :     /* Create new XML Element */
     701             :     CPLXMLNode *psCurNode =
     702       34110 :         static_cast<CPLXMLNode *>(CPLCalloc(sizeof(CPLXMLNode), 1));
     703       34110 :     psCurNode->eType = CXT_Element;
     704       34110 :     psCurNode->pszValue = static_cast<char *>(CPLMalloc(nLenName + 1));
     705       34110 :     memcpy(psCurNode->pszValue, pszName, nLenName + 1);
     706             : 
     707             :     /* Attach element as the last child of its parent */
     708       34110 :     NodeLastChild &sNodeLastChild = apsXMLNode.back();
     709       34110 :     CPLXMLNode *psLastChildParent = sNodeLastChild.psLastChild;
     710             : 
     711       34110 :     if (psLastChildParent == nullptr)
     712             :     {
     713       18198 :         CPLXMLNode *psParent = sNodeLastChild.psNode;
     714       18198 :         if (psParent)
     715       14991 :             psParent->psChild = psCurNode;
     716             :     }
     717             :     else
     718             :     {
     719       15912 :         psLastChildParent->psNext = psCurNode;
     720             :     }
     721       34110 :     sNodeLastChild.psLastChild = psCurNode;
     722             : 
     723             :     /* Add attributes to the element */
     724       34110 :     CPLXMLNode *psLastChildCurNode = AddAttributes(psCurNode, attr);
     725       49225 :     for (CPLXMLNode *psIter = psCurNode->psChild; psIter;
     726       15115 :          psIter = psIter->psNext)
     727             :     {
     728       15115 :         if (psIter->eType == CXT_Attribute &&
     729       15115 :             strcmp(psIter->pszValue, "xlink:href") == 0 &&
     730        1090 :             psIter->psChild->pszValue && psIter->psChild->pszValue[0] == '#')
     731             :         {
     732        1090 :             m_oMapElementToSubstitute[psIter->psChild->pszValue + 1] =
     733             :                 psCurNode;
     734             :         }
     735             :     }
     736             : 
     737             :     /* Some CityGML lack a srsDimension="3" in posList, such as in */
     738             :     /* http://www.citygml.org/fileadmin/count.php?f=fileadmin%2Fcitygml%2Fdocs%2FFrankfurt_Street_Setting_LOD3.zip
     739             :      */
     740             :     /* So we have to add it manually */
     741       71627 :     if (strcmp(pszName, "posList") == 0 &&
     742       35006 :         CPLGetXMLValue(psCurNode, "srsDimension", nullptr) == nullptr &&
     743         896 :         m_nSRSDimensionIfMissing != 0)
     744             :     {
     745             :         CPLXMLNode *psChild =
     746         118 :             CPLCreateXMLNode(nullptr, CXT_Attribute, "srsDimension");
     747         118 :         CPLCreateXMLNode(psChild, CXT_Text,
     748         118 :                          (m_nSRSDimensionIfMissing == 3) ? "3" : "2");
     749             : 
     750         118 :         if (psLastChildCurNode == nullptr)
     751         118 :             psCurNode->psChild = psChild;
     752             :         else
     753           0 :             psLastChildCurNode->psNext = psChild;
     754         118 :         psLastChildCurNode = psChild;
     755             :     }
     756             : 
     757             :     /* Push the element on the stack */
     758             :     NodeLastChild sNewNodeLastChild;
     759       34110 :     sNewNodeLastChild.psNode = psCurNode;
     760       34110 :     sNewNodeLastChild.psLastChild = psLastChildCurNode;
     761       34110 :     apsXMLNode.push_back(sNewNodeLastChild);
     762             : 
     763       34110 :     if (m_pszGeometry)
     764             :     {
     765           0 :         CPLFree(m_pszGeometry);
     766           0 :         m_pszGeometry = nullptr;
     767           0 :         m_nGeomAlloc = 0;
     768           0 :         m_nGeomLen = 0;
     769             :     }
     770             : 
     771       34110 :     return OGRERR_NONE;
     772             : }
     773             : 
     774             : /************************************************************************/
     775             : /*                    startElementCityGMLGenericAttr()                  */
     776             : /************************************************************************/
     777             : 
     778          42 : OGRErr GMLHandler::startElementCityGMLGenericAttr(const char *pszName,
     779             :                                                   int /*nLenName*/,
     780             :                                                   void * /*attr*/)
     781             : {
     782          42 :     if (strcmp(pszName, "value") == 0)
     783             :     {
     784          42 :         if (m_pszCurField)
     785             :         {
     786           0 :             CPLFree(m_pszCurField);
     787           0 :             m_pszCurField = nullptr;
     788           0 :             m_nCurFieldLen = 0;
     789           0 :             m_nCurFieldAlloc = 0;
     790             :         }
     791          42 :         m_bInCurField = true;
     792             :     }
     793             : 
     794          42 :     return OGRERR_NONE;
     795             : }
     796             : 
     797             : /************************************************************************/
     798             : /*                       DealWithAttributes()                           */
     799             : /************************************************************************/
     800             : 
     801       13313 : void GMLHandler::DealWithAttributes(const char *pszName, int nLenName,
     802             :                                     void *attr)
     803             : {
     804       13313 :     GMLReadState *poState = m_poReader->GetState();
     805       13313 :     GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
     806             : 
     807       13313 :     for (unsigned int idx = 0; true; idx++)
     808             :     {
     809       13935 :         char *pszAttrKey = nullptr;
     810             : 
     811       13935 :         char *pszAttrVal = GetAttributeByIdx(attr, idx, &pszAttrKey);
     812       13935 :         if (pszAttrVal == nullptr)
     813       13313 :             break;
     814             : 
     815         622 :         int nAttrIndex = 0;
     816         622 :         const char *pszAttrKeyNoNS = strchr(pszAttrKey, ':');
     817         622 :         if (pszAttrKeyNoNS != nullptr)
     818         149 :             pszAttrKeyNoNS++;
     819             : 
     820             :         /* If attribute is referenced by the .gfs */
     821         771 :         if (poClass->IsSchemaLocked() &&
     822          50 :             ((pszAttrKeyNoNS != nullptr &&
     823          50 :               (nAttrIndex = m_poReader->GetAttributeElementIndex(
     824         116 :                    pszName, nLenName, pszAttrKeyNoNS)) != -1) ||
     825         116 :              ((nAttrIndex = m_poReader->GetAttributeElementIndex(
     826             :                    pszName, nLenName, pszAttrKey)) != -1)))
     827             :         {
     828          67 :             nAttrIndex = FindRealPropertyByCheckingConditions(nAttrIndex, attr);
     829          67 :             if (nAttrIndex >= 0)
     830             :             {
     831          65 :                 m_poReader->SetFeaturePropertyDirectly(nullptr, pszAttrVal,
     832             :                                                        nAttrIndex);
     833          65 :                 pszAttrVal = nullptr;
     834             :             }
     835             :         }
     836             : 
     837             :         /* Hard-coded historic cases */
     838         555 :         else if (strcmp(pszAttrKey, "xlink:href") == 0)
     839             :         {
     840          14 :             if ((m_bReportHref || m_poReader->ReportAllAttributes()) &&
     841           7 :                 m_bInCurField)
     842             :             {
     843           4 :                 CPLFree(m_pszHref);
     844           4 :                 m_pszHref = pszAttrVal;
     845           4 :                 pszAttrVal = nullptr;
     846             :             }
     847           9 :             else if ((!poClass->IsSchemaLocked() &&
     848           9 :                       (m_bReportHref || m_poReader->ReportAllAttributes())) ||
     849           3 :                      (poClass->IsSchemaLocked() &&
     850           3 :                       (nAttrIndex = m_poReader->GetAttributeElementIndex(
     851           6 :                            (std::string(pszName) + "_href").c_str(),
     852             :                            nLenName + 5)) != -1))
     853             :             {
     854           3 :                 poState->PushPath(pszName, nLenName);
     855           3 :                 CPLString osPropNameHref = poState->osPath + "_href";
     856           3 :                 poState->PopPath();
     857           3 :                 m_poReader->SetFeaturePropertyDirectly(osPropNameHref,
     858             :                                                        pszAttrVal, nAttrIndex);
     859           3 :                 pszAttrVal = nullptr;
     860             :             }
     861             :         }
     862         548 :         else if (strcmp(pszAttrKey, "uom") == 0)
     863             :         {
     864          20 :             CPLFree(m_pszUom);
     865          20 :             m_pszUom = pszAttrVal;
     866          20 :             pszAttrVal = nullptr;
     867             :         }
     868         528 :         else if (strcmp(pszAttrKey, "value") == 0)
     869             :         {
     870          10 :             CPLFree(m_pszValue);
     871          10 :             m_pszValue = pszAttrVal;
     872          10 :             pszAttrVal = nullptr;
     873             :         }
     874             :         else /* Get language in 'kieli' attribute of 'teksti' element */
     875         518 :             if (eAppSchemaType == APPSCHEMA_MTKGML && nLenName == 6 &&
     876           7 :                 strcmp(pszName, "teksti") == 0 &&
     877           7 :                 strcmp(pszAttrKey, "kieli") == 0)
     878             :             {
     879           7 :                 CPLFree(m_pszKieli);
     880           7 :                 m_pszKieli = pszAttrVal;
     881           7 :                 pszAttrVal = nullptr;
     882             :             }
     883             : 
     884             :             /* Should we report all attributes ? */
     885         519 :             else if (m_poReader->ReportAllAttributes() &&
     886           8 :                      !poClass->IsSchemaLocked())
     887             :             {
     888           8 :                 poState->PushPath(pszName, nLenName);
     889           8 :                 CPLString osPropName = poState->osPath;
     890           8 :                 poState->PopPath();
     891             : 
     892           8 :                 m_poReader->SetFeaturePropertyDirectly(
     893             :                     CPLSPrintf("%s@%s", osPropName.c_str(),
     894             :                                pszAttrKeyNoNS ? pszAttrKeyNoNS : pszAttrKey),
     895             :                     pszAttrVal, -1);
     896           8 :                 pszAttrVal = nullptr;
     897             :             }
     898             : 
     899         622 :         CPLFree(pszAttrKey);
     900         622 :         CPLFree(pszAttrVal);
     901         622 :     }
     902             : 
     903             : #if 0
     904             :     if( poClass->IsSchemaLocked() )
     905             :     {
     906             :         poState->PushPath( pszName, nLenName );
     907             :         CPLString osPath = poState->osPath;
     908             :         poState->PopPath();
     909             :         /* Find fields that match an attribute that is missing */
     910             :         for(int i=0; i < poClass->GetPropertyCount(); i++ )
     911             :         {
     912             :             GMLPropertyDefn* poProp = poClass->GetProperty(i);
     913             :             const char* pszSrcElement = poProp->GetSrcElement();
     914             :             if( poProp->GetType() == OFTStringList &&
     915             :                 poProp->GetSrcElementLen() > osPath.size() &&
     916             :                 strncmp(pszSrcElement, osPath, osPath.size()) == 0 &&
     917             :                 pszSrcElement[osPath.size()] == '@' )
     918             :             {
     919             :                 char* pszAttrVal = GetAttributeValue(attr, pszSrcElement + osPath.size() + 1);
     920             :                 if( pszAttrVal == NULL )
     921             :                 {
     922             :                     const char* pszCond = poProp->GetCondition();
     923             :                     if( pszCond == NULL || IsConditionMatched(pszCond, attr) )
     924             :                     {
     925             :                         m_poReader->SetFeaturePropertyDirectly( NULL, CPLStrdup(""), i );
     926             :                     }
     927             :                 }
     928             :                 else
     929             :                     CPLFree(pszAttrVal);
     930             :             }
     931             :         }
     932             :     }
     933             : #endif
     934       13313 : }
     935             : 
     936             : /************************************************************************/
     937             : /*                        IsConditionMatched()                          */
     938             : /************************************************************************/
     939             : 
     940             : /* FIXME! 'and' / 'or' operators are evaluated left to right, without */
     941             : /* and precedence rules between them ! */
     942             : 
     943          15 : bool GMLHandler::IsConditionMatched(const char *pszCondition, void *attr)
     944             : {
     945          15 :     if (pszCondition == nullptr)
     946           0 :         return true;
     947             : 
     948          15 :     bool bSyntaxError = false;
     949          30 :     CPLString osCondAttr, osCondVal;
     950          15 :     const char *pszIter = pszCondition;
     951          15 :     bool bOpEqual = true;
     952          19 :     while (*pszIter == ' ')
     953           4 :         pszIter++;
     954          15 :     if (*pszIter != '@')
     955           0 :         bSyntaxError = true;
     956             :     else
     957             :     {
     958          15 :         pszIter++;
     959          75 :         while (*pszIter != '\0' && *pszIter != ' ' && *pszIter != '!' &&
     960          65 :                *pszIter != '=')
     961             :         {
     962          60 :             osCondAttr += *pszIter;
     963          60 :             pszIter++;
     964             :         }
     965          15 :         while (*pszIter == ' ')
     966           0 :             pszIter++;
     967             : 
     968          15 :         if (*pszIter == '!')
     969             :         {
     970          10 :             bOpEqual = false;
     971          10 :             pszIter++;
     972             :         }
     973             : 
     974          15 :         if (*pszIter != '=')
     975           0 :             bSyntaxError = true;
     976             :         else
     977             :         {
     978          15 :             pszIter++;
     979          15 :             while (*pszIter == ' ')
     980           0 :                 pszIter++;
     981          15 :             if (*pszIter != '\'')
     982           0 :                 bSyntaxError = true;
     983             :             else
     984             :             {
     985          15 :                 pszIter++;
     986          45 :                 while (*pszIter != '\0' && *pszIter != '\'')
     987             :                 {
     988          30 :                     osCondVal += *pszIter;
     989          30 :                     pszIter++;
     990             :                 }
     991          15 :                 if (*pszIter != '\'')
     992           0 :                     bSyntaxError = true;
     993             :                 else
     994             :                 {
     995          15 :                     pszIter++;
     996          21 :                     while (*pszIter == ' ')
     997           6 :                         pszIter++;
     998             :                 }
     999             :             }
    1000             :         }
    1001             :     }
    1002             : 
    1003          15 :     if (bSyntaxError)
    1004             :     {
    1005           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1006             :                  "Invalid condition : %s. Must be of the form "
    1007             :                  "@attrname[!]='attrvalue' [and|or other_cond]*. "
    1008             :                  "'and' and 'or' operators cannot be mixed",
    1009             :                  pszCondition);
    1010           0 :         return false;
    1011             :     }
    1012             : 
    1013          15 :     char *pszVal = GetAttributeValue(attr, osCondAttr);
    1014          15 :     if (pszVal == nullptr)
    1015           0 :         pszVal = CPLStrdup("");
    1016          28 :     const bool bCondMet = (bOpEqual && strcmp(pszVal, osCondVal) == 0) ||
    1017          13 :                           (!bOpEqual && strcmp(pszVal, osCondVal) != 0);
    1018          15 :     CPLFree(pszVal);
    1019          15 :     if (*pszIter == '\0')
    1020           9 :         return bCondMet;
    1021             : 
    1022           6 :     if (STARTS_WITH(pszIter, "and"))
    1023             :     {
    1024           6 :         pszIter += 3;
    1025           6 :         if (!bCondMet)
    1026           2 :             return false;
    1027           4 :         return IsConditionMatched(pszIter, attr);
    1028             :     }
    1029             : 
    1030           0 :     if (STARTS_WITH(pszIter, "or"))
    1031             :     {
    1032           0 :         pszIter += 2;
    1033           0 :         if (bCondMet)
    1034           0 :             return true;
    1035           0 :         return IsConditionMatched(pszIter, attr);
    1036             :     }
    1037             : 
    1038           0 :     CPLError(
    1039             :         CE_Failure, CPLE_NotSupported,
    1040             :         "Invalid condition : %s. Must be of the form @attrname[!]='attrvalue' "
    1041             :         "[and|or other_cond]*. 'and' and 'or' operators cannot be mixed",
    1042             :         pszCondition);
    1043           0 :     return false;
    1044             : }
    1045             : 
    1046             : /************************************************************************/
    1047             : /*                FindRealPropertyByCheckingConditions()                */
    1048             : /************************************************************************/
    1049             : 
    1050        3086 : int GMLHandler::FindRealPropertyByCheckingConditions(int nIdx, void *attr)
    1051             : {
    1052        3086 :     GMLReadState *poState = m_poReader->GetState();
    1053        3086 :     GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
    1054             : 
    1055        3086 :     GMLPropertyDefn *poProp = poClass->GetProperty(nIdx);
    1056        3086 :     const char *pszCond = poProp->GetCondition();
    1057        3086 :     if (pszCond != nullptr && !IsConditionMatched(pszCond, attr))
    1058             :     {
    1059             :         /* try other attributes with same source element, but with different */
    1060             :         /* condition */
    1061           4 :         const char *pszSrcElement = poProp->GetSrcElement();
    1062           4 :         nIdx = -1;
    1063          11 :         for (int i = m_nAttributeIndex + 1; i < poClass->GetPropertyCount();
    1064             :              i++)
    1065             :         {
    1066           9 :             poProp = poClass->GetProperty(i);
    1067           9 :             if (strcmp(poProp->GetSrcElement(), pszSrcElement) == 0)
    1068             :             {
    1069           5 :                 pszCond = poProp->GetCondition();
    1070           5 :                 if (IsConditionMatched(pszCond, attr))
    1071             :                 {
    1072           2 :                     nIdx = i;
    1073           2 :                     break;
    1074             :                 }
    1075             :             }
    1076             :         }
    1077             :     }
    1078        3086 :     return nIdx;
    1079             : }
    1080             : 
    1081             : /************************************************************************/
    1082             : /*                      startElementFeatureAttribute()                  */
    1083             : /************************************************************************/
    1084             : 
    1085       16868 : OGRErr GMLHandler::startElementFeatureAttribute(const char *pszName,
    1086             :                                                 int nLenName, void *attr)
    1087             : {
    1088             : 
    1089       16868 :     GMLReadState *poState = m_poReader->GetState();
    1090             : 
    1091       19696 :     if (m_bInCurField && m_nAttributeIndex >= 0 &&
    1092       19696 :         std::string_view(pszName, nLenName) == "timePosition")
    1093             :     {
    1094           6 :         poState->PushPath(pszName, nLenName);
    1095           6 :         return OGRERR_NONE;
    1096             :     }
    1097             : 
    1098             :     /* Reset flag */
    1099       16862 :     m_bInCurField = false;
    1100             : 
    1101             :     /* -------------------------------------------------------------------- */
    1102             :     /*      If we are collecting geometry, or if we determine this is a     */
    1103             :     /*      geometry element then append to the geometry info.              */
    1104             :     /* -------------------------------------------------------------------- */
    1105       16862 :     if (IsGeometryElement(pszName))
    1106             :     {
    1107             :         bool bReadGeometry;
    1108             : 
    1109             :         /* If the <GeometryElementPath> is defined in the .gfs, use it */
    1110             :         /* to read the appropriate geometry element */
    1111        3134 :         GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
    1112        3134 :         m_nGeometryPropertyIndex = 0;
    1113        4428 :         if (poClass->IsSchemaLocked() &&
    1114        1294 :             poClass->GetGeometryPropertyCount() == 0)
    1115             :         {
    1116           0 :             bReadGeometry = false;
    1117             :         }
    1118        4428 :         else if (poClass->IsSchemaLocked() &&
    1119        4428 :                  poClass->GetGeometryPropertyCount() == 1 &&
    1120        1132 :                  poClass->GetGeometryProperty(0)->GetSrcElement()[0] == '\0')
    1121             :         {
    1122          70 :             bReadGeometry = true;
    1123             :         }
    1124        4288 :         else if (poClass->IsSchemaLocked() &&
    1125        1224 :                  poClass->GetGeometryPropertyCount() > 0)
    1126             :         {
    1127        1224 :             m_nGeometryPropertyIndex =
    1128        1224 :                 poClass->GetGeometryPropertyIndexBySrcElement(
    1129             :                     poState->osPath.c_str());
    1130        1224 :             bReadGeometry = (m_nGeometryPropertyIndex >= 0);
    1131             :         }
    1132        1840 :         else if (m_poReader->FetchAllGeometries())
    1133             :         {
    1134           0 :             bReadGeometry = true;
    1135             :         }
    1136        1840 :         else if (!poClass->IsSchemaLocked() && m_poReader->IsWFSJointLayer())
    1137             :         {
    1138          12 :             m_nGeometryPropertyIndex =
    1139          12 :                 poClass->GetGeometryPropertyIndexBySrcElement(
    1140             :                     poState->osPath.c_str());
    1141          12 :             if (m_nGeometryPropertyIndex < 0)
    1142             :             {
    1143           4 :                 const char *pszElement = poState->osPath.c_str();
    1144           4 :                 CPLString osFieldName;
    1145             :                 /* Strip member| prefix. Should always be true normally */
    1146           4 :                 if (STARTS_WITH(pszElement, "member|"))
    1147           4 :                     osFieldName = pszElement + strlen("member|");
    1148             : 
    1149             :                 /* Replace layer|property by layer_property */
    1150           4 :                 size_t iPos = osFieldName.find('|');
    1151           4 :                 if (iPos != std::string::npos)
    1152           4 :                     osFieldName[iPos] = '.';
    1153             : 
    1154           8 :                 poClass->AddGeometryProperty(new GMLGeometryPropertyDefn(
    1155           4 :                     osFieldName, poState->osPath.c_str(), wkbUnknown, -1,
    1156           8 :                     true));
    1157           4 :                 m_nGeometryPropertyIndex = poClass->GetGeometryPropertyCount();
    1158             :             }
    1159          12 :             bReadGeometry = true;
    1160             :         }
    1161             :         else
    1162             :         {
    1163             :             /* AIXM special case: for RouteSegment, we only want to read Curve
    1164             :              * geometries */
    1165             :             /* not 'start' and 'end' geometries */
    1166        1840 :             if (eAppSchemaType == APPSCHEMA_AIXM &&
    1167          12 :                 strcmp(poState->m_poFeature->GetClass()->GetName(),
    1168             :                        "RouteSegment") == 0)
    1169           0 :                 bReadGeometry = strcmp(pszName, "Curve") == 0;
    1170             : 
    1171             :             // AIXM special case: we want to read both horizontalProjection_location and
    1172             :             // horizontalProjection_linearExtent
    1173        1840 :             else if (eAppSchemaType == APPSCHEMA_AIXM &&
    1174          12 :                      STARTS_WITH(poState->m_poFeature->GetClass()->GetName(),
    1175             :                                  "horizontalProjection_") == 0)
    1176          12 :                 bReadGeometry =
    1177          12 :                     STARTS_WITH(pszName, "horizontalProjection_") == 0;
    1178             : 
    1179             :             /* For Inspire objects : the "main" geometry is in a <geometry>
    1180             :              * element */
    1181        1816 :             else if (m_bAlreadyFoundGeometry)
    1182           2 :                 bReadGeometry = false;
    1183        1814 :             else if (strcmp(poState->osPath.c_str(), "geometry") == 0)
    1184             :             {
    1185           8 :                 m_bAlreadyFoundGeometry = true;
    1186           8 :                 bReadGeometry = true;
    1187           8 :                 m_nGeometryPropertyIndex =
    1188           8 :                     poClass->GetGeometryPropertyIndexBySrcElement(
    1189             :                         poState->osPath.c_str());
    1190           8 :                 if (m_nGeometryPropertyIndex < 0)
    1191             :                 {
    1192           6 :                     poClass->AddGeometryProperty(new GMLGeometryPropertyDefn(
    1193           3 :                         "geometry", poState->osPath.c_str(), wkbUnknown, -1,
    1194           3 :                         true));
    1195           3 :                     m_nGeometryPropertyIndex =
    1196           3 :                         poClass->GetGeometryPropertyCount();
    1197             :                 }
    1198             :             }
    1199             :             else
    1200             :             {
    1201        3612 :                 if (!poClass->IsSchemaLocked() &&
    1202        1806 :                     poClass->IsConsistentSingleGeomElemPath())
    1203             :                 {
    1204        1796 :                     m_osLastGeomPathInCurFeature = poState->osPath;
    1205             :                 }
    1206        1806 :                 bReadGeometry = true;
    1207             :             }
    1208             :         }
    1209        3134 :         if (bReadGeometry)
    1210             :         {
    1211        3129 :             m_nGeometryDepth = m_nDepth;
    1212             : 
    1213        3129 :             CPLAssert(apsXMLNode.empty());
    1214             : 
    1215             :             NodeLastChild sNodeLastChild;
    1216        3129 :             sNodeLastChild.psNode = nullptr;
    1217        3129 :             sNodeLastChild.psLastChild = nullptr;
    1218        3129 :             apsXMLNode.push_back(sNodeLastChild);
    1219             : 
    1220        3129 :             PUSH_STATE(STATE_GEOMETRY);
    1221             : 
    1222        3129 :             return startElementGeometry(pszName, nLenName, attr);
    1223             :         }
    1224             :     }
    1225       13728 :     else if (
    1226       12363 :         (nLenName == 9 || nLenName == 8) &&
    1227        2548 :         (strcmp(pszName, "boundedBy") == 0 ||
    1228       27465 :          strcmp(pszName, "boundary") == 0) &&
    1229             :         // We ignore the UseBBOX() flag for CityGML, since CityGML
    1230             :         // has elements like bldg:boundedBy or 'boundary', which are not a simple
    1231             :         // rectangular bbox. This is needed to read properly
    1232             :         // autotest/ogr/data/gml/citygml_lod2_713_5322.xml
    1233             :         // (this is a workaround of not being namespace aware)
    1234         967 :         (eAppSchemaType == APPSCHEMA_CITYGML || m_poReader->UseBBOX()))
    1235             :     {
    1236          78 :         m_inBoundedByDepth = m_nDepth;
    1237             : 
    1238          78 :         CPLAssert(apsXMLNode.empty());
    1239             : 
    1240             :         NodeLastChild sNodeLastChild;
    1241          78 :         sNodeLastChild.psNode = nullptr;
    1242          78 :         sNodeLastChild.psLastChild = nullptr;
    1243          78 :         apsXMLNode.push_back(sNodeLastChild);
    1244             : 
    1245          78 :         PUSH_STATE(STATE_BOUNDED_BY_IN_FEATURE);
    1246             : 
    1247          78 :         return OGRERR_NONE;
    1248             :     }
    1249             : 
    1250             :     /* -------------------------------------------------------------------- */
    1251             :     /*      Is it a CityGML generic attribute ?                             */
    1252             :     /* -------------------------------------------------------------------- */
    1253       13786 :     else if (eAppSchemaType == APPSCHEMA_CITYGML &&
    1254         136 :              m_poReader->IsCityGMLGenericAttributeElement(pszName, attr))
    1255             :     {
    1256          42 :         CPLFree(m_pszCityGMLGenericAttrName);
    1257          42 :         m_pszCityGMLGenericAttrName = GetAttributeValue(attr, "name");
    1258          42 :         m_inCityGMLGenericAttrDepth = m_nDepth;
    1259             : 
    1260          42 :         PUSH_STATE(STATE_CITYGML_ATTRIBUTE);
    1261             : 
    1262          42 :         return OGRERR_NONE;
    1263             :     }
    1264             : 
    1265       13608 :     else if (m_poReader->IsWFSJointLayer() && m_nDepth == m_nDepthFeature + 1)
    1266             :     {
    1267             :     }
    1268             : 
    1269       13484 :     else if (m_poReader->IsWFSJointLayer() && m_nDepth == m_nDepthFeature + 2)
    1270             :     {
    1271         124 :         const char *pszFID = GetFID(attr);
    1272         124 :         if (pszFID)
    1273             :         {
    1274         124 :             poState->PushPath(pszName, nLenName);
    1275         248 :             CPLString osPropPath = poState->osPath + "@id";
    1276         124 :             poState->PopPath();
    1277         124 :             m_poReader->SetFeaturePropertyDirectly(osPropPath,
    1278             :                                                    CPLStrdup(pszFID), -1);
    1279             :         }
    1280             :     }
    1281             : 
    1282             :     /* -------------------------------------------------------------------- */
    1283             :     /*      If it is (or at least potentially is) a simple attribute,       */
    1284             :     /*      then start collecting it.                                       */
    1285             :     /* -------------------------------------------------------------------- */
    1286       13360 :     else if ((m_nAttributeIndex = m_poReader->GetAttributeElementIndex(
    1287       13360 :                   pszName, nLenName)) != -1)
    1288             :     {
    1289       10809 :         GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
    1290       16894 :         if (poClass->IsSchemaLocked() &&
    1291        3043 :             (poClass->GetProperty(m_nAttributeIndex)->GetType() ==
    1292        3042 :                  GMLPT_FeatureProperty ||
    1293        3042 :              poClass->GetProperty(m_nAttributeIndex)->GetType() ==
    1294             :                  GMLPT_FeaturePropertyList))
    1295             :         {
    1296          24 :             m_nAttributeDepth = m_nDepth;
    1297          24 :             PUSH_STATE(STATE_FEATUREPROPERTY);
    1298             :         }
    1299             :         else
    1300             :         {
    1301             :             /* Is this a property with a condition on an attribute value ? */
    1302       10785 :             if (poClass->IsSchemaLocked())
    1303             :             {
    1304        3019 :                 m_nAttributeIndex = FindRealPropertyByCheckingConditions(
    1305             :                     m_nAttributeIndex, attr);
    1306             :             }
    1307             : 
    1308       10785 :             if (m_nAttributeIndex >= 0)
    1309             :             {
    1310       10785 :                 if (m_pszCurField)
    1311             :                 {
    1312        1277 :                     CPLFree(m_pszCurField);
    1313        1277 :                     m_pszCurField = nullptr;
    1314        1277 :                     m_nCurFieldLen = 0;
    1315        1277 :                     m_nCurFieldAlloc = 0;
    1316             :                 }
    1317       10785 :                 m_bInCurField = true;
    1318             : 
    1319       10785 :                 char *pszXSINil = GetAttributeValue(attr, "xsi:nil");
    1320       10785 :                 if (pszXSINil)
    1321             :                 {
    1322          23 :                     if (EQUAL(pszXSINil, "true"))
    1323          23 :                         m_poReader->SetFeaturePropertyDirectly(
    1324             :                             pszName, CPLStrdup(OGR_GML_NULL), -1);
    1325          23 :                     CPLFree(pszXSINil);
    1326             :                 }
    1327             :                 else
    1328             :                 {
    1329       10762 :                     DealWithAttributes(pszName, nLenName, attr);
    1330             :                 }
    1331             : 
    1332       10785 :                 if (stateStack[nStackDepth] != STATE_PROPERTY)
    1333             :                 {
    1334        8885 :                     m_nAttributeDepth = m_nDepth;
    1335        8885 :                     PUSH_STATE(STATE_PROPERTY);
    1336             :                 }
    1337             :             }
    1338             :             /*else
    1339             :             {
    1340             :                 DealWithAttributes(pszName, nLenName, attr);
    1341             :             }*/
    1342             :         }
    1343             :     }
    1344             :     else
    1345             :     {
    1346        2551 :         DealWithAttributes(pszName, nLenName, attr);
    1347             :     }
    1348             : 
    1349       13613 :     poState->PushPath(pszName, nLenName);
    1350             : 
    1351       13613 :     return OGRERR_NONE;
    1352             : }
    1353             : 
    1354             : /************************************************************************/
    1355             : /*                         startElementTop()                            */
    1356             : /************************************************************************/
    1357             : 
    1358         603 : OGRErr GMLHandler::startElementTop(const char *pszName, int /*nLenName*/,
    1359             :                                    void *attr)
    1360             : {
    1361         603 :     if (strcmp(pszName, "CityModel") == 0)
    1362             :     {
    1363          10 :         eAppSchemaType = APPSCHEMA_CITYGML;
    1364             :         // Default to 3D geometries for CityGML (#6989)
    1365          10 :         if (m_nSRSDimensionIfMissing == 0)
    1366          10 :             m_nSRSDimensionIfMissing = 3;
    1367             :     }
    1368         593 :     else if (strcmp(pszName, "AIXMBasicMessage") == 0)
    1369             :     {
    1370           7 :         eAppSchemaType = APPSCHEMA_AIXM;
    1371           7 :         m_bReportHref = true;
    1372             :     }
    1373         586 :     else if (strcmp(pszName, "Maastotiedot") == 0)
    1374             :     {
    1375           8 :         eAppSchemaType = APPSCHEMA_MTKGML;
    1376             : 
    1377           8 :         char *pszSRSName = GetAttributeValue(attr, "srsName");
    1378           8 :         m_poReader->SetGlobalSRSName(pszSRSName);
    1379           8 :         CPLFree(pszSRSName);
    1380             : 
    1381           8 :         m_bReportHref = true;
    1382             : 
    1383             :         /* the schemas of MTKGML don't have (string) width, so don't set it */
    1384           8 :         m_poReader->SetWidthFlag(false);
    1385             :     }
    1386             : 
    1387         603 :     stateStack[0] = STATE_DEFAULT;
    1388             : 
    1389         603 :     return OGRERR_NONE;
    1390             : }
    1391             : 
    1392             : /************************************************************************/
    1393             : /*                        startElementDefault()                         */
    1394             : /************************************************************************/
    1395             : 
    1396        7265 : OGRErr GMLHandler::startElementDefault(const char *pszName, int nLenName,
    1397             :                                        void *attr)
    1398             : 
    1399             : {
    1400             :     /* -------------------------------------------------------------------- */
    1401             :     /*      Is it a feature?  If so push a whole new state, and return.     */
    1402             :     /* -------------------------------------------------------------------- */
    1403             :     int nClassIndex;
    1404        7265 :     const char *pszFilteredClassName = nullptr;
    1405             : 
    1406        7265 :     if (nLenName == 9 && strcmp(pszName, "boundedBy") == 0)
    1407             :     {
    1408         283 :         m_inBoundedByDepth = m_nDepth;
    1409             : 
    1410         283 :         PUSH_STATE(STATE_BOUNDED_BY);
    1411             : 
    1412         283 :         return OGRERR_NONE;
    1413             :     }
    1414             : 
    1415        7803 :     else if (m_poReader->ShouldLookForClassAtAnyLevel() &&
    1416         821 :              (pszFilteredClassName = m_poReader->GetFilteredClassName()) !=
    1417             :                  nullptr)
    1418             :     {
    1419         821 :         if (strcmp(pszName, pszFilteredClassName) == 0)
    1420             :         {
    1421          49 :             m_poReader->PushFeature(pszName, GetFID(attr),
    1422          49 :                                     m_poReader->GetFilteredClassIndex());
    1423             : 
    1424          49 :             m_nDepthFeature = m_nDepth;
    1425             : 
    1426          49 :             PUSH_STATE(STATE_FEATURE);
    1427             : 
    1428          49 :             return OGRERR_NONE;
    1429             :         }
    1430             :     }
    1431             : 
    1432             :     /* WFS 2.0 GetFeature documents have a wfs:FeatureCollection */
    1433             :     /* as a wfs:member of the top wfs:FeatureCollection. We don't want this */
    1434             :     /* wfs:FeatureCollection to be recognized as a feature */
    1435        6189 :     else if ((!(nLenName == static_cast<int>(strlen("FeatureCollection")) &&
    1436       12308 :                 strcmp(pszName, "FeatureCollection") == 0)) &&
    1437        6147 :              (nClassIndex = m_poReader->GetFeatureElementIndex(
    1438             :                   pszName, nLenName, eAppSchemaType)) != -1)
    1439             :     {
    1440        3507 :         m_bAlreadyFoundGeometry = false;
    1441             : 
    1442        3507 :         pszFilteredClassName = m_poReader->GetFilteredClassName();
    1443        3507 :         if (pszFilteredClassName != nullptr &&
    1444         377 :             strcmp(pszName, pszFilteredClassName) != 0)
    1445             :         {
    1446         345 :             m_nDepthFeature = m_nDepth;
    1447             : 
    1448         345 :             PUSH_STATE(STATE_IGNORED_FEATURE);
    1449             : 
    1450         345 :             return OGRERR_NONE;
    1451             :         }
    1452             :         else
    1453             :         {
    1454        3162 :             if (eAppSchemaType == APPSCHEMA_MTKGML)
    1455             :             {
    1456          22 :                 m_poReader->PushFeature(pszName, nullptr, nClassIndex);
    1457             : 
    1458          22 :                 char *pszGID = GetAttributeValue(attr, "gid");
    1459          22 :                 if (pszGID)
    1460          21 :                     m_poReader->SetFeaturePropertyDirectly("gid", pszGID, -1,
    1461             :                                                            GMLPT_String);
    1462             :             }
    1463             :             else
    1464        3140 :                 m_poReader->PushFeature(pszName, GetFID(attr), nClassIndex);
    1465             : 
    1466        3162 :             m_nDepthFeature = m_nDepth;
    1467             : 
    1468        3162 :             PUSH_STATE(STATE_FEATURE);
    1469             : 
    1470        3162 :             return OGRERR_NONE;
    1471             :         }
    1472             :     }
    1473             : 
    1474             :     /* -------------------------------------------------------------------- */
    1475             :     /*      Push the element onto the current state's path.                 */
    1476             :     /* -------------------------------------------------------------------- */
    1477        3426 :     m_poReader->GetState()->PushPath(pszName, nLenName);
    1478             : 
    1479        3426 :     return OGRERR_NONE;
    1480             : }
    1481             : 
    1482             : /************************************************************************/
    1483             : /*                      endElementIgnoredFeature()                      */
    1484             : /************************************************************************/
    1485             : 
    1486        4348 : OGRErr GMLHandler::endElementIgnoredFeature()
    1487             : 
    1488             : {
    1489        4348 :     if (m_nDepth == m_nDepthFeature)
    1490             :     {
    1491         345 :         POP_STATE();
    1492             :     }
    1493        4348 :     return OGRERR_NONE;
    1494             : }
    1495             : 
    1496             : /************************************************************************/
    1497             : /*                         endElementBoundedBy()                        */
    1498             : /************************************************************************/
    1499        1477 : OGRErr GMLHandler::endElementBoundedBy()
    1500             : 
    1501             : {
    1502        1477 :     if (m_inBoundedByDepth == m_nDepth)
    1503             :     {
    1504         283 :         POP_STATE();
    1505             :     }
    1506             : 
    1507        1477 :     return OGRERR_NONE;
    1508             : }
    1509             : 
    1510             : /************************************************************************/
    1511             : /*                     endElementBoundedByInFeature()                   */
    1512             : /************************************************************************/
    1513        1166 : OGRErr GMLHandler::endElementBoundedByInFeature()
    1514             : 
    1515             : {
    1516        1166 :     if (m_nDepth > m_inBoundedByDepth)
    1517             :     {
    1518        1089 :         if (m_nDepth == m_inBoundedByDepth + 1)
    1519             :         {
    1520          78 :             m_nGeometryDepth = m_nDepth;
    1521             :         }
    1522        1089 :         return endElementGeometry();
    1523             :     }
    1524             :     else
    1525             :     {
    1526          77 :         POP_STATE();
    1527          77 :         if (apsXMLNode.size() >= 2 && apsXMLNode[1].psNode != nullptr)
    1528           0 :             CPLDestroyXMLNode(apsXMLNode[1].psNode);
    1529          77 :         apsXMLNode.clear();
    1530          77 :         return OGRERR_NONE;
    1531             :     }
    1532             : }
    1533             : 
    1534             : /************************************************************************/
    1535             : /*                      ParseAIXMElevationProperties()                  */
    1536             : /************************************************************************/
    1537             : 
    1538          10 : void GMLHandler::ParseAIXMElevationProperties(const CPLXMLNode *psGML)
    1539             : {
    1540          10 :     if (const char *pszElevation = CPLGetXMLValue(psGML, "elevation", nullptr))
    1541             :     {
    1542           7 :         m_poReader->SetFeaturePropertyDirectly("elevation",
    1543             :                                                CPLStrdup(pszElevation), -1);
    1544             :         const char *pszElevationUnit =
    1545           7 :             CPLGetXMLValue(psGML, "elevation.uom", nullptr);
    1546           7 :         if (pszElevationUnit)
    1547             :         {
    1548           5 :             m_poReader->SetFeaturePropertyDirectly(
    1549             :                 "elevation_uom", CPLStrdup(pszElevationUnit), -1);
    1550             :         }
    1551             :     }
    1552             : 
    1553          10 :     if (const char *pszGeoidUndulation =
    1554          10 :             CPLGetXMLValue(psGML, "geoidUndulation", nullptr))
    1555             :     {
    1556           0 :         m_poReader->SetFeaturePropertyDirectly(
    1557             :             "geoidUndulation", CPLStrdup(pszGeoidUndulation), -1);
    1558             :         const char *pszGeoidUndulationUnit =
    1559           0 :             CPLGetXMLValue(psGML, "geoidUndulation.uom", nullptr);
    1560           0 :         if (pszGeoidUndulationUnit)
    1561             :         {
    1562           0 :             m_poReader->SetFeaturePropertyDirectly(
    1563             :                 "geoidUndulation_uom", CPLStrdup(pszGeoidUndulationUnit), -1);
    1564             :         }
    1565             :     }
    1566             : 
    1567          10 :     if (const char *pszVerticalDatum =
    1568          10 :             CPLGetXMLValue(psGML, "verticalDatum", nullptr))
    1569             :     {
    1570           3 :         m_poReader->SetFeaturePropertyDirectly("verticalDatum",
    1571             :                                                CPLStrdup(pszVerticalDatum), -1);
    1572             :     }
    1573             : 
    1574          10 :     if (const char *pszVerticalAccuracy =
    1575          10 :             CPLGetXMLValue(psGML, "verticalAccuracy", nullptr))
    1576             :     {
    1577           5 :         m_poReader->SetFeaturePropertyDirectly(
    1578             :             "verticalAccuracy", CPLStrdup(pszVerticalAccuracy), -1);
    1579             :         const char *pszVerticalAccuracyUnit =
    1580           5 :             CPLGetXMLValue(psGML, "verticalAccuracy.uom", nullptr);
    1581           5 :         if (pszVerticalAccuracyUnit)
    1582             :         {
    1583           3 :             m_poReader->SetFeaturePropertyDirectly(
    1584             :                 "verticalAccuracy_uom", CPLStrdup(pszVerticalAccuracyUnit), -1);
    1585             :         }
    1586             :     }
    1587          10 : }
    1588             : 
    1589             : /************************************************************************/
    1590             : /*                       ParseAIXMElevationPoint()                      */
    1591             : /************************************************************************/
    1592             : 
    1593           2 : CPLXMLNode *GMLHandler::ParseAIXMElevationPoint(CPLXMLNode *psGML)
    1594             : {
    1595           2 :     ParseAIXMElevationProperties(psGML);
    1596             : 
    1597           2 :     const char *pszPos = CPLGetXMLValue(psGML, "pos", nullptr);
    1598           2 :     const char *pszCoordinates = CPLGetXMLValue(psGML, "coordinates", nullptr);
    1599           2 :     if (pszPos != nullptr || pszCoordinates != nullptr)
    1600             :     {
    1601           2 :         CPLFree(psGML->pszValue);
    1602           2 :         psGML->pszValue = CPLStrdup("gml:Point");
    1603             :     }
    1604             :     else
    1605             :     {
    1606           0 :         CPLDestroyXMLNode(psGML);
    1607           0 :         psGML = nullptr;
    1608             :     }
    1609             : 
    1610           2 :     return psGML;
    1611             : }
    1612             : 
    1613             : /************************************************************************/
    1614             : /*                         endElementGeometry()                         */
    1615             : /************************************************************************/
    1616       34078 : OGRErr GMLHandler::endElementGeometry()
    1617             : 
    1618             : {
    1619       34078 :     if (m_nGeomLen)
    1620             :     {
    1621             :         CPLXMLNode *psNode =
    1622        5129 :             static_cast<CPLXMLNode *>(CPLCalloc(sizeof(CPLXMLNode), 1));
    1623        5129 :         psNode->eType = CXT_Text;
    1624        5129 :         psNode->pszValue = m_pszGeometry;
    1625             : 
    1626        5129 :         NodeLastChild &sNodeLastChild = apsXMLNode.back();
    1627        5129 :         CPLXMLNode *psLastChildParent = sNodeLastChild.psLastChild;
    1628        5129 :         if (psLastChildParent == nullptr)
    1629             :         {
    1630        2423 :             CPLXMLNode *psParent = sNodeLastChild.psNode;
    1631        2423 :             if (psParent)
    1632        2423 :                 psParent->psChild = psNode;
    1633             :         }
    1634             :         else
    1635        2706 :             psLastChildParent->psNext = psNode;
    1636        5129 :         sNodeLastChild.psLastChild = psNode;
    1637             : 
    1638        5129 :         m_pszGeometry = nullptr;
    1639        5129 :         m_nGeomAlloc = 0;
    1640        5129 :         m_nGeomLen = 0;
    1641             :     }
    1642             : 
    1643       34078 :     CPLXMLNode *psThisNode = apsXMLNode.back().psNode;
    1644       34078 :     CPLXMLNode *psThisNodeChild = psThisNode->psChild;
    1645       41041 :     if (!m_oMapElementToSubstitute.empty() && psThisNodeChild &&
    1646        6963 :         psThisNodeChild->eType == CXT_Attribute &&
    1647       42157 :         strcmp(psThisNodeChild->pszValue, "gml:id") == 0 &&
    1648        1116 :         psThisNodeChild->psChild->pszValue)
    1649             :     {
    1650             :         auto oIter =
    1651        1116 :             m_oMapElementToSubstitute.find(psThisNodeChild->psChild->pszValue);
    1652        1116 :         if (oIter != m_oMapElementToSubstitute.end())
    1653             :         {
    1654         165 :             auto psLastChild = oIter->second->psChild;
    1655         165 :             if (psLastChild)
    1656             :             {
    1657             :                 // CPLDebug("GML", "Substitution of xlink:href=\"#%s\" with actual content", psThisNodeChild->psChild->pszValue);
    1658         165 :                 CPLXMLNode *psAfter = psThisNode->psNext;
    1659         165 :                 psThisNode->psNext = nullptr;
    1660             :                 // We can patch oIter->second as it stored as it in the current
    1661             :                 // GMLFeature.
    1662             :                 // Of course that would no longer be the case in case of
    1663             :                 // cross-references between different GMLFeature, hence we clear
    1664             :                 // m_oMapElementToSubstitute at the end of the current feature.
    1665         254 :                 while (psLastChild->psNext)
    1666          89 :                     psLastChild = psLastChild->psNext;
    1667         165 :                 if (psLastChild == psThisNode)
    1668             :                 {
    1669             :                     /* Can happen in situations like:
    1670             :                      <foo xlink:href="#X">
    1671             :                         <bar gml:id="X"/>
    1672             :                      </foo>
    1673             :                      Do not attempt substitution as that would cause a memory
    1674             :                      leak.
    1675             :                     */
    1676             :                 }
    1677             :                 else
    1678             :                 {
    1679         164 :                     psLastChild->psNext = CPLCloneXMLTree(psThisNode);
    1680             :                 }
    1681         165 :                 psThisNode->psNext = psAfter;
    1682             :             }
    1683             :         }
    1684             :     }
    1685             : 
    1686       34078 :     if (m_nDepth == m_nGeometryDepth)
    1687             :     {
    1688        3203 :         m_nGeometryDepth = 0;
    1689             : 
    1690        3203 :         CPLAssert(apsXMLNode.size() == 2);
    1691        3203 :         CPLXMLNode *psInterestNode = apsXMLNode.back().psNode;
    1692             : 
    1693             :         /*char* pszXML = CPLSerializeXMLTree(psInterestNode);
    1694             :         CPLDebug("GML", "geometry = %s", pszXML);
    1695             :         CPLFree(pszXML);*/
    1696             : 
    1697        3203 :         apsXMLNode.pop_back();
    1698             : 
    1699             :         /* AIXM ElevatedPoint. We want to parse this */
    1700             :         /* a bit specially because ElevatedPoint is aixm: stuff and */
    1701             :         /* the srsDimension of the <gml:pos> can be set to true although */
    1702             :         /* they are only 2 coordinates in practice */
    1703        3203 :         if (eAppSchemaType == APPSCHEMA_AIXM && psInterestNode != nullptr &&
    1704          12 :             strcmp(psInterestNode->pszValue, "ElevatedPoint") == 0)
    1705             :         {
    1706           2 :             psInterestNode = ParseAIXMElevationPoint(psInterestNode);
    1707             :         }
    1708        3201 :         else if (eAppSchemaType == APPSCHEMA_AIXM &&
    1709          10 :                  psInterestNode != nullptr &&
    1710          10 :                  (strcmp(psInterestNode->pszValue, "ElevatedCurve") == 0 ||
    1711           2 :                   strcmp(psInterestNode->pszValue, "ElevateSurface") == 0))
    1712             :         {
    1713           8 :             ParseAIXMElevationProperties(psInterestNode);
    1714             :         }
    1715        3193 :         else if (eAppSchemaType == APPSCHEMA_MTKGML &&
    1716             :                  psInterestNode != nullptr)
    1717             :         {
    1718          36 :             if (strcmp(psInterestNode->pszValue, "Murtoviiva") == 0)
    1719             :             {
    1720           7 :                 CPLFree(psInterestNode->pszValue);
    1721           7 :                 psInterestNode->pszValue = CPLStrdup("gml:LineString");
    1722             :             }
    1723          29 :             else if (strcmp(psInterestNode->pszValue, "Alue") == 0)
    1724             :             {
    1725           7 :                 CPLFree(psInterestNode->pszValue);
    1726           7 :                 psInterestNode->pszValue = CPLStrdup("gml:Polygon");
    1727             :             }
    1728          22 :             else if (strcmp(psInterestNode->pszValue, "Piste") == 0)
    1729             :             {
    1730          21 :                 CPLFree(psInterestNode->pszValue);
    1731          21 :                 psInterestNode->pszValue = CPLStrdup("gml:Point");
    1732             :             }
    1733             :         }
    1734        3157 :         else if (psInterestNode != nullptr &&
    1735        3157 :                  strcmp(psInterestNode->pszValue, "BoundingBox") == 0)
    1736             :         {
    1737          27 :             CPLFree(psInterestNode->pszValue);
    1738          27 :             psInterestNode->pszValue = CPLStrdup("Envelope");
    1739          30 :             for (CPLXMLNode *psChild = psInterestNode->psChild; psChild;
    1740           3 :                  psChild = psChild->psNext)
    1741             :             {
    1742          29 :                 if (psChild->eType == CXT_Attribute &&
    1743          27 :                     strcmp(psChild->pszValue, "crs") == 0)
    1744             :                 {
    1745          26 :                     CPLFree(psChild->pszValue);
    1746          26 :                     psChild->pszValue = CPLStrdup("srsName");
    1747          26 :                     break;
    1748             :                 }
    1749             :             }
    1750             :         }
    1751             : 
    1752        3203 :         GMLFeature *poGMLFeature = m_poReader->GetState()->m_poFeature;
    1753        3203 :         if (stateStack[nStackDepth] == STATE_BOUNDED_BY_IN_FEATURE)
    1754             :         {
    1755          78 :             if (eAppSchemaType == APPSCHEMA_CITYGML)
    1756          58 :                 CPLDestroyXMLNode(psInterestNode);
    1757             :             else
    1758          20 :                 poGMLFeature->SetBoundedByGeometry(psInterestNode);
    1759             :         }
    1760             :         else
    1761             :         {
    1762        3125 :             if (m_poReader->FetchAllGeometries())
    1763           0 :                 poGMLFeature->AddGeometry(psInterestNode);
    1764             :             else
    1765             :             {
    1766        3125 :                 GMLFeatureClass *poClass = poGMLFeature->GetClass();
    1767        3125 :                 if (poClass->GetGeometryPropertyCount() > 1)
    1768             :                 {
    1769         172 :                     if (poGMLFeature->GetGeometryRef(m_nGeometryPropertyIndex))
    1770             :                     {
    1771             :                         // If we have already a geometry, setting a new one
    1772             :                         // will invalidate nodes potentially stored in
    1773             :                         // m_oMapElementToSubstitute, so clear it
    1774           0 :                         m_oMapElementToSubstitute.clear();
    1775             :                     }
    1776         172 :                     poGMLFeature->SetGeometryDirectly(m_nGeometryPropertyIndex,
    1777             :                                                       psInterestNode);
    1778             :                 }
    1779             :                 else
    1780             :                 {
    1781        2953 :                     if (poGMLFeature->GetGeometryRef(0))
    1782             :                     {
    1783             :                         // If we have already a geometry, setting a new one
    1784             :                         // will invalidate nodes potentially stored in
    1785             :                         // m_oMapElementToSubstitute, so clear it
    1786          29 :                         m_oMapElementToSubstitute.clear();
    1787             :                     }
    1788        2953 :                     poGMLFeature->SetGeometryDirectly(psInterestNode);
    1789             :                 }
    1790             :             }
    1791             : 
    1792        3125 :             POP_STATE();
    1793             :         }
    1794             :     }
    1795             : 
    1796       34078 :     apsXMLNode.pop_back();
    1797             : 
    1798       34078 :     return OGRERR_NONE;
    1799             : }
    1800             : 
    1801             : /************************************************************************/
    1802             : /*                    endElementCityGMLGenericAttr()                    */
    1803             : /************************************************************************/
    1804          84 : OGRErr GMLHandler::endElementCityGMLGenericAttr()
    1805             : 
    1806             : {
    1807          84 :     if (m_pszCityGMLGenericAttrName != nullptr && m_bInCurField)
    1808             :     {
    1809          42 :         if (m_pszCurField != nullptr)
    1810             :         {
    1811          42 :             m_poReader->SetFeaturePropertyDirectly(m_pszCityGMLGenericAttrName,
    1812             :                                                    m_pszCurField, -1);
    1813             :         }
    1814          42 :         m_pszCurField = nullptr;
    1815          42 :         m_nCurFieldLen = 0;
    1816          42 :         m_nCurFieldAlloc = 0;
    1817          42 :         m_bInCurField = false;
    1818          42 :         CPLFree(m_pszCityGMLGenericAttrName);
    1819          42 :         m_pszCityGMLGenericAttrName = nullptr;
    1820             :     }
    1821             : 
    1822          84 :     if (m_inCityGMLGenericAttrDepth == m_nDepth)
    1823             :     {
    1824          42 :         POP_STATE();
    1825             :     }
    1826             : 
    1827          84 :     return OGRERR_NONE;
    1828             : }
    1829             : 
    1830             : /************************************************************************/
    1831             : /*                        endElementAttribute()                         */
    1832             : /************************************************************************/
    1833       10730 : OGRErr GMLHandler::endElementAttribute()
    1834             : 
    1835             : {
    1836       10730 :     GMLReadState *poState = m_poReader->GetState();
    1837             : 
    1838       10730 :     if (m_bInCurField)
    1839             :     {
    1840        7962 :         if (m_pszCurField == nullptr && m_poReader->IsEmptyAsNull())
    1841             :         {
    1842          70 :             if (m_pszValue != nullptr)
    1843             :             {
    1844          10 :                 m_poReader->SetFeaturePropertyDirectly(poState->osPath.c_str(),
    1845             :                                                        m_pszValue, -1);
    1846          10 :                 m_pszValue = nullptr;
    1847             :             }
    1848             :         }
    1849             :         else
    1850             :         {
    1851        7892 :             m_poReader->SetFeaturePropertyDirectly(
    1852             :                 poState->osPath.c_str(),
    1853        7892 :                 m_pszCurField ? m_pszCurField : CPLStrdup(""),
    1854             :                 m_nAttributeIndex);
    1855        7892 :             m_pszCurField = nullptr;
    1856             :         }
    1857             : 
    1858        7962 :         if (m_pszHref != nullptr)
    1859             :         {
    1860           4 :             CPLString osPropNameHref = poState->osPath + "_href";
    1861           4 :             m_poReader->SetFeaturePropertyDirectly(osPropNameHref, m_pszHref,
    1862             :                                                    -1);
    1863           4 :             m_pszHref = nullptr;
    1864             :         }
    1865             : 
    1866        7962 :         if (m_pszUom != nullptr)
    1867             :         {
    1868          20 :             CPLString osPropNameUom = poState->osPath + "_uom";
    1869          20 :             m_poReader->SetFeaturePropertyDirectly(osPropNameUom, m_pszUom, -1);
    1870          20 :             m_pszUom = nullptr;
    1871             :         }
    1872             : 
    1873        7962 :         if (m_pszKieli != nullptr)
    1874             :         {
    1875           7 :             CPLString osPropName = poState->osPath + "_kieli";
    1876           7 :             m_poReader->SetFeaturePropertyDirectly(osPropName, m_pszKieli, -1);
    1877           7 :             m_pszKieli = nullptr;
    1878             :         }
    1879             : 
    1880        7962 :         m_nCurFieldLen = 0;
    1881        7962 :         m_nCurFieldAlloc = 0;
    1882        7962 :         m_bInCurField = false;
    1883        7962 :         m_nAttributeIndex = -1;
    1884             : 
    1885        7962 :         CPLFree(m_pszValue);
    1886        7962 :         m_pszValue = nullptr;
    1887             :     }
    1888             : 
    1889       10730 :     poState->PopPath();
    1890             : 
    1891       10730 :     if (m_nAttributeDepth == m_nDepth)
    1892             :     {
    1893        8882 :         POP_STATE();
    1894             :     }
    1895             : 
    1896       10730 :     return OGRERR_NONE;
    1897             : }
    1898             : 
    1899             : /************************************************************************/
    1900             : /*                    startElementFeatureProperty()                     */
    1901             : /************************************************************************/
    1902             : 
    1903         165 : OGRErr GMLHandler::startElementFeatureProperty(const char * /*pszName*/,
    1904             :                                                int /*nLenName*/, void *attr)
    1905             : {
    1906         165 :     if (m_nDepth == m_nAttributeDepth + 1)
    1907             :     {
    1908          24 :         const char *pszGMLId = GetFID(attr);
    1909          24 :         if (pszGMLId != nullptr)
    1910             :         {
    1911          24 :             m_poReader->SetFeaturePropertyDirectly(
    1912             :                 nullptr, CPLStrdup(CPLSPrintf("#%s", pszGMLId)),
    1913             :                 m_nAttributeIndex);
    1914             :         }
    1915             :     }
    1916             : 
    1917         165 :     return OGRERR_NONE;
    1918             : }
    1919             : 
    1920             : /************************************************************************/
    1921             : /*                      endElementFeatureProperty()                      */
    1922             : /************************************************************************/
    1923             : 
    1924         189 : OGRErr GMLHandler::endElementFeatureProperty()
    1925             : 
    1926             : {
    1927         189 :     if (m_nDepth == m_nAttributeDepth)
    1928             :     {
    1929          24 :         GMLReadState *poState = m_poReader->GetState();
    1930          24 :         poState->PopPath();
    1931             : 
    1932          24 :         POP_STATE();
    1933             :     }
    1934         189 :     return OGRERR_NONE;
    1935             : }
    1936             : 
    1937             : /************************************************************************/
    1938             : /*                          endElementFeature()                         */
    1939             : /************************************************************************/
    1940        6007 : OGRErr GMLHandler::endElementFeature()
    1941             : 
    1942             : {
    1943             :     /* -------------------------------------------------------------------- */
    1944             :     /*      If we are collecting a feature, and this element tag matches    */
    1945             :     /*      element name for the class, then we have finished the           */
    1946             :     /*      feature, and we pop the feature read state.                     */
    1947             :     /* -------------------------------------------------------------------- */
    1948        6007 :     if (m_nDepth == m_nDepthFeature)
    1949             :     {
    1950        3205 :         GMLReadState *poState = m_poReader->GetState();
    1951        3205 :         GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
    1952        5119 :         if (!poClass->IsSchemaLocked() &&
    1953        1914 :             poClass->IsConsistentSingleGeomElemPath())
    1954             :         {
    1955             :             const std::string &osGeomElemPath =
    1956        1906 :                 poClass->GetSingleGeomElemPath();
    1957        1906 :             if (osGeomElemPath.empty())
    1958             :             {
    1959         548 :                 poClass->SetSingleGeomElemPath(m_osLastGeomPathInCurFeature);
    1960             :             }
    1961        1358 :             else if (m_osLastGeomPathInCurFeature != osGeomElemPath)
    1962             :             {
    1963           4 :                 poClass->SetConsistentSingleGeomElemPath(false);
    1964           4 :                 poClass->SetSingleGeomElemPath(std::string());
    1965             :             }
    1966             :         }
    1967             : 
    1968        3205 :         m_osLastGeomPathInCurFeature.clear();
    1969        3205 :         m_oMapElementToSubstitute.clear();
    1970        3205 :         m_poReader->PopState();
    1971             : 
    1972        3205 :         POP_STATE();
    1973             :     }
    1974             : 
    1975             :     /* -------------------------------------------------------------------- */
    1976             :     /*      Otherwise, we just pop the element off the local read states    */
    1977             :     /*      element stack.                                                  */
    1978             :     /* -------------------------------------------------------------------- */
    1979             :     else
    1980             :     {
    1981        2802 :         m_poReader->GetState()->PopPath();
    1982             :     }
    1983             : 
    1984        6007 :     return OGRERR_NONE;
    1985             : }
    1986             : 
    1987             : /************************************************************************/
    1988             : /*                          endElementDefault()                         */
    1989             : /************************************************************************/
    1990        4009 : OGRErr GMLHandler::endElementDefault()
    1991             : 
    1992             : {
    1993        4009 :     if (m_nDepth > 0)
    1994        3417 :         m_poReader->GetState()->PopPath();
    1995             : 
    1996        4009 :     return OGRERR_NONE;
    1997             : }
    1998             : 
    1999             : /************************************************************************/
    2000             : /*                         dataHandlerAttribute()                       */
    2001             : /************************************************************************/
    2002             : 
    2003       14502 : OGRErr GMLHandler::dataHandlerAttribute(const char *data, int nLen)
    2004             : 
    2005             : {
    2006       14502 :     if (!m_bInCurField)
    2007        3482 :         return OGRERR_NONE;
    2008             : 
    2009       11020 :     int nIter = 0;
    2010             : 
    2011             :     // Ignore white space.
    2012       11020 :     if (m_nCurFieldLen == 0)
    2013             :     {
    2014       25700 :         while (nIter < nLen)
    2015             :         {
    2016       22616 :             const char ch = data[nIter];
    2017       22616 :             if (!(ch == ' ' || ch == 10 || ch == 13 || ch == '\t'))
    2018        7934 :                 break;
    2019       14682 :             nIter++;
    2020             :         }
    2021             :     }
    2022             : 
    2023       11020 :     const int nCharsLen = nLen - nIter;
    2024             : 
    2025       11020 :     if (nCharsLen > INT_MAX - static_cast<int>(m_nCurFieldLen) - 1)
    2026             :     {
    2027           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
    2028             :                  "Too much data in a single element");
    2029           0 :         return OGRERR_NOT_ENOUGH_MEMORY;
    2030             :     }
    2031       11020 :     if (m_nCurFieldLen + nCharsLen + 1 > m_nCurFieldAlloc)
    2032             :     {
    2033        9275 :         if (m_nCurFieldAlloc < INT_MAX - m_nCurFieldAlloc / 3 - nCharsLen - 1)
    2034        9275 :             m_nCurFieldAlloc =
    2035        9275 :                 m_nCurFieldAlloc + m_nCurFieldAlloc / 3 + nCharsLen + 1;
    2036             :         else
    2037           0 :             m_nCurFieldAlloc = m_nCurFieldLen + nCharsLen + 1;
    2038             :         char *pszNewCurField = static_cast<char *>(
    2039        9275 :             VSI_REALLOC_VERBOSE(m_pszCurField, m_nCurFieldAlloc));
    2040        9275 :         if (pszNewCurField == nullptr)
    2041             :         {
    2042           0 :             return OGRERR_NOT_ENOUGH_MEMORY;
    2043             :         }
    2044        9275 :         m_pszCurField = pszNewCurField;
    2045             :     }
    2046       11020 :     memcpy(m_pszCurField + m_nCurFieldLen, data + nIter, nCharsLen);
    2047       11020 :     m_nCurFieldLen += nCharsLen;
    2048       11020 :     m_pszCurField[m_nCurFieldLen] = '\0';
    2049             : 
    2050       11020 :     return OGRERR_NONE;
    2051             : }
    2052             : 
    2053             : /************************************************************************/
    2054             : /*                         dataHandlerGeometry()                        */
    2055             : /************************************************************************/
    2056             : 
    2057       94713 : OGRErr GMLHandler::dataHandlerGeometry(const char *data, int nLen)
    2058             : 
    2059             : {
    2060       94713 :     int nIter = 0;
    2061             : 
    2062             :     // Ignore white space
    2063       94713 :     if (m_nGeomLen == 0)
    2064             :     {
    2065      737788 :         while (nIter < nLen)
    2066             :         {
    2067      648243 :             char ch = data[nIter];
    2068      648243 :             if (!(ch == ' ' || ch == 10 || ch == 13 || ch == '\t'))
    2069        5129 :                 break;
    2070      643114 :             nIter++;
    2071             :         }
    2072             :     }
    2073             : 
    2074       94713 :     const int nCharsLen = nLen - nIter;
    2075       94713 :     if (nCharsLen)
    2076             :     {
    2077        5168 :         if (nCharsLen > INT_MAX - static_cast<int>(m_nGeomLen) - 1)
    2078             :         {
    2079           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    2080             :                      "Too much data in a single element");
    2081           0 :             return OGRERR_NOT_ENOUGH_MEMORY;
    2082             :         }
    2083        5168 :         if (m_nGeomLen + nCharsLen + 1 > m_nGeomAlloc)
    2084             :         {
    2085        5163 :             if (m_nGeomAlloc < INT_MAX - m_nGeomAlloc / 3 - nCharsLen - 1)
    2086        5163 :                 m_nGeomAlloc = m_nGeomAlloc + m_nGeomAlloc / 3 + nCharsLen + 1;
    2087             :             else
    2088           0 :                 m_nGeomAlloc = m_nGeomAlloc + nCharsLen + 1;
    2089             :             char *pszNewGeometry = static_cast<char *>(
    2090        5163 :                 VSI_REALLOC_VERBOSE(m_pszGeometry, m_nGeomAlloc));
    2091        5163 :             if (pszNewGeometry == nullptr)
    2092             :             {
    2093           0 :                 return OGRERR_NOT_ENOUGH_MEMORY;
    2094             :             }
    2095        5163 :             m_pszGeometry = pszNewGeometry;
    2096             :         }
    2097        5168 :         memcpy(m_pszGeometry + m_nGeomLen, data + nIter, nCharsLen);
    2098        5168 :         m_nGeomLen += nCharsLen;
    2099        5168 :         m_pszGeometry[m_nGeomLen] = '\0';
    2100             :     }
    2101             : 
    2102       94713 :     return OGRERR_NONE;
    2103             : }
    2104             : 
    2105             : /************************************************************************/
    2106             : /*                         IsGeometryElement()                          */
    2107             : /************************************************************************/
    2108             : 
    2109       16862 : bool GMLHandler::IsGeometryElement(const char *pszElement)
    2110             : 
    2111             : {
    2112       16862 :     int nFirst = 0;
    2113       16862 :     int nLast = GML_GEOMETRY_TYPE_COUNT - 1;
    2114       16862 :     unsigned long nHash = CPLHashSetHashStr(pszElement);
    2115       60559 :     do
    2116             :     {
    2117       77421 :         const int nMiddle = (nFirst + nLast) / 2;
    2118       77421 :         if (nHash == pasGeometryNames[nMiddle].nHash)
    2119        3087 :             return strcmp(pszElement, pasGeometryNames[nMiddle].pszName) == 0;
    2120       74334 :         if (nHash < pasGeometryNames[nMiddle].nHash)
    2121       46988 :             nLast = nMiddle - 1;
    2122             :         else
    2123       27346 :             nFirst = nMiddle + 1;
    2124       74334 :     } while (nFirst <= nLast);
    2125             : 
    2126       13775 :     if (eAppSchemaType == APPSCHEMA_AIXM &&
    2127         265 :         (strcmp(pszElement, "ElevatedPoint") == 0 ||
    2128         263 :          strcmp(pszElement, "ElevatedCurve") == 0 ||
    2129         255 :          strcmp(pszElement, "ElevatedSurface") == 0))
    2130             :     {
    2131          12 :         return true;
    2132             :     }
    2133             : 
    2134       13763 :     if (eAppSchemaType == APPSCHEMA_MTKGML &&
    2135          77 :         (strcmp(pszElement, "Piste") == 0 || strcmp(pszElement, "Alue") == 0 ||
    2136          49 :          strcmp(pszElement, "Murtoviiva") == 0))
    2137          35 :         return true;
    2138             : 
    2139       13728 :     return false;
    2140             : }

Generated by: LCOV version 1.14