LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gml - gmlhandler.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 809 909 89.0 %
Date: 2024-11-21 22:18:42 Functions: 48 51 94.1 %

          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         517 : GMLExpatHandler::GMLExpatHandler(GMLReader *poReader, XML_Parser oParser)
     248             :     : GMLHandler(poReader), m_oParser(oParser), m_bStopParsing(false),
     249         517 :       m_nDataHandlerCounter(0)
     250             : {
     251         517 : }
     252             : 
     253             : /************************************************************************/
     254             : /*                  GMLExpatHandler::DealWithError()                    */
     255             : /************************************************************************/
     256             : 
     257      284396 : void GMLExpatHandler::DealWithError(OGRErr eErr)
     258             : {
     259      284396 :     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      284396 : }
     267             : 
     268             : /************************************************************************/
     269             : /*                           startElementCbk()                          */
     270             : /************************************************************************/
     271             : 
     272       45231 : void XMLCALL GMLExpatHandler::startElementCbk(void *pUserData,
     273             :                                               const char *pszName,
     274             :                                               const char **ppszAttr)
     275             : 
     276             : {
     277       45231 :     GMLExpatHandler *pThis = static_cast<GMLExpatHandler *>(pUserData);
     278       45231 :     if (pThis->m_bStopParsing)
     279           0 :         return;
     280             : 
     281       45231 :     const char *pszIter = pszName;
     282       45231 :     char ch = '\0';
     283      602033 :     while ((ch = *pszIter) != '\0')
     284             :     {
     285      556802 :         if (ch == ':')
     286       36743 :             pszName = pszIter + 1;
     287      556802 :         pszIter++;
     288             :     }
     289             : 
     290       45231 :     pThis->DealWithError(pThis->GMLHandler::startElement(
     291       45231 :         pszName, static_cast<int>(pszIter - pszName), ppszAttr));
     292             : }
     293             : 
     294             : /************************************************************************/
     295             : /*                            endElementCbk()                           */
     296             : /************************************************************************/
     297       45136 : void XMLCALL GMLExpatHandler::endElementCbk(void *pUserData,
     298             :                                             const char * /* pszName */)
     299             : {
     300       45136 :     GMLExpatHandler *pThis = static_cast<GMLExpatHandler *>(pUserData);
     301       45136 :     if (pThis->m_bStopParsing)
     302           1 :         return;
     303             : 
     304       45135 :     pThis->DealWithError(pThis->GMLHandler::endElement());
     305             : }
     306             : 
     307             : /************************************************************************/
     308             : /*                            dataHandlerCbk()                          */
     309             : /************************************************************************/
     310             : 
     311      194031 : void XMLCALL GMLExpatHandler::dataHandlerCbk(void *pUserData, const char *data,
     312             :                                              int nLen)
     313             : 
     314             : {
     315      194031 :     GMLExpatHandler *pThis = static_cast<GMLExpatHandler *>(pUserData);
     316      194031 :     if (pThis->m_bStopParsing)
     317           0 :         return;
     318             : 
     319      194031 :     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      194031 :     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      194030 :     pThis->DealWithError(pThis->GMLHandler::dataHandler(data, nLen));
     337             : }
     338             : 
     339             : /************************************************************************/
     340             : /*                               GetFID()                               */
     341             : /************************************************************************/
     342             : 
     343        2602 : const char *GMLExpatHandler::GetFID(void *attr)
     344             : {
     345        2602 :     const char *const *papszIter = static_cast<const char *const *>(attr);
     346        2620 :     while (*papszIter)
     347             :     {
     348        1618 :         if (strcmp(*papszIter, "fid") == 0 || strcmp(*papszIter, "gml:id") == 0)
     349             :         {
     350        1600 :             return papszIter[1];
     351             :         }
     352          18 :         papszIter += 2;
     353             :     }
     354        1002 :     return nullptr;
     355             : }
     356             : 
     357             : /************************************************************************/
     358             : /*                        AddAttributes()                               */
     359             : /************************************************************************/
     360             : 
     361       22091 : CPLXMLNode *GMLExpatHandler::AddAttributes(CPLXMLNode *psNode, void *attr)
     362             : {
     363       22091 :     const char **papszIter = static_cast<const char **>(attr);
     364             : 
     365       22091 :     CPLXMLNode *psLastChild = nullptr;
     366             : 
     367       31641 :     while (*papszIter)
     368             :     {
     369             :         CPLXMLNode *psChild =
     370        9550 :             CPLCreateXMLNode(nullptr, CXT_Attribute, papszIter[0]);
     371        9550 :         CPLCreateXMLNode(psChild, CXT_Text, papszIter[1]);
     372             : 
     373        9550 :         if (psLastChild == nullptr)
     374        8732 :             psNode->psChild = psChild;
     375             :         else
     376         818 :             psLastChild->psNext = psChild;
     377        9550 :         psLastChild = psChild;
     378             : 
     379        9550 :         papszIter += 2;
     380             :     }
     381             : 
     382       22091 :     return psLastChild;
     383             : }
     384             : 
     385             : /************************************************************************/
     386             : /*                    GetAttributeValue()                               */
     387             : /************************************************************************/
     388             : 
     389        8880 : char *GMLExpatHandler::GetAttributeValue(void *attr,
     390             :                                          const char *pszAttributeName)
     391             : {
     392        8880 :     const char *const *papszIter = static_cast<const char *const *>(attr);
     393        9402 :     while (*papszIter)
     394             :     {
     395         740 :         if (strcmp(*papszIter, pszAttributeName) == 0)
     396             :         {
     397         218 :             return CPLStrdup(papszIter[1]);
     398             :         }
     399         522 :         papszIter += 2;
     400             :     }
     401        8662 :     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       11411 : char *GMLExpatHandler::GetAttributeByIdx(void *attr, unsigned int idx,
     411             :                                          char **ppszKey)
     412             : {
     413       11411 :     const char *const *papszIter = static_cast<const char *const *>(attr);
     414       11411 :     if (papszIter[2 * idx] == nullptr)
     415             :     {
     416       10873 :         *ppszKey = nullptr;
     417       10873 :         return nullptr;
     418             :     }
     419         538 :     *ppszKey = CPLStrdup(papszIter[2 * idx]);
     420         538 :     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             :     "Curve",
     430             :     "GeometryCollection", /* OGR < 1.8.0 bug... */
     431             :     "LineString",
     432             :     "MultiCurve",
     433             :     "MultiGeometry",
     434             :     "MultiLineString",
     435             :     "MultiPoint",
     436             :     "MultiPolygon",
     437             :     "MultiSurface",
     438             :     "Point",
     439             :     "Polygon",
     440             :     "PolygonPatch",
     441             :     "PolyhedralSurface",
     442             :     "SimplePolygon",    /* GML 3.3 compact encoding */
     443             :     "SimpleRectangle",  /* GML 3.3 compact encoding */
     444             :     "SimpleTriangle",   /* GML 3.3 compact encoding */
     445             :     "SimpleMultiPoint", /* GML 3.3 compact encoding */
     446             :     "Solid",
     447             :     "Surface",
     448             :     "Tin",
     449             :     "TopoCurve",
     450             :     "TopoSurface",
     451             :     "Triangle",
     452             :     "TriangulatedSurface"};
     453             : 
     454             : #define GML_GEOMETRY_TYPE_COUNT                                                \
     455             :     static_cast<int>(sizeof(apszGMLGeometryElements) /                         \
     456             :                      sizeof(apszGMLGeometryElements[0]))
     457             : 
     458          98 : bool OGRGMLIsGeometryElement(const char *pszElement)
     459             : {
     460        2730 :     for (const auto &pszGMLElement : apszGMLGeometryElements)
     461             :     {
     462        2633 :         if (strcmp(pszElement, pszGMLElement) == 0)
     463           1 :             return true;
     464             :     }
     465          97 :     return false;
     466             : }
     467             : 
     468             : struct _GeometryNamesStruct
     469             : {
     470             :     unsigned long nHash;
     471             :     const char *pszName;
     472             : };
     473             : 
     474             : /************************************************************************/
     475             : /*                            GMLHandler()                              */
     476             : /************************************************************************/
     477             : 
     478         519 : GMLHandler::GMLHandler(GMLReader *poReader)
     479             :     : pasGeometryNames(static_cast<GeometryNamesStruct *>(
     480        1038 :           CPLMalloc(GML_GEOMETRY_TYPE_COUNT * sizeof(GeometryNamesStruct)))),
     481             :       m_nSRSDimensionIfMissing(
     482         519 :           atoi(CPLGetConfigOption("GML_SRS_DIMENSION_IF_MISSING", "0"))),
     483        1038 :       m_poReader(poReader), eAppSchemaType(APPSCHEMA_GENERIC), nStackDepth(0)
     484             : {
     485       14532 :     for (int i = 0; i < GML_GEOMETRY_TYPE_COUNT; i++)
     486             :     {
     487       14013 :         pasGeometryNames[i].pszName = apszGMLGeometryElements[i];
     488       28026 :         pasGeometryNames[i].nHash =
     489       14013 :             CPLHashSetHashStr(pasGeometryNames[i].pszName);
     490             :     }
     491         519 :     std::sort(pasGeometryNames, pasGeometryNames + GML_GEOMETRY_TYPE_COUNT,
     492       79926 :               [](const GeometryNamesStruct &a, const GeometryNamesStruct &b)
     493       79926 :               { return a.nHash < b.nHash; });
     494             : 
     495         519 :     stateStack[0] = STATE_TOP;
     496         519 : }
     497             : 
     498             : /************************************************************************/
     499             : /*                            ~GMLHandler()                             */
     500             : /************************************************************************/
     501             : 
     502         519 : GMLHandler::~GMLHandler()
     503             : 
     504             : {
     505         519 :     if (apsXMLNode.size() >= 2 && apsXMLNode[1].psNode != nullptr)
     506           2 :         CPLDestroyXMLNode(apsXMLNode[1].psNode);
     507             : 
     508         519 :     CPLFree(m_pszCurField);
     509         519 :     CPLFree(m_pszGeometry);
     510         519 :     CPLFree(m_pszCityGMLGenericAttrName);
     511         519 :     CPLFree(m_pszHref);
     512         519 :     CPLFree(m_pszUom);
     513         519 :     CPLFree(m_pszValue);
     514         519 :     CPLFree(m_pszKieli);
     515         519 :     CPLFree(pasGeometryNames);
     516         519 : }
     517             : 
     518             : /************************************************************************/
     519             : /*                             startElement()                           */
     520             : /************************************************************************/
     521             : 
     522       45250 : OGRErr GMLHandler::startElement(const char *pszName, int nLenName, void *attr)
     523             : {
     524             :     OGRErr eRet;
     525       45250 :     switch (stateStack[nStackDepth])
     526             :     {
     527         517 :         case STATE_TOP:
     528         517 :             eRet = startElementTop(pszName, nLenName, attr);
     529         517 :             break;
     530        6245 :         case STATE_DEFAULT:
     531        6245 :             eRet = startElementDefault(pszName, nLenName, attr);
     532        6245 :             break;
     533       10921 :         case STATE_FEATURE:
     534       10921 :             eRet = startElementFeatureAttribute(pszName, nLenName, attr);
     535       10921 :             break;
     536        2778 :         case STATE_PROPERTY:
     537        2778 :             eRet = startElementFeatureAttribute(pszName, nLenName, attr);
     538        2778 :             break;
     539         165 :         case STATE_FEATUREPROPERTY:
     540         165 :             eRet = startElementFeatureProperty(pszName, nLenName, attr);
     541         165 :             break;
     542       18616 :         case STATE_GEOMETRY:
     543       18616 :             eRet = startElementGeometry(pszName, nLenName, attr);
     544       18616 :             break;
     545        4003 :         case STATE_IGNORED_FEATURE:
     546        4003 :             eRet = OGRERR_NONE;
     547        4003 :             break;
     548         905 :         case STATE_BOUNDED_BY:
     549         905 :             eRet = startElementBoundedBy(pszName, nLenName, attr);
     550         905 :             break;
     551        1058 :         case STATE_BOUNDED_BY_IN_FEATURE:
     552        1058 :             eRet = startElementGeometry(pszName, nLenName, attr);
     553        1058 :             break;
     554          42 :         case STATE_CITYGML_ATTRIBUTE:
     555          42 :             eRet = startElementCityGMLGenericAttr(pszName, nLenName, attr);
     556          42 :             break;
     557           0 :         default:
     558           0 :             eRet = OGRERR_NONE;
     559           0 :             break;
     560             :     }
     561       45250 :     m_nDepth++;
     562       45250 :     if (m_nDepth == 64)
     563             :     {
     564             :         // Avoid performance issues on files like
     565             :         // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=21737
     566           3 :         if (m_nUnlimitedDepth < 0)
     567             :         {
     568           3 :             m_nUnlimitedDepth = EQUAL(
     569             :                 CPLGetConfigOption("OGR_GML_NESTING_LEVEL", ""), "UNLIMITED");
     570             :         }
     571           3 :         if (!m_nUnlimitedDepth)
     572             :         {
     573           1 :             CPLError(CE_Failure, CPLE_NotSupported,
     574             :                      "Too deep XML nesting level (%d). "
     575             :                      "Set the OGR_GML_NESTING_LEVEL configuration option to "
     576             :                      "UNLIMITED to remove that limitation.",
     577             :                      m_nDepth);
     578           1 :             eRet = OGRERR_FAILURE;
     579             :         }
     580             :     }
     581       45250 :     return eRet;
     582             : }
     583             : 
     584             : /************************************************************************/
     585             : /*                              endElement()                            */
     586             : /************************************************************************/
     587             : 
     588       45150 : OGRErr GMLHandler::endElement()
     589             : {
     590       45150 :     m_nDepth--;
     591       45150 :     switch (stateStack[nStackDepth])
     592             :     {
     593           0 :         case STATE_TOP:
     594           0 :             return OGRERR_NONE;
     595             :             break;
     596        3688 :         case STATE_DEFAULT:
     597        3688 :             return endElementDefault();
     598             :             break;
     599        5099 :         case STATE_FEATURE:
     600        5099 :             return endElementFeature();
     601             :             break;
     602        8451 :         case STATE_PROPERTY:
     603        8451 :             return endElementAttribute();
     604             :             break;
     605         189 :         case STATE_FEATUREPROPERTY:
     606         189 :             return endElementFeatureProperty();
     607             :             break;
     608       21020 :         case STATE_GEOMETRY:
     609       21020 :             return endElementGeometry();
     610             :             break;
     611        4348 :         case STATE_IGNORED_FEATURE:
     612        4348 :             return endElementIgnoredFeature();
     613             :             break;
     614        1141 :         case STATE_BOUNDED_BY:
     615        1141 :             return endElementBoundedBy();
     616             :             break;
     617        1130 :         case STATE_BOUNDED_BY_IN_FEATURE:
     618        1130 :             return endElementBoundedByInFeature();
     619             :             break;
     620          84 :         case STATE_CITYGML_ATTRIBUTE:
     621          84 :             return endElementCityGMLGenericAttr();
     622             :             break;
     623           0 :         default:
     624           0 :             return OGRERR_NONE;
     625             :             break;
     626             :     }
     627             : }
     628             : 
     629             : /************************************************************************/
     630             : /*                              dataHandler()                           */
     631             : /************************************************************************/
     632             : 
     633      194935 : OGRErr GMLHandler::dataHandler(const char *data, int nLen)
     634             : {
     635      194935 :     switch (stateStack[nStackDepth])
     636             :     {
     637           0 :         case STATE_TOP:
     638           0 :             return OGRERR_NONE;
     639             :             break;
     640      100586 :         case STATE_DEFAULT:
     641      100586 :             return OGRERR_NONE;
     642             :             break;
     643       22849 :         case STATE_FEATURE:
     644       22849 :             return OGRERR_NONE;
     645             :             break;
     646       10494 :         case STATE_PROPERTY:
     647       10494 :             return dataHandlerAttribute(data, nLen);
     648             :             break;
     649         625 :         case STATE_FEATUREPROPERTY:
     650         625 :             return OGRERR_NONE;
     651             :             break;
     652       52771 :         case STATE_GEOMETRY:
     653       52771 :             return dataHandlerGeometry(data, nLen);
     654             :             break;
     655        3295 :         case STATE_IGNORED_FEATURE:
     656        3295 :             return OGRERR_NONE;
     657             :             break;
     658        1895 :         case STATE_BOUNDED_BY:
     659        1895 :             return OGRERR_NONE;
     660             :             break;
     661        2282 :         case STATE_BOUNDED_BY_IN_FEATURE:
     662        2282 :             return dataHandlerGeometry(data, nLen);
     663             :             break;
     664         138 :         case STATE_CITYGML_ATTRIBUTE:
     665         138 :             return dataHandlerAttribute(data, nLen);
     666             :             break;
     667           0 :         default:
     668           0 :             return OGRERR_NONE;
     669             :             break;
     670             :     }
     671             : }
     672             : 
     673             : #define PUSH_STATE(val)                                                        \
     674             :     do                                                                         \
     675             :     {                                                                          \
     676             :         nStackDepth++;                                                         \
     677             :         CPLAssert(nStackDepth < STACK_SIZE);                                   \
     678             :         stateStack[nStackDepth] = val;                                         \
     679             :     } while (false)
     680             : #define POP_STATE() nStackDepth--
     681             : 
     682             : /************************************************************************/
     683             : /*                       startElementBoundedBy()                        */
     684             : /************************************************************************/
     685             : 
     686         905 : OGRErr GMLHandler::startElementBoundedBy(const char *pszName, int /*nLenName*/,
     687             :                                          void *attr)
     688             : {
     689         905 :     if (m_nDepth == 2 && strcmp(pszName, "Envelope") == 0)
     690             :     {
     691         126 :         char *pszGlobalSRSName = GetAttributeValue(attr, "srsName");
     692         126 :         m_poReader->SetGlobalSRSName(pszGlobalSRSName);
     693         126 :         CPLFree(pszGlobalSRSName);
     694             : 
     695         126 :         if (m_nSRSDimensionIfMissing == 0)
     696             :         {
     697             :             char *pszGlobalSRSDimension =
     698         122 :                 GetAttributeValue(attr, "srsDimension");
     699         122 :             if (pszGlobalSRSDimension)
     700          15 :                 m_nSRSDimensionIfMissing = atoi(pszGlobalSRSDimension);
     701         122 :             CPLFree(pszGlobalSRSDimension);
     702             :         }
     703             :     }
     704             : 
     705         905 :     return OGRERR_NONE;
     706             : }
     707             : 
     708             : /************************************************************************/
     709             : /*                       startElementGeometry()                         */
     710             : /************************************************************************/
     711             : 
     712       22094 : OGRErr GMLHandler::startElementGeometry(const char *pszName, int nLenName,
     713             :                                         void *attr)
     714             : {
     715       23152 :     if (stateStack[nStackDepth] == STATE_BOUNDED_BY_IN_FEATURE &&
     716        1058 :         apsXMLNode.empty())
     717             :     {
     718           1 :         CPLError(CE_Failure, CPLE_AppDefined, "Invalid <boundedBy> construct");
     719           1 :         return OGRERR_FAILURE;
     720             :     }
     721             : 
     722             :     /* Create new XML Element */
     723             :     CPLXMLNode *psCurNode =
     724       22093 :         static_cast<CPLXMLNode *>(CPLCalloc(sizeof(CPLXMLNode), 1));
     725       22093 :     psCurNode->eType = CXT_Element;
     726       22093 :     psCurNode->pszValue = static_cast<char *>(CPLMalloc(nLenName + 1));
     727       22093 :     memcpy(psCurNode->pszValue, pszName, nLenName + 1);
     728             : 
     729             :     /* Attach element as the last child of its parent */
     730       22093 :     NodeLastChild &sNodeLastChild = apsXMLNode.back();
     731       22093 :     CPLXMLNode *psLastChildParent = sNodeLastChild.psLastChild;
     732             : 
     733       22093 :     if (psLastChildParent == nullptr)
     734             :     {
     735       12334 :         CPLXMLNode *psParent = sNodeLastChild.psNode;
     736       12334 :         if (psParent)
     737        9840 :             psParent->psChild = psCurNode;
     738             :     }
     739             :     else
     740             :     {
     741        9759 :         psLastChildParent->psNext = psCurNode;
     742             :     }
     743       22093 :     sNodeLastChild.psLastChild = psCurNode;
     744             : 
     745             :     /* Add attributes to the element */
     746       22093 :     CPLXMLNode *psLastChildCurNode = AddAttributes(psCurNode, attr);
     747       31646 :     for (CPLXMLNode *psIter = psCurNode->psChild; psIter;
     748        9553 :          psIter = psIter->psNext)
     749             :     {
     750        9553 :         if (psIter->eType == CXT_Attribute &&
     751        9553 :             strcmp(psIter->pszValue, "xlink:href") == 0 &&
     752         723 :             psIter->psChild->pszValue && psIter->psChild->pszValue[0] == '#')
     753             :         {
     754         723 :             m_oMapElementToSubstitute[psIter->psChild->pszValue + 1] =
     755             :                 psCurNode;
     756             :         }
     757             :     }
     758             : 
     759             :     /* Some CityGML lack a srsDimension="3" in posList, such as in */
     760             :     /* http://www.citygml.org/fileadmin/count.php?f=fileadmin%2Fcitygml%2Fdocs%2FFrankfurt_Street_Setting_LOD3.zip
     761             :      */
     762             :     /* So we have to add it manually */
     763       46485 :     if (strcmp(pszName, "posList") == 0 &&
     764       22959 :         CPLGetXMLValue(psCurNode, "srsDimension", nullptr) == nullptr &&
     765         866 :         m_nSRSDimensionIfMissing != 0)
     766             :     {
     767             :         CPLXMLNode *psChild =
     768         118 :             CPLCreateXMLNode(nullptr, CXT_Attribute, "srsDimension");
     769         118 :         CPLCreateXMLNode(psChild, CXT_Text,
     770         118 :                          (m_nSRSDimensionIfMissing == 3) ? "3" : "2");
     771             : 
     772         118 :         if (psLastChildCurNode == nullptr)
     773         118 :             psCurNode->psChild = psChild;
     774             :         else
     775           0 :             psLastChildCurNode->psNext = psChild;
     776         118 :         psLastChildCurNode = psChild;
     777             :     }
     778             : 
     779             :     /* Push the element on the stack */
     780             :     NodeLastChild sNewNodeLastChild;
     781       22093 :     sNewNodeLastChild.psNode = psCurNode;
     782       22093 :     sNewNodeLastChild.psLastChild = psLastChildCurNode;
     783       22093 :     apsXMLNode.push_back(sNewNodeLastChild);
     784             : 
     785       22093 :     if (m_pszGeometry)
     786             :     {
     787           0 :         CPLFree(m_pszGeometry);
     788           0 :         m_pszGeometry = nullptr;
     789           0 :         m_nGeomAlloc = 0;
     790           0 :         m_nGeomLen = 0;
     791             :     }
     792             : 
     793       22093 :     return OGRERR_NONE;
     794             : }
     795             : 
     796             : /************************************************************************/
     797             : /*                    startElementCityGMLGenericAttr()                  */
     798             : /************************************************************************/
     799             : 
     800          42 : OGRErr GMLHandler::startElementCityGMLGenericAttr(const char *pszName,
     801             :                                                   int /*nLenName*/,
     802             :                                                   void * /*attr*/)
     803             : {
     804          42 :     if (strcmp(pszName, "value") == 0)
     805             :     {
     806          42 :         if (m_pszCurField)
     807             :         {
     808           0 :             CPLFree(m_pszCurField);
     809           0 :             m_pszCurField = nullptr;
     810           0 :             m_nCurFieldLen = 0;
     811           0 :             m_nCurFieldAlloc = 0;
     812             :         }
     813          42 :         m_bInCurField = true;
     814             :     }
     815             : 
     816          42 :     return OGRERR_NONE;
     817             : }
     818             : 
     819             : /************************************************************************/
     820             : /*                       DealWithAttributes()                           */
     821             : /************************************************************************/
     822             : 
     823       10882 : void GMLHandler::DealWithAttributes(const char *pszName, int nLenName,
     824             :                                     void *attr)
     825             : {
     826       10882 :     GMLReadState *poState = m_poReader->GetState();
     827       10882 :     GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
     828             : 
     829       10882 :     for (unsigned int idx = 0; true; idx++)
     830             :     {
     831       11420 :         char *pszAttrKey = nullptr;
     832             : 
     833       11420 :         char *pszAttrVal = GetAttributeByIdx(attr, idx, &pszAttrKey);
     834       11420 :         if (pszAttrVal == nullptr)
     835       10882 :             break;
     836             : 
     837         538 :         int nAttrIndex = 0;
     838         538 :         const char *pszAttrKeyNoNS = strchr(pszAttrKey, ':');
     839         538 :         if (pszAttrKeyNoNS != nullptr)
     840         101 :             pszAttrKeyNoNS++;
     841             : 
     842             :         /* If attribute is referenced by the .gfs */
     843         685 :         if (poClass->IsSchemaLocked() &&
     844          48 :             ((pszAttrKeyNoNS != nullptr &&
     845          48 :               (nAttrIndex = m_poReader->GetAttributeElementIndex(
     846         114 :                    pszName, nLenName, pszAttrKeyNoNS)) != -1) ||
     847         114 :              ((nAttrIndex = m_poReader->GetAttributeElementIndex(
     848             :                    pszName, nLenName, pszAttrKey)) != -1)))
     849             :         {
     850          67 :             nAttrIndex = FindRealPropertyByCheckingConditions(nAttrIndex, attr);
     851          67 :             if (nAttrIndex >= 0)
     852             :             {
     853          65 :                 m_poReader->SetFeaturePropertyDirectly(nullptr, pszAttrVal,
     854             :                                                        nAttrIndex);
     855          65 :                 pszAttrVal = nullptr;
     856             :             }
     857             :         }
     858             : 
     859             :         /* Hard-coded historic cases */
     860         471 :         else if (strcmp(pszAttrKey, "xlink:href") == 0)
     861             :         {
     862          14 :             if ((m_bReportHref || m_poReader->ReportAllAttributes()) &&
     863           7 :                 m_bInCurField)
     864             :             {
     865           4 :                 CPLFree(m_pszHref);
     866           4 :                 m_pszHref = pszAttrVal;
     867           4 :                 pszAttrVal = nullptr;
     868             :             }
     869           9 :             else if ((!poClass->IsSchemaLocked() &&
     870           9 :                       (m_bReportHref || m_poReader->ReportAllAttributes())) ||
     871           3 :                      (poClass->IsSchemaLocked() &&
     872           3 :                       (nAttrIndex = m_poReader->GetAttributeElementIndex(
     873           6 :                            (std::string(pszName) + "_href").c_str(),
     874             :                            nLenName + 5)) != -1))
     875             :             {
     876           3 :                 poState->PushPath(pszName, nLenName);
     877           3 :                 CPLString osPropNameHref = poState->osPath + "_href";
     878           3 :                 poState->PopPath();
     879           3 :                 m_poReader->SetFeaturePropertyDirectly(osPropNameHref,
     880             :                                                        pszAttrVal, nAttrIndex);
     881           3 :                 pszAttrVal = nullptr;
     882             :             }
     883             :         }
     884         464 :         else if (strcmp(pszAttrKey, "uom") == 0)
     885             :         {
     886          14 :             CPLFree(m_pszUom);
     887          14 :             m_pszUom = pszAttrVal;
     888          14 :             pszAttrVal = nullptr;
     889             :         }
     890         450 :         else if (strcmp(pszAttrKey, "value") == 0)
     891             :         {
     892          10 :             CPLFree(m_pszValue);
     893          10 :             m_pszValue = pszAttrVal;
     894          10 :             pszAttrVal = nullptr;
     895             :         }
     896             :         else /* Get language in 'kieli' attribute of 'teksti' element */
     897         440 :             if (eAppSchemaType == APPSCHEMA_MTKGML && nLenName == 6 &&
     898           7 :                 strcmp(pszName, "teksti") == 0 &&
     899           7 :                 strcmp(pszAttrKey, "kieli") == 0)
     900             :             {
     901           7 :                 CPLFree(m_pszKieli);
     902           7 :                 m_pszKieli = pszAttrVal;
     903           7 :                 pszAttrVal = nullptr;
     904             :             }
     905             : 
     906             :             /* Should we report all attributes ? */
     907         441 :             else if (m_poReader->ReportAllAttributes() &&
     908           8 :                      !poClass->IsSchemaLocked())
     909             :             {
     910           8 :                 poState->PushPath(pszName, nLenName);
     911           8 :                 CPLString osPropName = poState->osPath;
     912           8 :                 poState->PopPath();
     913             : 
     914           8 :                 m_poReader->SetFeaturePropertyDirectly(
     915             :                     CPLSPrintf("%s@%s", osPropName.c_str(),
     916             :                                pszAttrKeyNoNS ? pszAttrKeyNoNS : pszAttrKey),
     917             :                     pszAttrVal, -1);
     918           8 :                 pszAttrVal = nullptr;
     919             :             }
     920             : 
     921         538 :         CPLFree(pszAttrKey);
     922         538 :         CPLFree(pszAttrVal);
     923         538 :     }
     924             : 
     925             : #if 0
     926             :     if( poClass->IsSchemaLocked() )
     927             :     {
     928             :         poState->PushPath( pszName, nLenName );
     929             :         CPLString osPath = poState->osPath;
     930             :         poState->PopPath();
     931             :         /* Find fields that match an attribute that is missing */
     932             :         for(int i=0; i < poClass->GetPropertyCount(); i++ )
     933             :         {
     934             :             GMLPropertyDefn* poProp = poClass->GetProperty(i);
     935             :             const char* pszSrcElement = poProp->GetSrcElement();
     936             :             if( poProp->GetType() == OFTStringList &&
     937             :                 poProp->GetSrcElementLen() > osPath.size() &&
     938             :                 strncmp(pszSrcElement, osPath, osPath.size()) == 0 &&
     939             :                 pszSrcElement[osPath.size()] == '@' )
     940             :             {
     941             :                 char* pszAttrVal = GetAttributeValue(attr, pszSrcElement + osPath.size() + 1);
     942             :                 if( pszAttrVal == NULL )
     943             :                 {
     944             :                     const char* pszCond = poProp->GetCondition();
     945             :                     if( pszCond == NULL || IsConditionMatched(pszCond, attr) )
     946             :                     {
     947             :                         m_poReader->SetFeaturePropertyDirectly( NULL, CPLStrdup(""), i );
     948             :                     }
     949             :                 }
     950             :                 else
     951             :                     CPLFree(pszAttrVal);
     952             :             }
     953             :         }
     954             :     }
     955             : #endif
     956       10882 : }
     957             : 
     958             : /************************************************************************/
     959             : /*                        IsConditionMatched()                          */
     960             : /************************************************************************/
     961             : 
     962             : /* FIXME! 'and' / 'or' operators are evaluated left to right, without */
     963             : /* and precedence rules between them ! */
     964             : 
     965          15 : bool GMLHandler::IsConditionMatched(const char *pszCondition, void *attr)
     966             : {
     967          15 :     if (pszCondition == nullptr)
     968           0 :         return true;
     969             : 
     970          15 :     bool bSyntaxError = false;
     971          30 :     CPLString osCondAttr, osCondVal;
     972          15 :     const char *pszIter = pszCondition;
     973          15 :     bool bOpEqual = true;
     974          19 :     while (*pszIter == ' ')
     975           4 :         pszIter++;
     976          15 :     if (*pszIter != '@')
     977           0 :         bSyntaxError = true;
     978             :     else
     979             :     {
     980          15 :         pszIter++;
     981          75 :         while (*pszIter != '\0' && *pszIter != ' ' && *pszIter != '!' &&
     982          65 :                *pszIter != '=')
     983             :         {
     984          60 :             osCondAttr += *pszIter;
     985          60 :             pszIter++;
     986             :         }
     987          15 :         while (*pszIter == ' ')
     988           0 :             pszIter++;
     989             : 
     990          15 :         if (*pszIter == '!')
     991             :         {
     992          10 :             bOpEqual = false;
     993          10 :             pszIter++;
     994             :         }
     995             : 
     996          15 :         if (*pszIter != '=')
     997           0 :             bSyntaxError = true;
     998             :         else
     999             :         {
    1000          15 :             pszIter++;
    1001          15 :             while (*pszIter == ' ')
    1002           0 :                 pszIter++;
    1003          15 :             if (*pszIter != '\'')
    1004           0 :                 bSyntaxError = true;
    1005             :             else
    1006             :             {
    1007          15 :                 pszIter++;
    1008          45 :                 while (*pszIter != '\0' && *pszIter != '\'')
    1009             :                 {
    1010          30 :                     osCondVal += *pszIter;
    1011          30 :                     pszIter++;
    1012             :                 }
    1013          15 :                 if (*pszIter != '\'')
    1014           0 :                     bSyntaxError = true;
    1015             :                 else
    1016             :                 {
    1017          15 :                     pszIter++;
    1018          21 :                     while (*pszIter == ' ')
    1019           6 :                         pszIter++;
    1020             :                 }
    1021             :             }
    1022             :         }
    1023             :     }
    1024             : 
    1025          15 :     if (bSyntaxError)
    1026             :     {
    1027           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1028             :                  "Invalid condition : %s. Must be of the form "
    1029             :                  "@attrname[!]='attrvalue' [and|or other_cond]*. "
    1030             :                  "'and' and 'or' operators cannot be mixed",
    1031             :                  pszCondition);
    1032           0 :         return false;
    1033             :     }
    1034             : 
    1035          15 :     char *pszVal = GetAttributeValue(attr, osCondAttr);
    1036          15 :     if (pszVal == nullptr)
    1037           0 :         pszVal = CPLStrdup("");
    1038          28 :     const bool bCondMet = (bOpEqual && strcmp(pszVal, osCondVal) == 0) ||
    1039          13 :                           (!bOpEqual && strcmp(pszVal, osCondVal) != 0);
    1040          15 :     CPLFree(pszVal);
    1041          15 :     if (*pszIter == '\0')
    1042           9 :         return bCondMet;
    1043             : 
    1044           6 :     if (STARTS_WITH(pszIter, "and"))
    1045             :     {
    1046           6 :         pszIter += 3;
    1047           6 :         if (!bCondMet)
    1048           2 :             return false;
    1049           4 :         return IsConditionMatched(pszIter, attr);
    1050             :     }
    1051             : 
    1052           0 :     if (STARTS_WITH(pszIter, "or"))
    1053             :     {
    1054           0 :         pszIter += 2;
    1055           0 :         if (bCondMet)
    1056           0 :             return true;
    1057           0 :         return IsConditionMatched(pszIter, attr);
    1058             :     }
    1059             : 
    1060           0 :     CPLError(
    1061             :         CE_Failure, CPLE_NotSupported,
    1062             :         "Invalid condition : %s. Must be of the form @attrname[!]='attrvalue' "
    1063             :         "[and|or other_cond]*. 'and' and 'or' operators cannot be mixed",
    1064             :         pszCondition);
    1065           0 :     return false;
    1066             : }
    1067             : 
    1068             : /************************************************************************/
    1069             : /*                FindRealPropertyByCheckingConditions()                */
    1070             : /************************************************************************/
    1071             : 
    1072        2695 : int GMLHandler::FindRealPropertyByCheckingConditions(int nIdx, void *attr)
    1073             : {
    1074        2695 :     GMLReadState *poState = m_poReader->GetState();
    1075        2695 :     GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
    1076             : 
    1077        2695 :     GMLPropertyDefn *poProp = poClass->GetProperty(nIdx);
    1078        2695 :     const char *pszCond = poProp->GetCondition();
    1079        2695 :     if (pszCond != nullptr && !IsConditionMatched(pszCond, attr))
    1080             :     {
    1081             :         /* try other attributes with same source element, but with different */
    1082             :         /* condition */
    1083           4 :         const char *pszSrcElement = poProp->GetSrcElement();
    1084           4 :         nIdx = -1;
    1085          11 :         for (int i = m_nAttributeIndex + 1; i < poClass->GetPropertyCount();
    1086             :              i++)
    1087             :         {
    1088           9 :             poProp = poClass->GetProperty(i);
    1089           9 :             if (strcmp(poProp->GetSrcElement(), pszSrcElement) == 0)
    1090             :             {
    1091           5 :                 pszCond = poProp->GetCondition();
    1092           5 :                 if (IsConditionMatched(pszCond, attr))
    1093             :                 {
    1094           2 :                     nIdx = i;
    1095           2 :                     break;
    1096             :                 }
    1097             :             }
    1098             :         }
    1099             :     }
    1100        2695 :     return nIdx;
    1101             : }
    1102             : 
    1103             : /************************************************************************/
    1104             : /*                      startElementFeatureAttribute()                  */
    1105             : /************************************************************************/
    1106             : 
    1107       13699 : OGRErr GMLHandler::startElementFeatureAttribute(const char *pszName,
    1108             :                                                 int nLenName, void *attr)
    1109             : {
    1110             :     /* Reset flag */
    1111       13699 :     m_bInCurField = false;
    1112             : 
    1113       13699 :     GMLReadState *poState = m_poReader->GetState();
    1114             : 
    1115             :     /* -------------------------------------------------------------------- */
    1116             :     /*      If we are collecting geometry, or if we determine this is a     */
    1117             :     /*      geometry element then append to the geometry info.              */
    1118             :     /* -------------------------------------------------------------------- */
    1119       13699 :     if (IsGeometryElement(pszName))
    1120             :     {
    1121             :         bool bReadGeometry;
    1122             : 
    1123             :         /* If the <GeometryElementPath> is defined in the .gfs, use it */
    1124             :         /* to read the appropriate geometry element */
    1125        2425 :         GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
    1126        2425 :         m_nGeometryPropertyIndex = 0;
    1127        3558 :         if (poClass->IsSchemaLocked() &&
    1128        1133 :             poClass->GetGeometryPropertyCount() == 0)
    1129             :         {
    1130           0 :             bReadGeometry = false;
    1131             :         }
    1132        3558 :         else if (poClass->IsSchemaLocked() &&
    1133        3558 :                  poClass->GetGeometryPropertyCount() == 1 &&
    1134         971 :                  poClass->GetGeometryProperty(0)->GetSrcElement()[0] == '\0')
    1135             :         {
    1136          64 :             bReadGeometry = true;
    1137             :         }
    1138        3430 :         else if (poClass->IsSchemaLocked() &&
    1139        1069 :                  poClass->GetGeometryPropertyCount() > 0)
    1140             :         {
    1141        1069 :             m_nGeometryPropertyIndex =
    1142        1069 :                 poClass->GetGeometryPropertyIndexBySrcElement(
    1143             :                     poState->osPath.c_str());
    1144        1069 :             bReadGeometry = (m_nGeometryPropertyIndex >= 0);
    1145             :         }
    1146        1292 :         else if (m_poReader->FetchAllGeometries())
    1147             :         {
    1148           0 :             bReadGeometry = true;
    1149             :         }
    1150        1292 :         else if (!poClass->IsSchemaLocked() && m_poReader->IsWFSJointLayer())
    1151             :         {
    1152          12 :             m_nGeometryPropertyIndex =
    1153          12 :                 poClass->GetGeometryPropertyIndexBySrcElement(
    1154             :                     poState->osPath.c_str());
    1155          12 :             if (m_nGeometryPropertyIndex < 0)
    1156             :             {
    1157           4 :                 const char *pszElement = poState->osPath.c_str();
    1158           4 :                 CPLString osFieldName;
    1159             :                 /* Strip member| prefix. Should always be true normally */
    1160           4 :                 if (STARTS_WITH(pszElement, "member|"))
    1161           4 :                     osFieldName = pszElement + strlen("member|");
    1162             : 
    1163             :                 /* Replace layer|property by layer_property */
    1164           4 :                 size_t iPos = osFieldName.find('|');
    1165           4 :                 if (iPos != std::string::npos)
    1166           4 :                     osFieldName[iPos] = '.';
    1167             : 
    1168           8 :                 poClass->AddGeometryProperty(new GMLGeometryPropertyDefn(
    1169           4 :                     osFieldName, poState->osPath.c_str(), wkbUnknown, -1,
    1170           8 :                     true));
    1171           4 :                 m_nGeometryPropertyIndex = poClass->GetGeometryPropertyCount();
    1172             :             }
    1173          12 :             bReadGeometry = true;
    1174             :         }
    1175             :         else
    1176             :         {
    1177             :             /* AIXM special case: for RouteSegment, we only want to read Curve
    1178             :              * geometries */
    1179             :             /* not 'start' and 'end' geometries */
    1180        1282 :             if (eAppSchemaType == APPSCHEMA_AIXM &&
    1181           2 :                 strcmp(poState->m_poFeature->GetClass()->GetName(),
    1182             :                        "RouteSegment") == 0)
    1183           0 :                 bReadGeometry = strcmp(pszName, "Curve") == 0;
    1184             : 
    1185             :             /* For Inspire objects : the "main" geometry is in a <geometry>
    1186             :              * element */
    1187        1280 :             else if (m_bAlreadyFoundGeometry)
    1188           2 :                 bReadGeometry = false;
    1189        1278 :             else if (strcmp(poState->osPath.c_str(), "geometry") == 0)
    1190             :             {
    1191           8 :                 m_bAlreadyFoundGeometry = true;
    1192           8 :                 bReadGeometry = true;
    1193           8 :                 m_nGeometryPropertyIndex =
    1194           8 :                     poClass->GetGeometryPropertyIndexBySrcElement(
    1195             :                         poState->osPath.c_str());
    1196           8 :                 if (m_nGeometryPropertyIndex < 0)
    1197             :                 {
    1198           6 :                     poClass->AddGeometryProperty(new GMLGeometryPropertyDefn(
    1199           3 :                         "geometry", poState->osPath.c_str(), wkbUnknown, -1,
    1200           3 :                         true));
    1201           3 :                     m_nGeometryPropertyIndex =
    1202           3 :                         poClass->GetGeometryPropertyCount();
    1203             :                 }
    1204             :             }
    1205             : 
    1206             :             else
    1207             :             {
    1208        2540 :                 if (!poClass->IsSchemaLocked() &&
    1209        1270 :                     poClass->IsConsistentSingleGeomElemPath())
    1210             :                 {
    1211             :                     const std::string &osGeomElemPath =
    1212        1262 :                         poClass->GetSingleGeomElemPath();
    1213        1262 :                     if (osGeomElemPath.empty())
    1214             :                     {
    1215         249 :                         poClass->SetSingleGeomElemPath(poState->osPath);
    1216             :                     }
    1217        1013 :                     else if (poState->osPath != osGeomElemPath)
    1218             :                     {
    1219           4 :                         poClass->SetConsistentSingleGeomElemPath(false);
    1220           4 :                         poClass->SetSingleGeomElemPath(std::string());
    1221             :                     }
    1222             :                 }
    1223        1270 :                 bReadGeometry = true;
    1224             :             }
    1225             :         }
    1226        2425 :         if (bReadGeometry)
    1227             :         {
    1228        2420 :             m_nGeometryDepth = m_nDepth;
    1229             : 
    1230        2420 :             CPLAssert(apsXMLNode.empty());
    1231             : 
    1232             :             NodeLastChild sNodeLastChild;
    1233        2420 :             sNodeLastChild.psNode = nullptr;
    1234        2420 :             sNodeLastChild.psLastChild = nullptr;
    1235        2420 :             apsXMLNode.push_back(sNodeLastChild);
    1236             : 
    1237        2420 :             PUSH_STATE(STATE_GEOMETRY);
    1238             : 
    1239        2420 :             return startElementGeometry(pszName, nLenName, attr);
    1240             :         }
    1241             :     }
    1242       12232 :     else if (nLenName == 9 && strcmp(pszName, "boundedBy") == 0 &&
    1243             :              // We ignore the UseBBOX() flag for CityGML, since CityGML
    1244             :              // has elements like bldg:boundedBy, which are not a simple
    1245             :              // rectangular bbox. This is needed to read properly
    1246             :              // autotest/ogr/data/gml/citygml_lod2_713_5322.xml
    1247             :              // (this is a workaround of not being namespace aware)
    1248         958 :              (eAppSchemaType == APPSCHEMA_CITYGML || m_poReader->UseBBOX()))
    1249             :     {
    1250          74 :         m_inBoundedByDepth = m_nDepth;
    1251             : 
    1252          74 :         CPLAssert(apsXMLNode.empty());
    1253             : 
    1254             :         NodeLastChild sNodeLastChild;
    1255          74 :         sNodeLastChild.psNode = nullptr;
    1256          74 :         sNodeLastChild.psLastChild = nullptr;
    1257          74 :         apsXMLNode.push_back(sNodeLastChild);
    1258             : 
    1259          74 :         PUSH_STATE(STATE_BOUNDED_BY_IN_FEATURE);
    1260             : 
    1261          74 :         return OGRERR_NONE;
    1262             :     }
    1263             : 
    1264             :     /* -------------------------------------------------------------------- */
    1265             :     /*      Is it a CityGML generic attribute ?                             */
    1266             :     /* -------------------------------------------------------------------- */
    1267       11332 :     else if (eAppSchemaType == APPSCHEMA_CITYGML &&
    1268         132 :              m_poReader->IsCityGMLGenericAttributeElement(pszName, attr))
    1269             :     {
    1270          42 :         CPLFree(m_pszCityGMLGenericAttrName);
    1271          42 :         m_pszCityGMLGenericAttrName = GetAttributeValue(attr, "name");
    1272          42 :         m_inCityGMLGenericAttrDepth = m_nDepth;
    1273             : 
    1274          42 :         PUSH_STATE(STATE_CITYGML_ATTRIBUTE);
    1275             : 
    1276          42 :         return OGRERR_NONE;
    1277             :     }
    1278             : 
    1279       11158 :     else if (m_poReader->IsWFSJointLayer() && m_nDepth == m_nDepthFeature + 1)
    1280             :     {
    1281             :     }
    1282             : 
    1283       11034 :     else if (m_poReader->IsWFSJointLayer() && m_nDepth == m_nDepthFeature + 2)
    1284             :     {
    1285         124 :         const char *pszFID = GetFID(attr);
    1286         124 :         if (pszFID)
    1287             :         {
    1288         124 :             poState->PushPath(pszName, nLenName);
    1289         248 :             CPLString osPropPath = poState->osPath + "@id";
    1290         124 :             poState->PopPath();
    1291         124 :             m_poReader->SetFeaturePropertyDirectly(osPropPath,
    1292             :                                                    CPLStrdup(pszFID), -1);
    1293             :         }
    1294             :     }
    1295             : 
    1296             :     /* -------------------------------------------------------------------- */
    1297             :     /*      If it is (or at least potentially is) a simple attribute,       */
    1298             :     /*      then start collecting it.                                       */
    1299             :     /* -------------------------------------------------------------------- */
    1300       10910 :     else if ((m_nAttributeIndex = m_poReader->GetAttributeElementIndex(
    1301       10910 :                   pszName, nLenName)) != -1)
    1302             :     {
    1303        8535 :         GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
    1304       13838 :         if (poClass->IsSchemaLocked() &&
    1305        2652 :             (poClass->GetProperty(m_nAttributeIndex)->GetType() ==
    1306        2651 :                  GMLPT_FeatureProperty ||
    1307        2651 :              poClass->GetProperty(m_nAttributeIndex)->GetType() ==
    1308             :                  GMLPT_FeaturePropertyList))
    1309             :         {
    1310          24 :             m_nAttributeDepth = m_nDepth;
    1311          24 :             PUSH_STATE(STATE_FEATUREPROPERTY);
    1312             :         }
    1313             :         else
    1314             :         {
    1315             :             /* Is this a property with a condition on an attribute value ? */
    1316        8511 :             if (poClass->IsSchemaLocked())
    1317             :             {
    1318        2628 :                 m_nAttributeIndex = FindRealPropertyByCheckingConditions(
    1319             :                     m_nAttributeIndex, attr);
    1320             :             }
    1321             : 
    1322        8511 :             if (m_nAttributeIndex >= 0)
    1323             :             {
    1324        8511 :                 if (m_pszCurField)
    1325             :                 {
    1326         883 :                     CPLFree(m_pszCurField);
    1327         883 :                     m_pszCurField = nullptr;
    1328         883 :                     m_nCurFieldLen = 0;
    1329         883 :                     m_nCurFieldAlloc = 0;
    1330             :                 }
    1331        8511 :                 m_bInCurField = true;
    1332             : 
    1333        8511 :                 char *pszXSINil = GetAttributeValue(attr, "xsi:nil");
    1334        8511 :                 if (pszXSINil)
    1335             :                 {
    1336           4 :                     if (EQUAL(pszXSINil, "true"))
    1337           4 :                         m_poReader->SetFeaturePropertyDirectly(
    1338             :                             pszName, CPLStrdup(OGR_GML_NULL), -1);
    1339           4 :                     CPLFree(pszXSINil);
    1340             :                 }
    1341             :                 else
    1342             :                 {
    1343        8507 :                     DealWithAttributes(pszName, nLenName, attr);
    1344             :                 }
    1345             : 
    1346        8511 :                 if (stateStack[nStackDepth] != STATE_PROPERTY)
    1347             :                 {
    1348        6850 :                     m_nAttributeDepth = m_nDepth;
    1349        6850 :                     PUSH_STATE(STATE_PROPERTY);
    1350             :                 }
    1351             :             }
    1352             :             /*else
    1353             :             {
    1354             :                 DealWithAttributes(pszName, nLenName, attr);
    1355             :             }*/
    1356             :         }
    1357             :     }
    1358             :     else
    1359             :     {
    1360        2375 :         DealWithAttributes(pszName, nLenName, attr);
    1361             :     }
    1362             : 
    1363       11163 :     poState->PushPath(pszName, nLenName);
    1364             : 
    1365       11163 :     return OGRERR_NONE;
    1366             : }
    1367             : 
    1368             : /************************************************************************/
    1369             : /*                         startElementTop()                            */
    1370             : /************************************************************************/
    1371             : 
    1372         517 : OGRErr GMLHandler::startElementTop(const char *pszName, int /*nLenName*/,
    1373             :                                    void *attr)
    1374             : {
    1375         517 :     if (strcmp(pszName, "CityModel") == 0)
    1376             :     {
    1377           6 :         eAppSchemaType = APPSCHEMA_CITYGML;
    1378             :         // Default to 3D geometries for CityGML (#6989)
    1379           6 :         if (m_nSRSDimensionIfMissing == 0)
    1380           6 :             m_nSRSDimensionIfMissing = 3;
    1381             :     }
    1382         511 :     else if (strcmp(pszName, "AIXMBasicMessage") == 0)
    1383             :     {
    1384           2 :         eAppSchemaType = APPSCHEMA_AIXM;
    1385           2 :         m_bReportHref = true;
    1386             :     }
    1387         509 :     else if (strcmp(pszName, "Maastotiedot") == 0)
    1388             :     {
    1389           8 :         eAppSchemaType = APPSCHEMA_MTKGML;
    1390             : 
    1391           8 :         char *pszSRSName = GetAttributeValue(attr, "srsName");
    1392           8 :         m_poReader->SetGlobalSRSName(pszSRSName);
    1393           8 :         CPLFree(pszSRSName);
    1394             : 
    1395           8 :         m_bReportHref = true;
    1396             : 
    1397             :         /* the schemas of MTKGML don't have (string) width, so don't set it */
    1398           8 :         m_poReader->SetWidthFlag(false);
    1399             :     }
    1400             : 
    1401         517 :     stateStack[0] = STATE_DEFAULT;
    1402             : 
    1403         517 :     return OGRERR_NONE;
    1404             : }
    1405             : 
    1406             : /************************************************************************/
    1407             : /*                        startElementDefault()                         */
    1408             : /************************************************************************/
    1409             : 
    1410        6245 : OGRErr GMLHandler::startElementDefault(const char *pszName, int nLenName,
    1411             :                                        void *attr)
    1412             : 
    1413             : {
    1414             :     /* -------------------------------------------------------------------- */
    1415             :     /*      Is it a feature?  If so push a whole new state, and return.     */
    1416             :     /* -------------------------------------------------------------------- */
    1417             :     int nClassIndex;
    1418        6245 :     const char *pszFilteredClassName = nullptr;
    1419             : 
    1420        6245 :     if (nLenName == 9 && strcmp(pszName, "boundedBy") == 0)
    1421             :     {
    1422         236 :         m_inBoundedByDepth = m_nDepth;
    1423             : 
    1424         236 :         PUSH_STATE(STATE_BOUNDED_BY);
    1425             : 
    1426         236 :         return OGRERR_NONE;
    1427             :     }
    1428             : 
    1429        6830 :     else if (m_poReader->ShouldLookForClassAtAnyLevel() &&
    1430         821 :              (pszFilteredClassName = m_poReader->GetFilteredClassName()) !=
    1431             :                  nullptr)
    1432             :     {
    1433         821 :         if (strcmp(pszName, pszFilteredClassName) == 0)
    1434             :         {
    1435          49 :             m_poReader->PushFeature(pszName, GetFID(attr),
    1436          49 :                                     m_poReader->GetFilteredClassIndex());
    1437             : 
    1438          49 :             m_nDepthFeature = m_nDepth;
    1439             : 
    1440          49 :             PUSH_STATE(STATE_FEATURE);
    1441             : 
    1442          49 :             return OGRERR_NONE;
    1443             :         }
    1444             :     }
    1445             : 
    1446             :     /* WFS 2.0 GetFeature documents have a wfs:FeatureCollection */
    1447             :     /* as a wfs:member of the top wfs:FeatureCollection. We don't want this */
    1448             :     /* wfs:FeatureCollection to be recognized as a feature */
    1449        5205 :     else if ((!(nLenName == static_cast<int>(strlen("FeatureCollection")) &&
    1450       10366 :                 strcmp(pszName, "FeatureCollection") == 0)) &&
    1451        5178 :              (nClassIndex = m_poReader->GetFeatureElementIndex(
    1452             :                   pszName, nLenName, eAppSchemaType)) != -1)
    1453             :     {
    1454        2773 :         m_bAlreadyFoundGeometry = false;
    1455             : 
    1456        2773 :         pszFilteredClassName = m_poReader->GetFilteredClassName();
    1457        2773 :         if (pszFilteredClassName != nullptr &&
    1458         377 :             strcmp(pszName, pszFilteredClassName) != 0)
    1459             :         {
    1460         345 :             m_nDepthFeature = m_nDepth;
    1461             : 
    1462         345 :             PUSH_STATE(STATE_IGNORED_FEATURE);
    1463             : 
    1464         345 :             return OGRERR_NONE;
    1465             :         }
    1466             :         else
    1467             :         {
    1468        2428 :             if (eAppSchemaType == APPSCHEMA_MTKGML)
    1469             :             {
    1470          22 :                 m_poReader->PushFeature(pszName, nullptr, nClassIndex);
    1471             : 
    1472          22 :                 char *pszGID = GetAttributeValue(attr, "gid");
    1473          22 :                 if (pszGID)
    1474          21 :                     m_poReader->SetFeaturePropertyDirectly("gid", pszGID, -1,
    1475             :                                                            GMLPT_String);
    1476             :             }
    1477             :             else
    1478        2406 :                 m_poReader->PushFeature(pszName, GetFID(attr), nClassIndex);
    1479             : 
    1480        2428 :             m_nDepthFeature = m_nDepth;
    1481             : 
    1482        2428 :             PUSH_STATE(STATE_FEATURE);
    1483             : 
    1484        2428 :             return OGRERR_NONE;
    1485             :         }
    1486             :     }
    1487             : 
    1488             :     /* -------------------------------------------------------------------- */
    1489             :     /*      Push the element onto the current state's path.                 */
    1490             :     /* -------------------------------------------------------------------- */
    1491        3187 :     m_poReader->GetState()->PushPath(pszName, nLenName);
    1492             : 
    1493        3187 :     return OGRERR_NONE;
    1494             : }
    1495             : 
    1496             : /************************************************************************/
    1497             : /*                      endElementIgnoredFeature()                      */
    1498             : /************************************************************************/
    1499             : 
    1500        4348 : OGRErr GMLHandler::endElementIgnoredFeature()
    1501             : 
    1502             : {
    1503        4348 :     if (m_nDepth == m_nDepthFeature)
    1504             :     {
    1505         345 :         POP_STATE();
    1506             :     }
    1507        4348 :     return OGRERR_NONE;
    1508             : }
    1509             : 
    1510             : /************************************************************************/
    1511             : /*                         endElementBoundedBy()                        */
    1512             : /************************************************************************/
    1513        1141 : OGRErr GMLHandler::endElementBoundedBy()
    1514             : 
    1515             : {
    1516        1141 :     if (m_inBoundedByDepth == m_nDepth)
    1517             :     {
    1518         236 :         POP_STATE();
    1519             :     }
    1520             : 
    1521        1141 :     return OGRERR_NONE;
    1522             : }
    1523             : 
    1524             : /************************************************************************/
    1525             : /*                     endElementBoundedByInFeature()                   */
    1526             : /************************************************************************/
    1527        1130 : OGRErr GMLHandler::endElementBoundedByInFeature()
    1528             : 
    1529             : {
    1530        1130 :     if (m_nDepth > m_inBoundedByDepth)
    1531             :     {
    1532        1057 :         if (m_nDepth == m_inBoundedByDepth + 1)
    1533             :         {
    1534          74 :             m_nGeometryDepth = m_nDepth;
    1535             :         }
    1536        1057 :         return endElementGeometry();
    1537             :     }
    1538             :     else
    1539             :     {
    1540          73 :         POP_STATE();
    1541          73 :         if (apsXMLNode.size() >= 2 && apsXMLNode[1].psNode != nullptr)
    1542           0 :             CPLDestroyXMLNode(apsXMLNode[1].psNode);
    1543          73 :         apsXMLNode.clear();
    1544          73 :         return OGRERR_NONE;
    1545             :     }
    1546             : }
    1547             : 
    1548             : /************************************************************************/
    1549             : /*                       ParseAIXMElevationPoint()                      */
    1550             : /************************************************************************/
    1551             : 
    1552           0 : CPLXMLNode *GMLHandler::ParseAIXMElevationPoint(CPLXMLNode *psGML)
    1553             : {
    1554           0 :     const char *pszElevation = CPLGetXMLValue(psGML, "elevation", nullptr);
    1555           0 :     if (pszElevation)
    1556             :     {
    1557           0 :         m_poReader->SetFeaturePropertyDirectly("elevation",
    1558             :                                                CPLStrdup(pszElevation), -1);
    1559             :         const char *pszElevationUnit =
    1560           0 :             CPLGetXMLValue(psGML, "elevation.uom", nullptr);
    1561           0 :         if (pszElevationUnit)
    1562             :         {
    1563           0 :             m_poReader->SetFeaturePropertyDirectly(
    1564             :                 "elevation_uom", CPLStrdup(pszElevationUnit), -1);
    1565             :         }
    1566             :     }
    1567             : 
    1568             :     const char *pszGeoidUndulation =
    1569           0 :         CPLGetXMLValue(psGML, "geoidUndulation", nullptr);
    1570           0 :     if (pszGeoidUndulation)
    1571             :     {
    1572           0 :         m_poReader->SetFeaturePropertyDirectly(
    1573             :             "geoidUndulation", CPLStrdup(pszGeoidUndulation), -1);
    1574             :         const char *pszGeoidUndulationUnit =
    1575           0 :             CPLGetXMLValue(psGML, "geoidUndulation.uom", nullptr);
    1576           0 :         if (pszGeoidUndulationUnit)
    1577             :         {
    1578           0 :             m_poReader->SetFeaturePropertyDirectly(
    1579             :                 "geoidUndulation_uom", CPLStrdup(pszGeoidUndulationUnit), -1);
    1580             :         }
    1581             :     }
    1582             : 
    1583           0 :     const char *pszPos = CPLGetXMLValue(psGML, "pos", nullptr);
    1584           0 :     const char *pszCoordinates = CPLGetXMLValue(psGML, "coordinates", nullptr);
    1585           0 :     if (pszPos != nullptr || pszCoordinates != nullptr)
    1586             :     {
    1587           0 :         CPLFree(psGML->pszValue);
    1588           0 :         psGML->pszValue = CPLStrdup("gml:Point");
    1589             :     }
    1590             :     else
    1591             :     {
    1592           0 :         CPLDestroyXMLNode(psGML);
    1593           0 :         psGML = nullptr;
    1594             :     }
    1595             : 
    1596           0 :     return psGML;
    1597             : }
    1598             : 
    1599             : /************************************************************************/
    1600             : /*                         endElementGeometry()                         */
    1601             : /************************************************************************/
    1602       22077 : OGRErr GMLHandler::endElementGeometry()
    1603             : 
    1604             : {
    1605       22077 :     if (m_nGeomLen)
    1606             :     {
    1607             :         CPLXMLNode *psNode =
    1608        3770 :             static_cast<CPLXMLNode *>(CPLCalloc(sizeof(CPLXMLNode), 1));
    1609        3770 :         psNode->eType = CXT_Text;
    1610        3770 :         psNode->pszValue = m_pszGeometry;
    1611             : 
    1612        3770 :         NodeLastChild &sNodeLastChild = apsXMLNode.back();
    1613        3770 :         CPLXMLNode *psLastChildParent = sNodeLastChild.psLastChild;
    1614        3770 :         if (psLastChildParent == nullptr)
    1615             :         {
    1616        2153 :             CPLXMLNode *psParent = sNodeLastChild.psNode;
    1617        2153 :             if (psParent)
    1618        2153 :                 psParent->psChild = psNode;
    1619             :         }
    1620             :         else
    1621        1617 :             psLastChildParent->psNext = psNode;
    1622        3770 :         sNodeLastChild.psLastChild = psNode;
    1623             : 
    1624        3770 :         m_pszGeometry = nullptr;
    1625        3770 :         m_nGeomAlloc = 0;
    1626        3770 :         m_nGeomLen = 0;
    1627             :     }
    1628             : 
    1629       22077 :     CPLXMLNode *psThisNode = apsXMLNode.back().psNode;
    1630       22077 :     CPLXMLNode *psThisNodeChild = psThisNode->psChild;
    1631       26746 :     if (!m_oMapElementToSubstitute.empty() && psThisNodeChild &&
    1632        4669 :         psThisNodeChild->eType == CXT_Attribute &&
    1633       27466 :         strcmp(psThisNodeChild->pszValue, "gml:id") == 0 &&
    1634         720 :         psThisNodeChild->psChild->pszValue)
    1635             :     {
    1636             :         auto oIter =
    1637         720 :             m_oMapElementToSubstitute.find(psThisNodeChild->psChild->pszValue);
    1638         720 :         if (oIter != m_oMapElementToSubstitute.end())
    1639             :         {
    1640         109 :             auto psLastChild = oIter->second->psChild;
    1641         109 :             if (psLastChild)
    1642             :             {
    1643             :                 // CPLDebug("GML", "Substitution of xlink:href=\"#%s\" with actual content", psThisNodeChild->psChild->pszValue);
    1644         109 :                 CPLXMLNode *psAfter = psThisNode->psNext;
    1645         109 :                 psThisNode->psNext = nullptr;
    1646             :                 // We can patch oIter->second as it stored as it in the current
    1647             :                 // GMLFeature.
    1648             :                 // Of course that would no longer be the case in case of
    1649             :                 // cross-references between different GMLFeature, hence we clear
    1650             :                 // m_oMapElementToSubstitute at the end of the current feature.
    1651         154 :                 while (psLastChild->psNext)
    1652          45 :                     psLastChild = psLastChild->psNext;
    1653         109 :                 if (psLastChild == psThisNode)
    1654             :                 {
    1655             :                     /* Can happen in situations like:
    1656             :                      <foo xlink:href="#X">
    1657             :                         <bar gml:id="X"/>
    1658             :                      </foo>
    1659             :                      Do not attempt substitution as that would cause a memory
    1660             :                      leak.
    1661             :                     */
    1662             :                 }
    1663             :                 else
    1664             :                 {
    1665         108 :                     psLastChild->psNext = CPLCloneXMLTree(psThisNode);
    1666             :                 }
    1667         109 :                 psThisNode->psNext = psAfter;
    1668             :             }
    1669             :         }
    1670             :     }
    1671             : 
    1672       22077 :     if (m_nDepth == m_nGeometryDepth)
    1673             :     {
    1674        2492 :         m_nGeometryDepth = 0;
    1675             : 
    1676        2492 :         CPLAssert(apsXMLNode.size() == 2);
    1677        2492 :         CPLXMLNode *psInterestNode = apsXMLNode.back().psNode;
    1678             : 
    1679             :         /*char* pszXML = CPLSerializeXMLTree(psInterestNode);
    1680             :         CPLDebug("GML", "geometry = %s", pszXML);
    1681             :         CPLFree(pszXML);*/
    1682             : 
    1683        2492 :         apsXMLNode.pop_back();
    1684             : 
    1685             :         /* AIXM ElevatedPoint. We want to parse this */
    1686             :         /* a bit specially because ElevatedPoint is aixm: stuff and */
    1687             :         /* the srsDimension of the <gml:pos> can be set to true although */
    1688             :         /* they are only 2 coordinates in practice */
    1689        2492 :         if (eAppSchemaType == APPSCHEMA_AIXM && psInterestNode != nullptr &&
    1690           2 :             strcmp(psInterestNode->pszValue, "ElevatedPoint") == 0)
    1691             :         {
    1692           0 :             psInterestNode = ParseAIXMElevationPoint(psInterestNode);
    1693             :         }
    1694        2492 :         else if (eAppSchemaType == APPSCHEMA_MTKGML &&
    1695             :                  psInterestNode != nullptr)
    1696             :         {
    1697          36 :             if (strcmp(psInterestNode->pszValue, "Murtoviiva") == 0)
    1698             :             {
    1699           7 :                 CPLFree(psInterestNode->pszValue);
    1700           7 :                 psInterestNode->pszValue = CPLStrdup("gml:LineString");
    1701             :             }
    1702          29 :             else if (strcmp(psInterestNode->pszValue, "Alue") == 0)
    1703             :             {
    1704           7 :                 CPLFree(psInterestNode->pszValue);
    1705           7 :                 psInterestNode->pszValue = CPLStrdup("gml:Polygon");
    1706             :             }
    1707          22 :             else if (strcmp(psInterestNode->pszValue, "Piste") == 0)
    1708             :             {
    1709          21 :                 CPLFree(psInterestNode->pszValue);
    1710          21 :                 psInterestNode->pszValue = CPLStrdup("gml:Point");
    1711             :             }
    1712             :         }
    1713        2456 :         else if (psInterestNode != nullptr &&
    1714        2456 :                  strcmp(psInterestNode->pszValue, "BoundingBox") == 0)
    1715             :         {
    1716          27 :             CPLFree(psInterestNode->pszValue);
    1717          27 :             psInterestNode->pszValue = CPLStrdup("Envelope");
    1718          30 :             for (CPLXMLNode *psChild = psInterestNode->psChild; psChild;
    1719           3 :                  psChild = psChild->psNext)
    1720             :             {
    1721          29 :                 if (psChild->eType == CXT_Attribute &&
    1722          27 :                     strcmp(psChild->pszValue, "crs") == 0)
    1723             :                 {
    1724          26 :                     CPLFree(psChild->pszValue);
    1725          26 :                     psChild->pszValue = CPLStrdup("srsName");
    1726          26 :                     break;
    1727             :                 }
    1728             :             }
    1729             :         }
    1730             : 
    1731        2492 :         GMLFeature *poGMLFeature = m_poReader->GetState()->m_poFeature;
    1732        2492 :         if (stateStack[nStackDepth] == STATE_BOUNDED_BY_IN_FEATURE)
    1733             :         {
    1734          74 :             if (eAppSchemaType == APPSCHEMA_CITYGML)
    1735          54 :                 CPLDestroyXMLNode(psInterestNode);
    1736             :             else
    1737          20 :                 poGMLFeature->SetBoundedByGeometry(psInterestNode);
    1738             :         }
    1739             :         else
    1740             :         {
    1741        2418 :             if (m_poReader->FetchAllGeometries())
    1742           0 :                 poGMLFeature->AddGeometry(psInterestNode);
    1743             :             else
    1744             :             {
    1745        2418 :                 GMLFeatureClass *poClass = poGMLFeature->GetClass();
    1746        2418 :                 if (poClass->GetGeometryPropertyCount() > 1)
    1747             :                 {
    1748         172 :                     if (poGMLFeature->GetGeometryRef(m_nGeometryPropertyIndex))
    1749             :                     {
    1750             :                         // If we have already a geometry, setting a new one
    1751             :                         // will invalidate nodes potentially stored in
    1752             :                         // m_oMapElementToSubstitute, so clear it
    1753           0 :                         m_oMapElementToSubstitute.clear();
    1754             :                     }
    1755         172 :                     poGMLFeature->SetGeometryDirectly(m_nGeometryPropertyIndex,
    1756             :                                                       psInterestNode);
    1757             :                 }
    1758             :                 else
    1759             :                 {
    1760        2246 :                     if (poGMLFeature->GetGeometryRef(0))
    1761             :                     {
    1762             :                         // If we have already a geometry, setting a new one
    1763             :                         // will invalidate nodes potentially stored in
    1764             :                         // m_oMapElementToSubstitute, so clear it
    1765          16 :                         m_oMapElementToSubstitute.clear();
    1766             :                     }
    1767        2246 :                     poGMLFeature->SetGeometryDirectly(psInterestNode);
    1768             :                 }
    1769             :             }
    1770             : 
    1771        2418 :             POP_STATE();
    1772             :         }
    1773             :     }
    1774             : 
    1775       22077 :     apsXMLNode.pop_back();
    1776             : 
    1777       22077 :     return OGRERR_NONE;
    1778             : }
    1779             : 
    1780             : /************************************************************************/
    1781             : /*                    endElementCityGMLGenericAttr()                    */
    1782             : /************************************************************************/
    1783          84 : OGRErr GMLHandler::endElementCityGMLGenericAttr()
    1784             : 
    1785             : {
    1786          84 :     if (m_pszCityGMLGenericAttrName != nullptr && m_bInCurField)
    1787             :     {
    1788          42 :         if (m_pszCurField != nullptr)
    1789             :         {
    1790          42 :             m_poReader->SetFeaturePropertyDirectly(m_pszCityGMLGenericAttrName,
    1791             :                                                    m_pszCurField, -1);
    1792             :         }
    1793          42 :         m_pszCurField = nullptr;
    1794          42 :         m_nCurFieldLen = 0;
    1795          42 :         m_nCurFieldAlloc = 0;
    1796          42 :         m_bInCurField = false;
    1797          42 :         CPLFree(m_pszCityGMLGenericAttrName);
    1798          42 :         m_pszCityGMLGenericAttrName = nullptr;
    1799             :     }
    1800             : 
    1801          84 :     if (m_inCityGMLGenericAttrDepth == m_nDepth)
    1802             :     {
    1803          42 :         POP_STATE();
    1804             :     }
    1805             : 
    1806          84 :     return OGRERR_NONE;
    1807             : }
    1808             : 
    1809             : /************************************************************************/
    1810             : /*                        endElementAttribute()                         */
    1811             : /************************************************************************/
    1812        8451 : OGRErr GMLHandler::endElementAttribute()
    1813             : 
    1814             : {
    1815        8451 :     GMLReadState *poState = m_poReader->GetState();
    1816             : 
    1817        8451 :     if (m_bInCurField)
    1818             :     {
    1819        6196 :         if (m_pszCurField == nullptr && m_poReader->IsEmptyAsNull())
    1820             :         {
    1821          31 :             if (m_pszValue != nullptr)
    1822             :             {
    1823          10 :                 m_poReader->SetFeaturePropertyDirectly(poState->osPath.c_str(),
    1824             :                                                        m_pszValue, -1);
    1825          10 :                 m_pszValue = nullptr;
    1826             :             }
    1827             :         }
    1828             :         else
    1829             :         {
    1830        6165 :             m_poReader->SetFeaturePropertyDirectly(
    1831             :                 poState->osPath.c_str(),
    1832        6165 :                 m_pszCurField ? m_pszCurField : CPLStrdup(""),
    1833             :                 m_nAttributeIndex);
    1834        6165 :             m_pszCurField = nullptr;
    1835             :         }
    1836             : 
    1837        6196 :         if (m_pszHref != nullptr)
    1838             :         {
    1839           4 :             CPLString osPropNameHref = poState->osPath + "_href";
    1840           4 :             m_poReader->SetFeaturePropertyDirectly(osPropNameHref, m_pszHref,
    1841             :                                                    -1);
    1842           4 :             m_pszHref = nullptr;
    1843             :         }
    1844             : 
    1845        6196 :         if (m_pszUom != nullptr)
    1846             :         {
    1847          14 :             CPLString osPropNameUom = poState->osPath + "_uom";
    1848          14 :             m_poReader->SetFeaturePropertyDirectly(osPropNameUom, m_pszUom, -1);
    1849          14 :             m_pszUom = nullptr;
    1850             :         }
    1851             : 
    1852        6196 :         if (m_pszKieli != nullptr)
    1853             :         {
    1854           7 :             CPLString osPropName = poState->osPath + "_kieli";
    1855           7 :             m_poReader->SetFeaturePropertyDirectly(osPropName, m_pszKieli, -1);
    1856           7 :             m_pszKieli = nullptr;
    1857             :         }
    1858             : 
    1859        6196 :         m_nCurFieldLen = 0;
    1860        6196 :         m_nCurFieldAlloc = 0;
    1861        6196 :         m_bInCurField = false;
    1862        6196 :         m_nAttributeIndex = -1;
    1863             : 
    1864        6196 :         CPLFree(m_pszValue);
    1865        6196 :         m_pszValue = nullptr;
    1866             :     }
    1867             : 
    1868        8451 :     poState->PopPath();
    1869             : 
    1870        8451 :     if (m_nAttributeDepth == m_nDepth)
    1871             :     {
    1872        6848 :         POP_STATE();
    1873             :     }
    1874             : 
    1875        8451 :     return OGRERR_NONE;
    1876             : }
    1877             : 
    1878             : /************************************************************************/
    1879             : /*                    startElementFeatureProperty()                     */
    1880             : /************************************************************************/
    1881             : 
    1882         165 : OGRErr GMLHandler::startElementFeatureProperty(const char * /*pszName*/,
    1883             :                                                int /*nLenName*/, void *attr)
    1884             : {
    1885         165 :     if (m_nDepth == m_nAttributeDepth + 1)
    1886             :     {
    1887          24 :         const char *pszGMLId = GetFID(attr);
    1888          24 :         if (pszGMLId != nullptr)
    1889             :         {
    1890          24 :             m_poReader->SetFeaturePropertyDirectly(
    1891             :                 nullptr, CPLStrdup(CPLSPrintf("#%s", pszGMLId)),
    1892             :                 m_nAttributeIndex);
    1893             :         }
    1894             :     }
    1895             : 
    1896         165 :     return OGRERR_NONE;
    1897             : }
    1898             : 
    1899             : /************************************************************************/
    1900             : /*                      endElementFeatureProperty()                      */
    1901             : /************************************************************************/
    1902             : 
    1903         189 : OGRErr GMLHandler::endElementFeatureProperty()
    1904             : 
    1905             : {
    1906         189 :     if (m_nDepth == m_nAttributeDepth)
    1907             :     {
    1908          24 :         GMLReadState *poState = m_poReader->GetState();
    1909          24 :         poState->PopPath();
    1910             : 
    1911          24 :         POP_STATE();
    1912             :     }
    1913         189 :     return OGRERR_NONE;
    1914             : }
    1915             : 
    1916             : /************************************************************************/
    1917             : /*                          endElementFeature()                         */
    1918             : /************************************************************************/
    1919        5099 : OGRErr GMLHandler::endElementFeature()
    1920             : 
    1921             : {
    1922             :     /* -------------------------------------------------------------------- */
    1923             :     /*      If we are collecting a feature, and this element tag matches    */
    1924             :     /*      element name for the class, then we have finished the           */
    1925             :     /*      feature, and we pop the feature read state.                     */
    1926             :     /* -------------------------------------------------------------------- */
    1927        5099 :     if (m_nDepth == m_nDepthFeature)
    1928             :     {
    1929        2473 :         m_oMapElementToSubstitute.clear();
    1930        2473 :         m_poReader->PopState();
    1931             : 
    1932        2473 :         POP_STATE();
    1933             :     }
    1934             : 
    1935             :     /* -------------------------------------------------------------------- */
    1936             :     /*      Otherwise, we just pop the element off the local read states    */
    1937             :     /*      element stack.                                                  */
    1938             :     /* -------------------------------------------------------------------- */
    1939             :     else
    1940             :     {
    1941        2626 :         m_poReader->GetState()->PopPath();
    1942             :     }
    1943             : 
    1944        5099 :     return OGRERR_NONE;
    1945             : }
    1946             : 
    1947             : /************************************************************************/
    1948             : /*                          endElementDefault()                         */
    1949             : /************************************************************************/
    1950        3688 : OGRErr GMLHandler::endElementDefault()
    1951             : 
    1952             : {
    1953        3688 :     if (m_nDepth > 0)
    1954        3180 :         m_poReader->GetState()->PopPath();
    1955             : 
    1956        3688 :     return OGRERR_NONE;
    1957             : }
    1958             : 
    1959             : /************************************************************************/
    1960             : /*                         dataHandlerAttribute()                       */
    1961             : /************************************************************************/
    1962             : 
    1963       10632 : OGRErr GMLHandler::dataHandlerAttribute(const char *data, int nLen)
    1964             : 
    1965             : {
    1966       10632 :     if (!m_bInCurField)
    1967        2319 :         return OGRERR_NONE;
    1968             : 
    1969        8313 :     int nIter = 0;
    1970             : 
    1971             :     // Ignore white space.
    1972        8313 :     if (m_nCurFieldLen == 0)
    1973             :     {
    1974       17290 :         while (nIter < nLen)
    1975             :         {
    1976       15186 :             const char ch = data[nIter];
    1977       15186 :             if (!(ch == ' ' || ch == 10 || ch == 13 || ch == '\t'))
    1978        6207 :                 break;
    1979        8979 :             nIter++;
    1980             :         }
    1981             :     }
    1982             : 
    1983        8313 :     const int nCharsLen = nLen - nIter;
    1984             : 
    1985        8313 :     if (nCharsLen > INT_MAX - static_cast<int>(m_nCurFieldLen) - 1)
    1986             :     {
    1987           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
    1988             :                  "Too much data in a single element");
    1989           0 :         return OGRERR_NOT_ENOUGH_MEMORY;
    1990             :     }
    1991        8313 :     if (m_nCurFieldLen + nCharsLen + 1 > m_nCurFieldAlloc)
    1992             :     {
    1993        7135 :         if (m_nCurFieldAlloc < INT_MAX - m_nCurFieldAlloc / 3 - nCharsLen - 1)
    1994        7135 :             m_nCurFieldAlloc =
    1995        7135 :                 m_nCurFieldAlloc + m_nCurFieldAlloc / 3 + nCharsLen + 1;
    1996             :         else
    1997           0 :             m_nCurFieldAlloc = m_nCurFieldLen + nCharsLen + 1;
    1998             :         char *pszNewCurField = static_cast<char *>(
    1999        7135 :             VSI_REALLOC_VERBOSE(m_pszCurField, m_nCurFieldAlloc));
    2000        7135 :         if (pszNewCurField == nullptr)
    2001             :         {
    2002           0 :             return OGRERR_NOT_ENOUGH_MEMORY;
    2003             :         }
    2004        7135 :         m_pszCurField = pszNewCurField;
    2005             :     }
    2006        8313 :     memcpy(m_pszCurField + m_nCurFieldLen, data + nIter, nCharsLen);
    2007        8313 :     m_nCurFieldLen += nCharsLen;
    2008        8313 :     m_pszCurField[m_nCurFieldLen] = '\0';
    2009             : 
    2010        8313 :     return OGRERR_NONE;
    2011             : }
    2012             : 
    2013             : /************************************************************************/
    2014             : /*                         dataHandlerGeometry()                        */
    2015             : /************************************************************************/
    2016             : 
    2017       55053 : OGRErr GMLHandler::dataHandlerGeometry(const char *data, int nLen)
    2018             : 
    2019             : {
    2020       55053 :     int nIter = 0;
    2021             : 
    2022             :     // Ignore white space
    2023       55053 :     if (m_nGeomLen == 0)
    2024             :     {
    2025      406475 :         while (nIter < nLen)
    2026             :         {
    2027      355225 :             char ch = data[nIter];
    2028      355225 :             if (!(ch == ' ' || ch == 10 || ch == 13 || ch == '\t'))
    2029        3770 :                 break;
    2030      351455 :             nIter++;
    2031             :         }
    2032             :     }
    2033             : 
    2034       55053 :     const int nCharsLen = nLen - nIter;
    2035       55053 :     if (nCharsLen)
    2036             :     {
    2037        3803 :         if (nCharsLen > INT_MAX - static_cast<int>(m_nGeomLen) - 1)
    2038             :         {
    2039           0 :             CPLError(CE_Failure, CPLE_OutOfMemory,
    2040             :                      "Too much data in a single element");
    2041           0 :             return OGRERR_NOT_ENOUGH_MEMORY;
    2042             :         }
    2043        3803 :         if (m_nGeomLen + nCharsLen + 1 > m_nGeomAlloc)
    2044             :         {
    2045        3798 :             if (m_nGeomAlloc < INT_MAX - m_nGeomAlloc / 3 - nCharsLen - 1)
    2046        3798 :                 m_nGeomAlloc = m_nGeomAlloc + m_nGeomAlloc / 3 + nCharsLen + 1;
    2047             :             else
    2048           0 :                 m_nGeomAlloc = m_nGeomAlloc + nCharsLen + 1;
    2049             :             char *pszNewGeometry = static_cast<char *>(
    2050        3798 :                 VSI_REALLOC_VERBOSE(m_pszGeometry, m_nGeomAlloc));
    2051        3798 :             if (pszNewGeometry == nullptr)
    2052             :             {
    2053           0 :                 return OGRERR_NOT_ENOUGH_MEMORY;
    2054             :             }
    2055        3798 :             m_pszGeometry = pszNewGeometry;
    2056             :         }
    2057        3803 :         memcpy(m_pszGeometry + m_nGeomLen, data + nIter, nCharsLen);
    2058        3803 :         m_nGeomLen += nCharsLen;
    2059        3803 :         m_pszGeometry[m_nGeomLen] = '\0';
    2060             :     }
    2061             : 
    2062       55053 :     return OGRERR_NONE;
    2063             : }
    2064             : 
    2065             : /************************************************************************/
    2066             : /*                         IsGeometryElement()                          */
    2067             : /************************************************************************/
    2068             : 
    2069       13699 : bool GMLHandler::IsGeometryElement(const char *pszElement)
    2070             : 
    2071             : {
    2072       13699 :     int nFirst = 0;
    2073       13699 :     int nLast = GML_GEOMETRY_TYPE_COUNT - 1;
    2074       13699 :     unsigned long nHash = CPLHashSetHashStr(pszElement);
    2075       47524 :     do
    2076             :     {
    2077       61223 :         const int nMiddle = (nFirst + nLast) / 2;
    2078       61223 :         if (nHash == pasGeometryNames[nMiddle].nHash)
    2079        2388 :             return strcmp(pszElement, pasGeometryNames[nMiddle].pszName) == 0;
    2080       58835 :         if (nHash < pasGeometryNames[nMiddle].nHash)
    2081       33496 :             nLast = nMiddle - 1;
    2082             :         else
    2083       25339 :             nFirst = nMiddle + 1;
    2084       58835 :     } while (nFirst <= nLast);
    2085             : 
    2086       11311 :     if (eAppSchemaType == APPSCHEMA_AIXM &&
    2087          10 :         (strcmp(pszElement, "ElevatedPoint") == 0 ||
    2088          10 :          strcmp(pszElement, "ElevatedSurface") == 0))
    2089             :     {
    2090           2 :         return true;
    2091             :     }
    2092             : 
    2093       11309 :     if (eAppSchemaType == APPSCHEMA_MTKGML &&
    2094          77 :         (strcmp(pszElement, "Piste") == 0 || strcmp(pszElement, "Alue") == 0 ||
    2095          49 :          strcmp(pszElement, "Murtoviiva") == 0))
    2096          35 :         return true;
    2097             : 
    2098       11274 :     return false;
    2099             : }

Generated by: LCOV version 1.14