LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gml - gmlreader.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 583 641 91.0 %
Date: 2024-11-21 22:18:42 Functions: 32 33 97.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GML Reader
       4             :  * Purpose:  Implementation of GMLReader 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 "gmlreaderp.h"
      16             : #include "gmlreader.h"
      17             : 
      18             : #include <climits>
      19             : #include <cstdlib>
      20             : #include <cstring>
      21             : #include <algorithm>
      22             : #include <set>
      23             : #include <string>
      24             : 
      25             : #include "cpl_conv.h"
      26             : #include "cpl_error.h"
      27             : #include "cpl_multiproc.h"
      28             : #include "cpl_string.h"
      29             : #include "cpl_vsi_virtual.h"
      30             : #include "gmlutils.h"
      31             : #include "ogr_geometry.h"
      32             : 
      33             : #ifdef EMBED_RESOURCE_FILES
      34             : #include "embedded_resources.h"
      35             : #endif
      36             : 
      37             : /************************************************************************/
      38             : /*                            ~IGMLReader()                             */
      39             : /************************************************************************/
      40             : 
      41         401 : IGMLReader::~IGMLReader()
      42             : {
      43         401 : }
      44             : 
      45             : /************************************************************************/
      46             : /* ==================================================================== */
      47             : /*                  No XERCES or EXPAT Library                          */
      48             : /* ==================================================================== */
      49             : /************************************************************************/
      50             : #if !defined(HAVE_XERCES) && !defined(HAVE_EXPAT)
      51             : 
      52             : /************************************************************************/
      53             : /*                          CreateGMLReader()                           */
      54             : /************************************************************************/
      55             : 
      56             : IGMLReader *CreateGMLReader(bool /*bUseExpatParserPreferably*/,
      57             :                             bool /*bInvertAxisOrderIfLatLong*/,
      58             :                             bool /*bConsiderEPSGAsURN*/,
      59             :                             GMLSwapCoordinatesEnum /* eSwapCoordinates */,
      60             :                             bool /*bGetSecondaryGeometryOption*/)
      61             : {
      62             :     CPLError(CE_Failure, CPLE_AppDefined,
      63             :              "Unable to create Xerces C++ or Expat based GML reader, Xerces "
      64             :              "or Expat support not configured into GDAL/OGR.");
      65             :     return nullptr;
      66             : }
      67             : 
      68             : /************************************************************************/
      69             : /* ==================================================================== */
      70             : /*                  With XERCES or EXPAT Library                        */
      71             : /* ==================================================================== */
      72             : /************************************************************************/
      73             : #else /* defined(HAVE_XERCES) || defined(HAVE_EXPAT) */
      74             : 
      75             : /************************************************************************/
      76             : /*                          CreateGMLReader()                           */
      77             : /************************************************************************/
      78             : 
      79         396 : IGMLReader *CreateGMLReader(bool bUseExpatParserPreferably,
      80             :                             bool bInvertAxisOrderIfLatLong,
      81             :                             bool bConsiderEPSGAsURN,
      82             :                             GMLSwapCoordinatesEnum eSwapCoordinates,
      83             :                             bool bGetSecondaryGeometryOption)
      84             : 
      85             : {
      86             :     return new GMLReader(bUseExpatParserPreferably, bInvertAxisOrderIfLatLong,
      87             :                          bConsiderEPSGAsURN, eSwapCoordinates,
      88         396 :                          bGetSecondaryGeometryOption);
      89             : }
      90             : 
      91             : #endif
      92             : 
      93             : CPLMutex *GMLReader::hMutex = nullptr;
      94             : 
      95             : /************************************************************************/
      96             : /*                             GMLReader()                              */
      97             : /************************************************************************/
      98             : 
      99         396 : GMLReader::GMLReader(
     100             : #if !defined(HAVE_EXPAT) || !defined(HAVE_XERCES)
     101             :     CPL_UNUSED
     102             : #endif
     103             :     bool bUseExpatParserPreferably,
     104             :     bool bInvertAxisOrderIfLatLong, bool bConsiderEPSGAsURN,
     105         396 :     GMLSwapCoordinatesEnum eSwapCoordinates, bool bGetSecondaryGeometryOption)
     106             :     :  // Experimental. Not publicly advertized. See commented doc in
     107             :       // drv_gml.html
     108             :       m_bFetchAllGeometries(
     109         396 :           CPLTestBool(CPLGetConfigOption("GML_FETCH_ALL_GEOMETRIES", "NO"))),
     110             :       m_bInvertAxisOrderIfLatLong(bInvertAxisOrderIfLatLong),
     111             :       m_bConsiderEPSGAsURN(bConsiderEPSGAsURN),
     112             :       m_eSwapCoordinates(eSwapCoordinates),
     113             :       m_bGetSecondaryGeometryOption(bGetSecondaryGeometryOption),
     114             :       // Must be in synced in OGR_G_CreateFromGML(), OGRGMLLayer::OGRGMLLayer(),
     115             :       // and GMLReader::GMLReader().
     116             :       m_bFaceHoleNegative(
     117         792 :           CPLTestBool(CPLGetConfigOption("GML_FACE_HOLE_NEGATIVE", "NO")))
     118             : {
     119             : #ifndef HAVE_XERCES
     120             : #else
     121             : #ifdef HAVE_EXPAT
     122         396 :     if (bUseExpatParserPreferably)
     123         394 :         bUseExpatReader = true;
     124             : #endif
     125             : #endif
     126             : 
     127             : #if defined(HAVE_EXPAT) && defined(HAVE_XERCES)
     128         396 :     if (bUseExpatReader)
     129         394 :         CPLDebug("GML", "Using Expat reader");
     130             :     else
     131           2 :         CPLDebug("GML", "Using Xerces reader");
     132             : #endif
     133         396 : }
     134             : 
     135             : /************************************************************************/
     136             : /*                             ~GMLReader()                             */
     137             : /************************************************************************/
     138             : 
     139         792 : GMLReader::~GMLReader()
     140             : 
     141             : {
     142         396 :     GMLReader::ClearClasses();
     143             : 
     144         396 :     CPLFree(m_pszFilename);
     145             : 
     146         396 :     CleanupParser();
     147             : 
     148         396 :     delete m_poRecycledState;
     149             : 
     150             : #ifdef HAVE_XERCES
     151         396 :     if (m_bXercesInitialized)
     152           2 :         OGRDeinitializeXerces();
     153             : #endif
     154             : #ifdef HAVE_EXPAT
     155         396 :     CPLFree(pabyBuf);
     156             : #endif
     157             : 
     158         396 :     if (fpGML)
     159         395 :         VSIFCloseL(fpGML);
     160         396 :     fpGML = nullptr;
     161             : 
     162         396 :     CPLFree(m_pszGlobalSRSName);
     163             : 
     164         396 :     CPLFree(m_pszFilteredClassName);
     165         792 : }
     166             : 
     167             : /************************************************************************/
     168             : /*                          SetSourceFile()                             */
     169             : /************************************************************************/
     170             : 
     171         399 : void GMLReader::SetSourceFile(const char *pszFilename)
     172             : 
     173             : {
     174         399 :     CPLFree(m_pszFilename);
     175         399 :     m_pszFilename = CPLStrdup(pszFilename);
     176         399 : }
     177             : 
     178             : /************************************************************************/
     179             : /*                       GetSourceFileName()                           */
     180             : /************************************************************************/
     181             : 
     182         395 : const char *GMLReader::GetSourceFileName()
     183             : {
     184         395 :     return m_pszFilename;
     185             : }
     186             : 
     187             : /************************************************************************/
     188             : /*                               SetFP()                                */
     189             : /************************************************************************/
     190             : 
     191         386 : void GMLReader::SetFP(VSILFILE *fp)
     192             : {
     193         386 :     fpGML = fp;
     194         386 : }
     195             : 
     196             : /************************************************************************/
     197             : /*                            SetupParser()                             */
     198             : /************************************************************************/
     199             : 
     200         519 : bool GMLReader::SetupParser()
     201             : 
     202             : {
     203         519 :     if (fpGML == nullptr)
     204          12 :         fpGML = VSIFOpenL(m_pszFilename, "rt");
     205         519 :     if (fpGML != nullptr)
     206         519 :         VSIFSeekL(fpGML, 0, SEEK_SET);
     207             : 
     208         519 :     int bRet = -1;
     209             : #ifdef HAVE_EXPAT
     210         519 :     if (bUseExpatReader)
     211         517 :         bRet = SetupParserExpat();
     212             : #endif
     213             : 
     214             : #ifdef HAVE_XERCES
     215         519 :     if (!bUseExpatReader)
     216           2 :         bRet = SetupParserXerces();
     217             : #endif
     218         519 :     if (bRet < 0)
     219             :     {
     220           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     221             :                  "SetupParser(): should not happen");
     222           0 :         return false;
     223             :     }
     224             : 
     225         519 :     if (!bRet)
     226           0 :         return false;
     227             : 
     228         519 :     m_bReadStarted = false;
     229             : 
     230             :     // Push an empty state.
     231         519 :     PushState(m_poRecycledState ? m_poRecycledState : new GMLReadState());
     232         519 :     m_poRecycledState = nullptr;
     233             : 
     234         519 :     return true;
     235             : }
     236             : 
     237             : #ifdef HAVE_XERCES
     238             : /************************************************************************/
     239             : /*                        SetupParserXerces()                           */
     240             : /************************************************************************/
     241             : 
     242           2 : bool GMLReader::SetupParserXerces()
     243             : {
     244           2 :     if (!m_bXercesInitialized)
     245             :     {
     246           2 :         if (!OGRInitializeXerces())
     247           0 :             return false;
     248           2 :         m_bXercesInitialized = true;
     249             :     }
     250             : 
     251             :     // Cleanup any old parser.
     252           2 :     if (m_poSAXReader != nullptr)
     253           0 :         CleanupParser();
     254             : 
     255             :     // Create and initialize parser.
     256           2 :     XMLCh *xmlUriValid = nullptr;
     257           2 :     XMLCh *xmlUriNS = nullptr;
     258             : 
     259             :     try
     260             :     {
     261           2 :         m_poSAXReader = XMLReaderFactory::createXMLReader();
     262             : 
     263           2 :         GMLXercesHandler *poXercesHandler = new GMLXercesHandler(this);
     264           2 :         m_poGMLHandler = poXercesHandler;
     265             : 
     266           2 :         m_poSAXReader->setContentHandler(poXercesHandler);
     267           2 :         m_poSAXReader->setErrorHandler(poXercesHandler);
     268           2 :         m_poSAXReader->setLexicalHandler(poXercesHandler);
     269           2 :         m_poSAXReader->setEntityResolver(poXercesHandler);
     270           2 :         m_poSAXReader->setDTDHandler(poXercesHandler);
     271           2 :         m_poSAXReader->setFeature(
     272           2 :             XMLUni::fgXercesDisableDefaultEntityResolution, true);
     273             : 
     274           2 :         xmlUriValid =
     275           2 :             XMLString::transcode("http://xml.org/sax/features/validation");
     276           2 :         xmlUriNS =
     277           2 :             XMLString::transcode("http://xml.org/sax/features/namespaces");
     278             : 
     279             : #if (OGR_GML_VALIDATION)
     280             :         m_poSAXReader->setFeature(xmlUriValid, true);
     281             :         m_poSAXReader->setFeature(xmlUriNS, true);
     282             : 
     283             :         m_poSAXReader->setFeature(XMLUni::fgSAX2CoreNameSpaces, true);
     284             :         m_poSAXReader->setFeature(XMLUni::fgXercesSchema, true);
     285             : 
     286             :         // m_poSAXReader->setDoSchema(true);
     287             :         // m_poSAXReader->setValidationSchemaFullChecking(true);
     288             : #else
     289           2 :         m_poSAXReader->setFeature(XMLUni::fgSAX2CoreValidation, false);
     290             : 
     291           2 :         m_poSAXReader->setFeature(XMLUni::fgXercesSchema, false);
     292             : 
     293             : #endif
     294           2 :         XMLString::release(&xmlUriValid);
     295           2 :         XMLString::release(&xmlUriNS);
     296             :     }
     297           0 :     catch (...)
     298             :     {
     299           0 :         XMLString::release(&xmlUriValid);
     300           0 :         XMLString::release(&xmlUriNS);
     301             : 
     302           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     303             :                  "Exception initializing Xerces based GML reader.\n");
     304           0 :         return false;
     305             :     }
     306             : 
     307           2 :     if (m_GMLInputSource == nullptr && fpGML != nullptr)
     308           2 :         m_GMLInputSource = OGRCreateXercesInputSource(fpGML);
     309             : 
     310           2 :     return true;
     311             : }
     312             : #endif
     313             : 
     314             : /************************************************************************/
     315             : /*                        SetupParserExpat()                            */
     316             : /************************************************************************/
     317             : 
     318             : #ifdef HAVE_EXPAT
     319         517 : bool GMLReader::SetupParserExpat()
     320             : {
     321             :     // Cleanup any old parser.
     322         517 :     if (oParser != nullptr)
     323           3 :         CleanupParser();
     324             : 
     325         517 :     oParser = OGRCreateExpatXMLParser();
     326         517 :     m_poGMLHandler = new GMLExpatHandler(this, oParser);
     327             : 
     328         517 :     XML_SetElementHandler(oParser, GMLExpatHandler::startElementCbk,
     329             :                           GMLExpatHandler::endElementCbk);
     330         517 :     XML_SetCharacterDataHandler(oParser, GMLExpatHandler::dataHandlerCbk);
     331         517 :     XML_SetUserData(oParser, m_poGMLHandler);
     332             : 
     333         517 :     if (pabyBuf == nullptr)
     334         308 :         pabyBuf = static_cast<char *>(VSI_MALLOC_VERBOSE(PARSER_BUF_SIZE));
     335         517 :     if (pabyBuf == nullptr)
     336           0 :         return false;
     337             : 
     338         517 :     return true;
     339             : }
     340             : #endif
     341             : 
     342             : /************************************************************************/
     343             : /*                           CleanupParser()                            */
     344             : /************************************************************************/
     345             : 
     346        1213 : void GMLReader::CleanupParser()
     347             : 
     348             : {
     349             : #ifdef HAVE_XERCES
     350        1213 :     if (!bUseExpatReader && m_poSAXReader == nullptr)
     351           3 :         return;
     352             : #endif
     353             : 
     354             : #ifdef HAVE_EXPAT
     355        1210 :     if (bUseExpatReader && oParser == nullptr)
     356         691 :         return;
     357             : #endif
     358             : 
     359        1042 :     while (m_poState)
     360         523 :         PopState();
     361             : 
     362             : #ifdef HAVE_XERCES
     363         519 :     delete m_poSAXReader;
     364         519 :     m_poSAXReader = nullptr;
     365         519 :     OGRDestroyXercesInputSource(m_GMLInputSource);
     366         519 :     m_GMLInputSource = nullptr;
     367         519 :     delete m_poCompleteFeature;
     368         519 :     m_poCompleteFeature = nullptr;
     369         519 :     m_bEOF = false;
     370             : #endif
     371             : 
     372             : #ifdef HAVE_EXPAT
     373         519 :     if (oParser)
     374         517 :         XML_ParserFree(oParser);
     375         519 :     oParser = nullptr;
     376             : 
     377        1049 :     for (int i = nFeatureTabIndex; i < nFeatureTabLength; i++)
     378         530 :         delete ppoFeatureTab[i];
     379         519 :     CPLFree(ppoFeatureTab);
     380         519 :     nFeatureTabIndex = 0;
     381         519 :     nFeatureTabLength = 0;
     382         519 :     nFeatureTabAlloc = 0;
     383         519 :     ppoFeatureTab = nullptr;
     384         519 :     m_osErrorMessage.clear();
     385             : 
     386             : #endif
     387             : 
     388         519 :     delete m_poGMLHandler;
     389         519 :     m_poGMLHandler = nullptr;
     390             : 
     391         519 :     m_bReadStarted = false;
     392             : }
     393             : 
     394             : /************************************************************************/
     395             : /*                        NextFeatureXerces()                           */
     396             : /************************************************************************/
     397             : 
     398             : #ifdef HAVE_XERCES
     399           2 : GMLFeature *GMLReader::NextFeatureXerces()
     400             : 
     401             : {
     402           2 :     GMLFeature *poReturn = nullptr;
     403             : 
     404           2 :     if (m_bEOF)
     405           0 :         return nullptr;
     406             : 
     407             :     try
     408             :     {
     409           2 :         if (!m_bReadStarted)
     410             :         {
     411           2 :             if (m_poSAXReader == nullptr)
     412           2 :                 SetupParser();
     413             : 
     414           2 :             m_bReadStarted = true;
     415             : 
     416           2 :             if (m_poSAXReader == nullptr || m_GMLInputSource == nullptr)
     417           0 :                 return nullptr;
     418             : 
     419           2 :             if (!m_poSAXReader->parseFirst(*m_GMLInputSource, m_oToFill))
     420           0 :                 return nullptr;
     421             :         }
     422             : 
     423         942 :         while (m_poCompleteFeature == nullptr && !m_bStopParsing &&
     424         940 :                m_poSAXReader->parseNext(m_oToFill))
     425             :         {
     426             :         }
     427             : 
     428           1 :         if (m_poCompleteFeature == nullptr)
     429           0 :             m_bEOF = true;
     430             : 
     431           1 :         poReturn = m_poCompleteFeature;
     432           1 :         m_poCompleteFeature = nullptr;
     433             :     }
     434           0 :     catch (const XMLException &toCatch)
     435             :     {
     436           0 :         CPLString osErrMsg;
     437           0 :         transcode(toCatch.getMessage(), osErrMsg);
     438           0 :         CPLDebug("GML", "Error during NextFeature()! Message:\n%s",
     439             :                  osErrMsg.c_str());
     440           0 :         m_bStopParsing = true;
     441             :     }
     442           2 :     catch (const SAXException &toCatch)
     443             :     {
     444           1 :         CPLString osErrMsg;
     445           1 :         transcode(toCatch.getMessage(), osErrMsg);
     446           1 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrMsg.c_str());
     447           1 :         m_bStopParsing = true;
     448             :     }
     449             : 
     450           2 :     return poReturn;
     451             : }
     452             : #endif
     453             : 
     454             : #ifdef HAVE_EXPAT
     455        2183 : GMLFeature *GMLReader::NextFeatureExpat()
     456             : 
     457             : {
     458        2183 :     if (!m_bReadStarted)
     459             :     {
     460         517 :         if (oParser == nullptr)
     461         408 :             SetupParser();
     462             : 
     463         517 :         m_bReadStarted = true;
     464             :     }
     465             : 
     466        2183 :     if (nFeatureTabIndex < nFeatureTabLength)
     467             :     {
     468        1473 :         return ppoFeatureTab[nFeatureTabIndex++];
     469             :     }
     470             : 
     471         710 :     if (!m_osErrorMessage.empty())
     472             :     {
     473           2 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", m_osErrorMessage.c_str());
     474           2 :         m_osErrorMessage.clear();
     475           2 :         return nullptr;
     476             :     }
     477             : 
     478        1225 :     if (fpGML == nullptr || m_bStopParsing || VSIFEofL(fpGML) ||
     479         517 :         VSIFErrorL(fpGML))
     480         191 :         return nullptr;
     481             : 
     482         517 :     nFeatureTabLength = 0;
     483         517 :     nFeatureTabIndex = 0;
     484             : 
     485         517 :     int nDone = 0;
     486           0 :     do
     487             :     {
     488             :         // Reset counter that is used to detect billion laugh attacks.
     489         517 :         static_cast<GMLExpatHandler *>(m_poGMLHandler)
     490         517 :             ->ResetDataHandlerCounter();
     491             : 
     492             :         unsigned int nLen = static_cast<unsigned int>(
     493         517 :             VSIFReadL(pabyBuf, 1, PARSER_BUF_SIZE, fpGML));
     494         517 :         nDone = nLen < PARSER_BUF_SIZE;
     495             : 
     496             :         // Some files, such as APT_AIXM.xml from
     497             :         // https://nfdc.faa.gov/webContent/56DaySub/2015-03-05/aixm5.1.zip
     498             :         // end with trailing nul characters. This test is not fully bullet-proof
     499             :         // in case the nul characters would occur at a buffer boundary.
     500         517 :         while (nDone && nLen > 0 && pabyBuf[nLen - 1] == '\0')
     501           0 :             nLen--;
     502             : 
     503         517 :         if (XML_Parse(oParser, pabyBuf, nLen, nDone) == XML_STATUS_ERROR)
     504             :         {
     505             :             // Defer emission of the error message until we have to return
     506             :             // nullptr
     507             :             m_osErrorMessage.Printf(
     508             :                 "XML parsing of GML file failed : %s "
     509             :                 "at line %d, column %d",
     510             :                 XML_ErrorString(XML_GetErrorCode(oParser)),
     511          10 :                 static_cast<int>(XML_GetCurrentLineNumber(oParser)),
     512           5 :                 static_cast<int>(XML_GetCurrentColumnNumber(oParser)));
     513           5 :             m_bStopParsing = true;
     514             :         }
     515         517 :         if (!m_bStopParsing)
     516         512 :             m_bStopParsing = static_cast<GMLExpatHandler *>(m_poGMLHandler)
     517         512 :                                  ->HasStoppedParsing();
     518         517 :     } while (!nDone && !m_bStopParsing && nFeatureTabLength == 0);
     519             : 
     520         517 :     if (nFeatureTabLength)
     521         473 :         return ppoFeatureTab[nFeatureTabIndex++];
     522             : 
     523          44 :     if (!m_osErrorMessage.empty())
     524             :     {
     525           3 :         CPLError(CE_Failure, CPLE_AppDefined, "%s", m_osErrorMessage.c_str());
     526           3 :         m_osErrorMessage.clear();
     527             :     }
     528             : 
     529          44 :     return nullptr;
     530             : }
     531             : #endif
     532             : 
     533        2185 : GMLFeature *GMLReader::NextFeature()
     534             : {
     535             : #ifdef HAVE_EXPAT
     536        2185 :     if (bUseExpatReader)
     537        2183 :         return NextFeatureExpat();
     538             : #ifdef HAVE_XERCES
     539           2 :     return NextFeatureXerces();
     540             : #else
     541             :     CPLError(CE_Failure, CPLE_AppDefined, "NextFeature(): Should not happen");
     542             :     return nullptr;
     543             : #endif
     544             : #else
     545             : #ifdef HAVE_XERCES
     546             :     if (!bUseExpatReader)
     547             :         return NextFeatureXerces();
     548             : #endif
     549             :     CPLError(CE_Failure, CPLE_AppDefined, "NextFeature(): Should not happen");
     550             :     return nullptr;
     551             : #endif
     552             : }
     553             : 
     554             : /************************************************************************/
     555             : /*                            PushFeature()                             */
     556             : /*                                                                      */
     557             : /*      Create a feature based on the named element.  If the            */
     558             : /*      corresponding feature class doesn't exist yet, then create      */
     559             : /*      it now.  A new GMLReadState will be created for the feature,    */
     560             : /*      and it will be placed within that state.  The state is          */
     561             : /*      pushed onto the readstate stack.                                */
     562             : /************************************************************************/
     563             : 
     564        2477 : void GMLReader::PushFeature(const char *pszElement, const char *pszFID,
     565             :                             int nClassIndex)
     566             : 
     567             : {
     568        2477 :     int iClass = 0;
     569             : 
     570        2477 :     if (nClassIndex != INT_MAX)
     571             :     {
     572        1131 :         iClass = nClassIndex;
     573             :     }
     574             :     else
     575             :     {
     576             :         /* --------------------------------------------------------------------
     577             :          */
     578             :         /*      Find the class of this element. */
     579             :         /* --------------------------------------------------------------------
     580             :          */
     581        3575 :         for (; iClass < m_nClassCount; iClass++)
     582             :         {
     583        3458 :             if (EQUAL(pszElement, m_papoClass[iClass]->GetElementName()))
     584        1229 :                 break;
     585             :         }
     586             : 
     587             :         /* --------------------------------------------------------------------
     588             :          */
     589             :         /*      Create a new feature class for this element, if there is no */
     590             :         /*      existing class for it. */
     591             :         /* --------------------------------------------------------------------
     592             :          */
     593        1346 :         if (iClass == m_nClassCount)
     594             :         {
     595         117 :             CPLAssert(!m_bClassListLocked);
     596             : 
     597         117 :             GMLFeatureClass *poNewClass = new GMLFeatureClass(pszElement);
     598             : 
     599         117 :             AddClass(poNewClass);
     600             :         }
     601             :     }
     602             : 
     603             :     /* -------------------------------------------------------------------- */
     604             :     /*      Create a feature of this feature class.  Try to set the fid     */
     605             :     /*      if available.                                                   */
     606             :     /* -------------------------------------------------------------------- */
     607        2477 :     GMLFeature *poFeature = new GMLFeature(m_papoClass[iClass]);
     608        2477 :     if (pszFID != nullptr)
     609             :     {
     610        1453 :         poFeature->SetFID(pszFID);
     611             :     }
     612             : 
     613             :     /* -------------------------------------------------------------------- */
     614             :     /*      Create and push a new read state.                               */
     615             :     /* -------------------------------------------------------------------- */
     616             :     GMLReadState *poState =
     617        2477 :         m_poRecycledState ? m_poRecycledState : new GMLReadState();
     618        2477 :     m_poRecycledState = nullptr;
     619        2477 :     poState->m_poFeature = poFeature;
     620        2477 :     PushState(poState);
     621        2477 : }
     622             : 
     623             : /************************************************************************/
     624             : /*                          IsFeatureElement()                          */
     625             : /*                                                                      */
     626             : /*      Based on context and the element name, is this element a new    */
     627             : /*      GML feature element?                                            */
     628             : /************************************************************************/
     629             : 
     630        5178 : int GMLReader::GetFeatureElementIndex(const char *pszElement,
     631             :                                       int nElementLength,
     632             :                                       GMLAppSchemaType eAppSchemaType)
     633             : 
     634             : {
     635        5178 :     const char *pszLast = m_poState->GetLastComponent();
     636        5178 :     const size_t nLenLast = m_poState->GetLastComponentLen();
     637             : 
     638        5178 :     if (eAppSchemaType == APPSCHEMA_MTKGML)
     639             :     {
     640          44 :         if (m_poState->m_nPathLength != 1)
     641          22 :             return -1;
     642             :     }
     643        5134 :     else if ((nLenLast >= 6 && EQUAL(pszLast + nLenLast - 6, "member")) ||
     644        1447 :              (nLenLast >= 7 && EQUAL(pszLast + nLenLast - 7, "members")))
     645             :     {
     646             :         // Default feature name.
     647             :     }
     648             :     else
     649             :     {
     650        2521 :         if (nLenLast == 4 && strcmp(pszLast, "dane") == 0)
     651             :         {
     652             :             // Polish TBD GML.
     653             :         }
     654             : 
     655             :         // Begin of OpenLS.
     656        2521 :         else if (nLenLast == 19 && nElementLength == 15 &&
     657           1 :                  strcmp(pszLast, "GeocodeResponseList") == 0 &&
     658           0 :                  strcmp(pszElement, "GeocodedAddress") == 0)
     659             :         {
     660             :         }
     661        2521 :         else if (nLenLast == 22 &&
     662           3 :                  strcmp(pszLast, "DetermineRouteResponse") == 0)
     663             :         {
     664             :             // We don't want the children of RouteInstructionsList
     665             :             // to be a single feature. We want each RouteInstruction
     666             :             // to be a feature.
     667           3 :             if (strcmp(pszElement, "RouteInstructionsList") == 0)
     668           1 :                 return -1;
     669             :         }
     670        2518 :         else if (nElementLength == 16 && nLenLast == 21 &&
     671           9 :                  strcmp(pszElement, "RouteInstruction") == 0 &&
     672           9 :                  strcmp(pszLast, "RouteInstructionsList") == 0)
     673             :         {
     674             :         }
     675             :         // End of OpenLS.
     676             : 
     677        2509 :         else if (nLenLast > 6 &&
     678         513 :                  strcmp(pszLast + nLenLast - 6, "_layer") == 0 &&
     679           0 :                  nElementLength > 8 &&
     680           0 :                  strcmp(pszElement + nElementLength - 8, "_feature") == 0)
     681             :         {
     682             :             // GML answer of MapServer WMS GetFeatureInfo request.
     683             :         }
     684             : 
     685             :         // Begin of CSW SearchResults.
     686        2509 :         else if (nElementLength == static_cast<int>(strlen("BriefRecord")) &&
     687           4 :                  nLenLast == strlen("SearchResults") &&
     688           4 :                  strcmp(pszElement, "BriefRecord") == 0 &&
     689           4 :                  strcmp(pszLast, "SearchResults") == 0)
     690             :         {
     691             :         }
     692        2505 :         else if (nElementLength == static_cast<int>(strlen("SummaryRecord")) &&
     693           4 :                  nLenLast == strlen("SearchResults") &&
     694           4 :                  strcmp(pszElement, "SummaryRecord") == 0 &&
     695           4 :                  strcmp(pszLast, "SearchResults") == 0)
     696             :         {
     697             :         }
     698        2501 :         else if (nElementLength == static_cast<int>(strlen("Record")) &&
     699          45 :                  nLenLast == strlen("SearchResults") &&
     700          45 :                  strcmp(pszElement, "Record") == 0 &&
     701          44 :                  strcmp(pszLast, "SearchResults") == 0)
     702             :         {
     703             :         }
     704             :         /* End of CSW SearchResults */
     705             : 
     706             :         else
     707             :         {
     708        2457 :             if (m_bClassListLocked)
     709             :             {
     710       12241 :                 for (int i = 0; i < m_nClassCount; i++)
     711             :                 {
     712       10770 :                     if (m_poState->osPath.size() + 1 + nElementLength ==
     713       10770 :                             m_papoClass[i]->GetElementNameLen() &&
     714         158 :                         m_papoClass[i]
     715         158 :                                 ->GetElementName()[m_poState->osPath.size()] ==
     716         103 :                             '|' &&
     717         103 :                         memcmp(m_poState->osPath.c_str(),
     718         103 :                                m_papoClass[i]->GetElementName(),
     719       11031 :                                m_poState->osPath.size()) == 0 &&
     720         206 :                         memcmp(pszElement,
     721         206 :                                m_papoClass[i]->GetElementName() + 1 +
     722         103 :                                    m_poState->osPath.size(),
     723             :                                nElementLength) == 0)
     724             :                     {
     725          81 :                         return i;
     726             :                     }
     727             :                 }
     728             :                 // Give a chance to find a feature class by element name
     729             :                 // This is for example needed for
     730             :                 // autotest/ogr/data/gml_jpfgd/BldA.xml that has a
     731             :                 // feature at a low nesting level.
     732             :             }
     733             :             else
     734             :             {
     735         905 :                 return -1;
     736             :             }
     737             :         }
     738             :     }
     739             : 
     740             :     // If the class list isn't locked, any element that is a featureMember
     741             :     // will do.
     742        4169 :     if (!m_bClassListLocked)
     743        1346 :         return INT_MAX;
     744             : 
     745             :     // otherwise, find a class with the desired element name.
     746       16912 :     for (int i = 0; i < m_nClassCount; i++)
     747             :     {
     748       30870 :         if (nElementLength ==
     749       17683 :                 static_cast<int>(m_papoClass[i]->GetElementNameLen()) &&
     750        2248 :             memcmp(pszElement, m_papoClass[i]->GetElementName(),
     751             :                    nElementLength) == 0)
     752        1346 :             return i;
     753             :     }
     754             : 
     755        1477 :     return -1;
     756             : }
     757             : 
     758             : /************************************************************************/
     759             : /*                IsCityGMLGenericAttributeElement()                    */
     760             : /************************************************************************/
     761             : 
     762         132 : bool GMLReader::IsCityGMLGenericAttributeElement(const char *pszElement,
     763             :                                                  void *attr)
     764             : 
     765             : {
     766         132 :     if (strcmp(pszElement, "stringAttribute") != 0 &&
     767          94 :         strcmp(pszElement, "intAttribute") != 0 &&
     768          92 :         strcmp(pszElement, "doubleAttribute") != 0)
     769          90 :         return false;
     770             : 
     771          42 :     char *pszVal = m_poGMLHandler->GetAttributeValue(attr, "name");
     772          42 :     if (pszVal == nullptr)
     773           0 :         return false;
     774             : 
     775          42 :     GMLFeatureClass *poClass = m_poState->m_poFeature->GetClass();
     776             : 
     777             :     // If the schema is not yet locked, then any simple element
     778             :     // is potentially an attribute.
     779          42 :     if (!poClass->IsSchemaLocked())
     780             :     {
     781          42 :         CPLFree(pszVal);
     782          42 :         return true;
     783             :     }
     784             : 
     785           0 :     for (int i = 0; i < poClass->GetPropertyCount(); i++)
     786             :     {
     787           0 :         if (strcmp(poClass->GetProperty(i)->GetSrcElement(), pszVal) == 0)
     788             :         {
     789           0 :             CPLFree(pszVal);
     790           0 :             return true;
     791             :         }
     792             :     }
     793             : 
     794           0 :     CPLFree(pszVal);
     795           0 :     return false;
     796             : }
     797             : 
     798             : /************************************************************************/
     799             : /*                       GetAttributeElementIndex()                     */
     800             : /************************************************************************/
     801             : 
     802       11075 : int GMLReader::GetAttributeElementIndex(const char *pszElement, int nLen,
     803             :                                         const char *pszAttrKey)
     804             : 
     805             : {
     806       11075 :     GMLFeatureClass *poClass = m_poState->m_poFeature->GetClass();
     807             : 
     808             :     // If the schema is not yet locked, then any simple element
     809             :     // is potentially an attribute.
     810       11075 :     if (!poClass->IsSchemaLocked())
     811        5883 :         return INT_MAX;
     812             : 
     813             :     // Otherwise build the path to this element into a single string
     814             :     // and compare against known attributes.
     815        5192 :     if (m_poState->m_nPathLength == 0)
     816             :     {
     817        3334 :         if (pszAttrKey == nullptr)
     818        3261 :             return poClass->GetPropertyIndexBySrcElement(pszElement, nLen);
     819             :         else
     820             :         {
     821          73 :             int nFullLen = nLen + 1 + static_cast<int>(strlen(pszAttrKey));
     822          73 :             osElemPath.reserve(nFullLen);
     823          73 :             osElemPath.assign(pszElement, nLen);
     824          73 :             osElemPath.append(1, '@');
     825          73 :             osElemPath.append(pszAttrKey);
     826          73 :             return poClass->GetPropertyIndexBySrcElement(osElemPath.c_str(),
     827          73 :                                                          nFullLen);
     828             :         }
     829             :     }
     830             :     else
     831             :     {
     832        1858 :         int nFullLen = nLen + static_cast<int>(m_poState->osPath.size()) + 1;
     833        1858 :         if (pszAttrKey != nullptr)
     834          89 :             nFullLen += 1 + static_cast<int>(strlen(pszAttrKey));
     835        1858 :         osElemPath.reserve(nFullLen);
     836        1858 :         osElemPath.assign(m_poState->osPath);
     837        1858 :         osElemPath.append(1, '|');
     838        1858 :         osElemPath.append(pszElement, nLen);
     839        1858 :         if (pszAttrKey != nullptr)
     840             :         {
     841          89 :             osElemPath.append(1, '@');
     842          89 :             osElemPath.append(pszAttrKey);
     843             :         }
     844        1858 :         return poClass->GetPropertyIndexBySrcElement(osElemPath.c_str(),
     845        1858 :                                                      nFullLen);
     846             :     }
     847             : }
     848             : 
     849             : /************************************************************************/
     850             : /*                              PopState()                              */
     851             : /************************************************************************/
     852             : 
     853        2996 : void GMLReader::PopState()
     854             : 
     855             : {
     856        2996 :     if (m_poState != nullptr)
     857             :     {
     858             : #ifdef HAVE_XERCES
     859        2996 :         if (!bUseExpatReader && m_poState->m_poFeature != nullptr &&
     860           1 :             m_poCompleteFeature == nullptr)
     861             :         {
     862           1 :             m_poCompleteFeature = m_poState->m_poFeature;
     863           1 :             m_poState->m_poFeature = nullptr;
     864             :         }
     865        2995 :         else if (!bUseExpatReader && m_poState->m_poFeature != nullptr)
     866             :         {
     867           0 :             delete m_poState->m_poFeature;
     868           0 :             m_poState->m_poFeature = nullptr;
     869             :         }
     870             : #endif
     871             : 
     872             : #ifdef HAVE_EXPAT
     873        2996 :         if (bUseExpatReader && m_poState->m_poFeature != nullptr)
     874             :         {
     875        2476 :             if (nFeatureTabLength >= nFeatureTabAlloc)
     876             :             {
     877         528 :                 nFeatureTabAlloc = nFeatureTabLength * 4 / 3 + 16;
     878         528 :                 ppoFeatureTab = static_cast<GMLFeature **>(CPLRealloc(
     879         528 :                     ppoFeatureTab, sizeof(GMLFeature *) * (nFeatureTabAlloc)));
     880             :             }
     881        2476 :             ppoFeatureTab[nFeatureTabLength] = m_poState->m_poFeature;
     882        2476 :             nFeatureTabLength++;
     883             : 
     884        2476 :             m_poState->m_poFeature = nullptr;
     885             :         }
     886             : #endif
     887             : 
     888        2996 :         GMLReadState *poParent = m_poState->m_poParentState;
     889             : 
     890        2996 :         delete m_poRecycledState;
     891        2996 :         m_poRecycledState = m_poState;
     892        2996 :         m_poRecycledState->Reset();
     893        2996 :         m_poState = poParent;
     894             :     }
     895        2996 : }
     896             : 
     897             : /************************************************************************/
     898             : /*                             PushState()                              */
     899             : /************************************************************************/
     900             : 
     901        2996 : void GMLReader::PushState(GMLReadState *poState)
     902             : 
     903             : {
     904        2996 :     poState->m_poParentState = m_poState;
     905        2996 :     m_poState = poState;
     906        2996 : }
     907             : 
     908             : /************************************************************************/
     909             : /*                              GetClass()                              */
     910             : /************************************************************************/
     911             : 
     912         842 : GMLFeatureClass *GMLReader::GetClass(int iClass) const
     913             : 
     914             : {
     915         842 :     if (iClass < 0 || iClass >= m_nClassCount)
     916           0 :         return nullptr;
     917             : 
     918         842 :     return m_papoClass[iClass];
     919             : }
     920             : 
     921             : /************************************************************************/
     922             : /*                              GetClass()                              */
     923             : /************************************************************************/
     924             : 
     925        1535 : GMLFeatureClass *GMLReader::GetClass(const char *pszName) const
     926             : 
     927             : {
     928        2666 :     for (int iClass = 0; iClass < m_nClassCount; iClass++)
     929             :     {
     930        1666 :         if (EQUAL(m_papoClass[iClass]->GetName(), pszName))
     931         535 :             return m_papoClass[iClass];
     932             :     }
     933             : 
     934        1000 :     return nullptr;
     935             : }
     936             : 
     937             : /************************************************************************/
     938             : /*                              AddClass()                              */
     939             : /************************************************************************/
     940             : 
     941         634 : int GMLReader::AddClass(GMLFeatureClass *poNewClass)
     942             : 
     943             : {
     944         634 :     CPLAssert(GetClass(poNewClass->GetName()) == nullptr);
     945             : 
     946         634 :     m_nClassCount++;
     947         634 :     m_papoClass = static_cast<GMLFeatureClass **>(
     948         634 :         CPLRealloc(m_papoClass, sizeof(void *) * m_nClassCount));
     949         634 :     m_papoClass[m_nClassCount - 1] = poNewClass;
     950             : 
     951         634 :     if (poNewClass->HasFeatureProperties())
     952           8 :         m_bLookForClassAtAnyLevel = true;
     953             : 
     954         634 :     return m_nClassCount - 1;
     955             : }
     956             : 
     957             : /************************************************************************/
     958             : /*                            ClearClasses()                            */
     959             : /************************************************************************/
     960             : 
     961         537 : void GMLReader::ClearClasses()
     962             : 
     963             : {
     964        1166 :     for (int i = 0; i < m_nClassCount; i++)
     965         629 :         delete m_papoClass[i];
     966         537 :     CPLFree(m_papoClass);
     967             : 
     968         537 :     m_nClassCount = 0;
     969         537 :     m_papoClass = nullptr;
     970         537 :     m_bLookForClassAtAnyLevel = false;
     971         537 : }
     972             : 
     973             : /************************************************************************/
     974             : /*                     SetFeaturePropertyDirectly()                     */
     975             : /*                                                                      */
     976             : /*      Set the property value on the current feature, adding the       */
     977             : /*      property name to the GMLFeatureClass if required.               */
     978             : /*      The pszValue ownership is passed to this function.              */
     979             : /************************************************************************/
     980             : 
     981        6491 : void GMLReader::SetFeaturePropertyDirectly(const char *pszElement,
     982             :                                            char *pszValue, int iPropertyIn,
     983             :                                            GMLPropertyType eType)
     984             : 
     985             : {
     986        6491 :     GMLFeature *poFeature = GetState()->m_poFeature;
     987             : 
     988        6491 :     CPLAssert(poFeature != nullptr);
     989             : 
     990             :     /* -------------------------------------------------------------------- */
     991             :     /*      Does this property exist in the feature class?  If not, add     */
     992             :     /*      it.                                                             */
     993             :     /* -------------------------------------------------------------------- */
     994        6491 :     GMLFeatureClass *poClass = poFeature->GetClass();
     995        6491 :     int iProperty = 0;
     996             : 
     997        6491 :     const int nPropertyCount = poClass->GetPropertyCount();
     998        6491 :     if (iPropertyIn >= 0 && iPropertyIn < nPropertyCount)
     999             :     {
    1000        2718 :         iProperty = iPropertyIn;
    1001             :     }
    1002             :     else
    1003             :     {
    1004        9706 :         for (; iProperty < nPropertyCount; iProperty++)
    1005             :         {
    1006        9359 :             if (strcmp(poClass->GetProperty(iProperty)->GetSrcElement(),
    1007        9359 :                        pszElement) == 0)
    1008        3426 :                 break;
    1009             :         }
    1010             : 
    1011        3773 :         if (iProperty == nPropertyCount)
    1012             :         {
    1013         347 :             if (poClass->IsSchemaLocked())
    1014             :             {
    1015           1 :                 CPLDebug("GML",
    1016             :                          "Encountered property missing from class schema : %s.",
    1017             :                          pszElement);
    1018           1 :                 CPLFree(pszValue);
    1019           1 :                 return;
    1020             :             }
    1021             : 
    1022         346 :             CPLString osFieldName;
    1023             : 
    1024         346 :             if (IsWFSJointLayer())
    1025             :             {
    1026             :                 // At that point the element path should be
    1027             :                 // member|layer|property.
    1028             : 
    1029             :                 // Strip member| prefix. Should always be true normally.
    1030          12 :                 if (STARTS_WITH(pszElement, "member|"))
    1031          12 :                     osFieldName = pszElement + strlen("member|");
    1032             : 
    1033             :                 // Replace layer|property by layer_property.
    1034          12 :                 size_t iPos = osFieldName.find('|');
    1035          12 :                 if (iPos != std::string::npos)
    1036           8 :                     osFieldName[iPos] = '.';
    1037             : 
    1038             :                 // Special case for gml:id on layer.
    1039          12 :                 iPos = osFieldName.find("@id");
    1040          12 :                 if (iPos != std::string::npos)
    1041             :                 {
    1042           4 :                     osFieldName.resize(iPos);
    1043           4 :                     osFieldName += ".gml_id";
    1044             :                 }
    1045             :             }
    1046         334 :             else if (strchr(pszElement, '|') == nullptr)
    1047             :             {
    1048         272 :                 osFieldName = pszElement;
    1049             :             }
    1050             :             else
    1051             :             {
    1052          62 :                 osFieldName = strrchr(pszElement, '|') + 1;
    1053          62 :                 if (poClass->GetPropertyIndex(osFieldName) != -1)
    1054           4 :                     osFieldName = pszElement;
    1055             :             }
    1056             : 
    1057         346 :             size_t nPos = osFieldName.find("@");
    1058         346 :             if (nPos != std::string::npos)
    1059           3 :                 osFieldName[nPos] = '_';
    1060             : 
    1061             :             // Does this conflict with an existing property name?
    1062         347 :             for (int i = 0; poClass->GetProperty(osFieldName) != nullptr; i++)
    1063             :             {
    1064           1 :                 osFieldName += "_";
    1065           1 :                 if (i == 10)
    1066             :                 {
    1067           0 :                     CPLDebug("GML", "Too many conflicting property names : %s.",
    1068             :                              osFieldName.c_str());
    1069           0 :                     CPLFree(pszValue);
    1070           0 :                     return;
    1071             :                 }
    1072             :             }
    1073             : 
    1074             :             GMLPropertyDefn *poPDefn =
    1075         346 :                 new GMLPropertyDefn(osFieldName, pszElement);
    1076             : 
    1077         346 :             if (EQUAL(CPLGetConfigOption("GML_FIELDTYPES", ""),
    1078             :                       "ALWAYS_STRING"))
    1079           0 :                 poPDefn->SetType(GMLPT_String);
    1080         346 :             else if (eType != GMLPT_Untyped)
    1081           3 :                 poPDefn->SetType(eType);
    1082             : 
    1083         346 :             if (poClass->AddProperty(poPDefn) < 0)
    1084             :             {
    1085           0 :                 delete poPDefn;
    1086           0 :                 CPLFree(pszValue);
    1087           0 :                 return;
    1088             :             }
    1089             :         }
    1090             :     }
    1091             : 
    1092             :     /* -------------------------------------------------------------------- */
    1093             :     /*      Set the property                                                */
    1094             :     /* -------------------------------------------------------------------- */
    1095        6490 :     poFeature->SetPropertyDirectly(iProperty, pszValue);
    1096             : 
    1097             :     /* -------------------------------------------------------------------- */
    1098             :     /*      Do we need to update the property type?                         */
    1099             :     /* -------------------------------------------------------------------- */
    1100        6490 :     if (!poClass->IsSchemaLocked() && !EQUAL(pszValue, OGR_GML_NULL))
    1101             :     {
    1102        3649 :         auto poClassProperty = poClass->GetProperty(iProperty);
    1103        3649 :         if (poClassProperty)
    1104             :         {
    1105        3649 :             poClassProperty->AnalysePropertyValue(
    1106        3649 :                 poFeature->GetProperty(iProperty), m_bSetWidthFlag);
    1107             :         }
    1108             :         else
    1109             :         {
    1110           0 :             CPLAssert(false);
    1111             :         }
    1112             :     }
    1113             : }
    1114             : 
    1115             : /************************************************************************/
    1116             : /*                            LoadClasses()                             */
    1117             : /************************************************************************/
    1118             : 
    1119          54 : bool GMLReader::LoadClasses(const char *pszFile)
    1120             : 
    1121             : {
    1122             :     // Add logic later to determine reasonable default schema file.
    1123          54 :     if (pszFile == nullptr)
    1124           0 :         return false;
    1125             : 
    1126             :     /* -------------------------------------------------------------------- */
    1127             :     /*      Load the raw XML file.                                          */
    1128             :     /* -------------------------------------------------------------------- */
    1129          54 :     GByte *pabyRet = nullptr;
    1130          54 :     const char *pszWholeText = nullptr;
    1131             :     {
    1132             : #ifdef EMBED_RESOURCE_FILES
    1133             :         CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
    1134             : #endif
    1135          54 :         if (VSIIngestFile(nullptr, pszFile, &pabyRet, nullptr,
    1136          54 :                           100 * 1024 * 1024))
    1137             :         {
    1138          54 :             pszWholeText = reinterpret_cast<const char *>(pabyRet);
    1139             :         }
    1140             :         else
    1141             :         {
    1142             : #ifdef EMBED_RESOURCE_FILES
    1143             :             pszWholeText = GMLGetFileContent(pszFile);
    1144             :             if (pszWholeText)
    1145             :             {
    1146             :                 CPLDebug("GML", "Using embedded %s", pszFile);
    1147             :             }
    1148             : #endif
    1149             :         }
    1150             :     }
    1151          54 :     if (!pszWholeText)
    1152           0 :         return false;
    1153             : 
    1154          54 :     if (strstr(pszWholeText, "<GMLFeatureClassList") == nullptr)
    1155             :     {
    1156           0 :         VSIFree(pabyRet);
    1157           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1158             :                  "File %s does not contain a GMLFeatureClassList tree.",
    1159             :                  pszFile);
    1160           0 :         return false;
    1161             :     }
    1162             : 
    1163             :     /* -------------------------------------------------------------------- */
    1164             :     /*      Convert to XML parse tree.                                      */
    1165             :     /* -------------------------------------------------------------------- */
    1166         108 :     CPLXMLTreeCloser psRoot(CPLParseXMLString(pszWholeText));
    1167          54 :     VSIFree(pabyRet);
    1168             : 
    1169             :     // We assume parser will report errors via CPL.
    1170          54 :     if (psRoot.get() == nullptr)
    1171           0 :         return false;
    1172             : 
    1173         106 :     if (psRoot->eType != CXT_Element ||
    1174          52 :         !EQUAL(psRoot->pszValue, "GMLFeatureClassList"))
    1175             :     {
    1176           2 :         CPLError(CE_Failure, CPLE_AppDefined,
    1177             :                  "File %s is not a GMLFeatureClassList document.", pszFile);
    1178           2 :         return false;
    1179             :     }
    1180             : 
    1181             :     const char *pszSequentialLayers =
    1182          52 :         CPLGetXMLValue(psRoot.get(), "SequentialLayers", nullptr);
    1183          52 :     if (pszSequentialLayers)
    1184           8 :         m_nHasSequentialLayers = CPLTestBool(pszSequentialLayers);
    1185             : 
    1186             :     /* -------------------------------------------------------------------- */
    1187             :     /*      Extract feature classes for all definitions found.              */
    1188             :     /* -------------------------------------------------------------------- */
    1189         188 :     for (CPLXMLNode *psThis = psRoot->psChild; psThis != nullptr;
    1190         136 :          psThis = psThis->psNext)
    1191             :     {
    1192         136 :         if (psThis->eType == CXT_Element &&
    1193         109 :             EQUAL(psThis->pszValue, "GMLFeatureClass"))
    1194             :         {
    1195         101 :             GMLFeatureClass *poClass = new GMLFeatureClass();
    1196             : 
    1197         101 :             if (!poClass->InitializeFromXML(psThis))
    1198             :             {
    1199           0 :                 delete poClass;
    1200           0 :                 return false;
    1201             :             }
    1202             : 
    1203         101 :             poClass->SetSchemaLocked(true);
    1204             : 
    1205         101 :             AddClass(poClass);
    1206             :         }
    1207             :     }
    1208             : 
    1209          52 :     SetClassListLocked(true);
    1210             : 
    1211          52 :     return true;
    1212             : }
    1213             : 
    1214             : /************************************************************************/
    1215             : /*                            SaveClasses()                             */
    1216             : /************************************************************************/
    1217             : 
    1218          85 : bool GMLReader::SaveClasses(const char *pszFile)
    1219             : 
    1220             : {
    1221             :     // Add logic later to determine reasonable default schema file.
    1222          85 :     if (pszFile == nullptr)
    1223           0 :         return false;
    1224             : 
    1225             :     /* -------------------------------------------------------------------- */
    1226             :     /*      Create in memory schema tree.                                   */
    1227             :     /* -------------------------------------------------------------------- */
    1228             :     CPLXMLNode *psRoot =
    1229          85 :         CPLCreateXMLNode(nullptr, CXT_Element, "GMLFeatureClassList");
    1230             : 
    1231          85 :     if (m_nHasSequentialLayers != -1 && m_nClassCount > 1)
    1232             :     {
    1233          13 :         CPLCreateXMLElementAndValue(psRoot, "SequentialLayers",
    1234          13 :                                     m_nHasSequentialLayers ? "true" : "false");
    1235             :     }
    1236             : 
    1237         188 :     for (int iClass = 0; iClass < m_nClassCount; iClass++)
    1238             :     {
    1239         103 :         CPLAddXMLChild(psRoot, m_papoClass[iClass]->SerializeToXML());
    1240             :     }
    1241             : 
    1242             :     /* -------------------------------------------------------------------- */
    1243             :     /*      Serialize to disk.                                              */
    1244             :     /* -------------------------------------------------------------------- */
    1245          85 :     char *pszWholeText = CPLSerializeXMLTree(psRoot);
    1246             : 
    1247          85 :     CPLDestroyXMLNode(psRoot);
    1248             : 
    1249          85 :     auto fp = VSIVirtualHandleUniquePtr(VSIFOpenL(pszFile, "wb"));
    1250             : 
    1251          85 :     bool bSuccess = true;
    1252          85 :     if (fp == nullptr)
    1253           0 :         bSuccess = false;
    1254             :     else
    1255             :     {
    1256          85 :         if (fp->Write(pszWholeText, strlen(pszWholeText), 1) != 1)
    1257           0 :             bSuccess = false;
    1258             :     }
    1259             : 
    1260          85 :     CPLFree(pszWholeText);
    1261             : 
    1262          85 :     return bSuccess;
    1263             : }
    1264             : 
    1265             : /************************************************************************/
    1266             : /*                          PrescanForSchema()                          */
    1267             : /*                                                                      */
    1268             : /*      For now we use a pretty dumb approach of just doing a normal    */
    1269             : /*      scan of the whole file, building up the schema information.     */
    1270             : /*      Eventually we hope to do a more efficient scan when just        */
    1271             : /*      looking for schema information.                                 */
    1272             : /************************************************************************/
    1273             : 
    1274         103 : bool GMLReader::PrescanForSchema(bool bGetExtents, bool bOnlyDetectSRS)
    1275             : 
    1276             : {
    1277         103 :     if (m_pszFilename == nullptr)
    1278           0 :         return false;
    1279             : 
    1280         103 :     if (!bOnlyDetectSRS)
    1281             :     {
    1282          94 :         SetClassListLocked(false);
    1283          94 :         ClearClasses();
    1284             :     }
    1285             : 
    1286         103 :     if (!SetupParser())
    1287           0 :         return false;
    1288             : 
    1289         103 :     m_bCanUseGlobalSRSName = true;
    1290             : 
    1291         103 :     GMLFeatureClass *poLastClass = nullptr;
    1292             : 
    1293         103 :     m_nHasSequentialLayers = TRUE;
    1294             : 
    1295         103 :     void *hCacheSRS = GML_BuildOGRGeometryFromList_CreateCache();
    1296             : 
    1297         206 :     std::string osWork;
    1298             : 
    1299         112 :     for (int i = 0; i < m_nClassCount; i++)
    1300             :     {
    1301           9 :         m_papoClass[i]->SetFeatureCount(-1);
    1302           9 :         m_papoClass[i]->SetSRSName(nullptr);
    1303             :     }
    1304             : 
    1305         103 :     GMLFeature *poFeature = nullptr;
    1306         103 :     std::set<GMLFeatureClass *> knownClasses;
    1307         103 :     bool bFoundPerFeatureSRSName = false;
    1308             : 
    1309         598 :     while ((poFeature = NextFeature()) != nullptr)
    1310             :     {
    1311         495 :         GMLFeatureClass *poClass = poFeature->GetClass();
    1312             : 
    1313         495 :         if (knownClasses.find(poClass) == knownClasses.end())
    1314             :         {
    1315         116 :             knownClasses.insert(poClass);
    1316         116 :             if (m_pszGlobalSRSName && GML_IsLegitSRSName(m_pszGlobalSRSName))
    1317             :             {
    1318          37 :                 poClass->SetSRSName(m_pszGlobalSRSName);
    1319             :             }
    1320             :         }
    1321             : 
    1322         524 :         if (poLastClass != nullptr && poClass != poLastClass &&
    1323          29 :             poClass->GetFeatureCount() != -1)
    1324           0 :             m_nHasSequentialLayers = false;
    1325         495 :         poLastClass = poClass;
    1326             : 
    1327         495 :         if (poClass->GetFeatureCount() == -1)
    1328         116 :             poClass->SetFeatureCount(1);
    1329             :         else
    1330         379 :             poClass->SetFeatureCount(poClass->GetFeatureCount() + 1);
    1331             : 
    1332         495 :         const CPLXMLNode *const *papsGeometry = poFeature->GetGeometryList();
    1333         495 :         bool bGeometryColumnJustCreated = false;
    1334             : 
    1335         495 :         const CPLXMLNode *apsGeometries[2] = {nullptr, nullptr};
    1336             :         const CPLXMLNode *psBoundedByGeometry =
    1337         495 :             poFeature->GetBoundedByGeometry();
    1338         495 :         int nFeatureGeomCount = poFeature->GetGeometryCount();
    1339         495 :         if (psBoundedByGeometry && nFeatureGeomCount == 0 &&
    1340           3 :             strcmp(psBoundedByGeometry->pszValue, "null") != 0)
    1341             :         {
    1342           1 :             apsGeometries[0] = psBoundedByGeometry;
    1343           1 :             papsGeometry = apsGeometries;
    1344           1 :             nFeatureGeomCount = 1;
    1345             :         }
    1346             : 
    1347         495 :         if (!bOnlyDetectSRS && papsGeometry != nullptr &&
    1348         476 :             papsGeometry[0] != nullptr)
    1349             :         {
    1350         437 :             if (poClass->GetGeometryPropertyCount() == 0)
    1351             :             {
    1352         176 :                 std::string osPath(poClass->GetSingleGeomElemPath());
    1353         109 :                 if (osPath.empty() &&
    1354         109 :                     poClass->IsConsistentSingleGeomElemPath() &&
    1355          17 :                     papsGeometry[0] == psBoundedByGeometry)
    1356             :                 {
    1357           1 :                     osPath = "boundedBy";
    1358             :                 }
    1359          88 :                 std::string osGeomName(osPath);
    1360          88 :                 const auto nPos = osGeomName.rfind('|');
    1361          88 :                 if (nPos != std::string::npos)
    1362           2 :                     osGeomName = osGeomName.substr(nPos + 1);
    1363          88 :                 bGeometryColumnJustCreated = true;
    1364         176 :                 poClass->AddGeometryProperty(new GMLGeometryPropertyDefn(
    1365         176 :                     osGeomName.c_str(), osPath.c_str(), wkbUnknown, -1, true));
    1366             :             }
    1367             :         }
    1368             : 
    1369         495 :         if (bGetExtents && papsGeometry != nullptr)
    1370             :         {
    1371             :             const int nIters = std::min(nFeatureGeomCount,
    1372         495 :                                         poClass->GetGeometryPropertyCount());
    1373         959 :             for (int i = 0; i < nIters; ++i)
    1374             :             {
    1375         464 :                 if (papsGeometry[i] == nullptr)
    1376           7 :                     continue;
    1377             : 
    1378         459 :                 const CPLXMLNode *myGeometryList[2] = {papsGeometry[i],
    1379         459 :                                                        nullptr};
    1380         918 :                 OGRGeometry *poGeometry = GML_BuildOGRGeometryFromList(
    1381         459 :                     myGeometryList, true, m_bInvertAxisOrderIfLatLong, nullptr,
    1382         459 :                     m_bConsiderEPSGAsURN, m_eSwapCoordinates,
    1383         459 :                     m_bGetSecondaryGeometryOption, hCacheSRS,
    1384         459 :                     m_bFaceHoleNegative);
    1385         459 :                 if (poGeometry == nullptr)
    1386           2 :                     continue;
    1387             : 
    1388         457 :                 auto poGeomProperty = poClass->GetGeometryProperty(i);
    1389             :                 OGRwkbGeometryType eGType =
    1390         457 :                     static_cast<OGRwkbGeometryType>(poGeomProperty->GetType());
    1391             : 
    1392         914 :                 const char *pszSRSName = GML_ExtractSrsNameFromGeometry(
    1393         457 :                     myGeometryList, osWork, m_bConsiderEPSGAsURN);
    1394         457 :                 if (pszSRSName != nullptr)
    1395         138 :                     bFoundPerFeatureSRSName = true;
    1396             : 
    1397         457 :                 if (pszSRSName != nullptr && m_pszGlobalSRSName != nullptr &&
    1398          73 :                     !EQUAL(pszSRSName, m_pszGlobalSRSName))
    1399             :                 {
    1400           0 :                     m_bCanUseGlobalSRSName = false;
    1401             :                 }
    1402         457 :                 if (m_pszGlobalSRSName == nullptr || pszSRSName != nullptr)
    1403             :                 {
    1404         201 :                     if (poClass->GetGeometryPropertyCount() == 1)
    1405         192 :                         poClass->MergeSRSName(pszSRSName);
    1406             :                     else
    1407           9 :                         poGeomProperty->MergeSRSName(pszSRSName ? pszSRSName
    1408             :                                                                 : "");
    1409             :                 }
    1410             : 
    1411             :                 // Merge geometry type into layer.
    1412         457 :                 if (bGeometryColumnJustCreated)
    1413             :                 {
    1414          86 :                     poGeomProperty->SetType(poGeometry->getGeometryType());
    1415             :                 }
    1416             :                 else
    1417             :                 {
    1418         371 :                     poGeomProperty->SetType(OGRMergeGeometryTypesEx(
    1419         371 :                         eGType, poGeometry->getGeometryType(), true));
    1420             :                 }
    1421             : 
    1422             :                 // Merge extents.
    1423         457 :                 if (!poGeometry->IsEmpty())
    1424             :                 {
    1425         456 :                     double dfXMin = 0.0;
    1426         456 :                     double dfXMax = 0.0;
    1427         456 :                     double dfYMin = 0.0;
    1428         456 :                     double dfYMax = 0.0;
    1429             : 
    1430         456 :                     OGREnvelope sEnvelope;
    1431             : 
    1432         456 :                     poGeometry->getEnvelope(&sEnvelope);
    1433         456 :                     if (poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax))
    1434             :                     {
    1435         361 :                         dfXMin = std::min(dfXMin, sEnvelope.MinX);
    1436         361 :                         dfXMax = std::max(dfXMax, sEnvelope.MaxX);
    1437         361 :                         dfYMin = std::min(dfYMin, sEnvelope.MinY);
    1438         361 :                         dfYMax = std::max(dfYMax, sEnvelope.MaxY);
    1439             :                     }
    1440             :                     else
    1441             :                     {
    1442          95 :                         dfXMin = sEnvelope.MinX;
    1443          95 :                         dfXMax = sEnvelope.MaxX;
    1444          95 :                         dfYMin = sEnvelope.MinY;
    1445          95 :                         dfYMax = sEnvelope.MaxY;
    1446             :                     }
    1447             : 
    1448         456 :                     poClass->SetExtents(dfXMin, dfXMax, dfYMin, dfYMax);
    1449             :                 }
    1450         457 :                 delete poGeometry;
    1451             :             }
    1452             :         }
    1453             : 
    1454         495 :         delete poFeature;
    1455             :     }
    1456             : 
    1457         103 :     GML_BuildOGRGeometryFromList_DestroyCache(hCacheSRS);
    1458             : 
    1459         103 :     if (bGetExtents && m_bCanUseGlobalSRSName && m_pszGlobalSRSName &&
    1460          26 :         !bFoundPerFeatureSRSName && m_bInvertAxisOrderIfLatLong &&
    1461         219 :         GML_IsLegitSRSName(m_pszGlobalSRSName) &&
    1462          13 :         GML_IsSRSLatLongOrder(m_pszGlobalSRSName))
    1463             :     {
    1464             :         /* So when we have computed the extent, we didn't know yet */
    1465             :         /* the SRS to use. Now we know it, we have to fix the extent */
    1466             :         /* order */
    1467             : 
    1468           6 :         for (int i = 0; i < m_nClassCount; i++)
    1469             :         {
    1470           3 :             GMLFeatureClass *poClass = m_papoClass[i];
    1471           3 :             if (poClass->HasExtents())
    1472             :             {
    1473           2 :                 double dfXMin = 0.0;
    1474           2 :                 double dfXMax = 0.0;
    1475           2 :                 double dfYMin = 0.0;
    1476           2 :                 double dfYMax = 0.0;
    1477           2 :                 if (poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax))
    1478           2 :                     poClass->SetExtents(dfYMin, dfYMax, dfXMin, dfXMax);
    1479             :             }
    1480             :         }
    1481             :     }
    1482             : 
    1483         103 :     CleanupParser();
    1484             : 
    1485         103 :     return true;
    1486             : }
    1487             : 
    1488             : /************************************************************************/
    1489             : /*                            ResetReading()                            */
    1490             : /************************************************************************/
    1491             : 
    1492         708 : void GMLReader::ResetReading()
    1493             : 
    1494             : {
    1495         708 :     CleanupParser();
    1496         708 :     SetFilteredClassName(nullptr);
    1497         708 : }
    1498             : 
    1499             : /************************************************************************/
    1500             : /*                          SetGlobalSRSName()                          */
    1501             : /************************************************************************/
    1502             : 
    1503         193 : void GMLReader::SetGlobalSRSName(const char *pszGlobalSRSName)
    1504             : {
    1505         193 :     if (m_pszGlobalSRSName == nullptr && pszGlobalSRSName != nullptr)
    1506             :     {
    1507          59 :         const char *pszVertCS_EPSG = nullptr;
    1508          73 :         if (STARTS_WITH(pszGlobalSRSName, "EPSG:") &&
    1509          14 :             (pszVertCS_EPSG = strstr(pszGlobalSRSName, ", EPSG:")) != nullptr)
    1510             :         {
    1511           2 :             m_pszGlobalSRSName =
    1512           2 :                 CPLStrdup(CPLSPrintf("EPSG:%d+%d", atoi(pszGlobalSRSName + 5),
    1513             :                                      atoi(pszVertCS_EPSG + 7)));
    1514             :         }
    1515          57 :         else if (STARTS_WITH(pszGlobalSRSName, "EPSG:") && m_bConsiderEPSGAsURN)
    1516             :         {
    1517           5 :             m_pszGlobalSRSName = CPLStrdup(
    1518             :                 CPLSPrintf("urn:ogc:def:crs:EPSG::%s", pszGlobalSRSName + 5));
    1519             :         }
    1520             :         else
    1521             :         {
    1522          52 :             m_pszGlobalSRSName = CPLStrdup(pszGlobalSRSName);
    1523             :         }
    1524          59 :         m_bCanUseGlobalSRSName = m_pszGlobalSRSName != nullptr;
    1525             :     }
    1526         193 : }
    1527             : 
    1528             : /************************************************************************/
    1529             : /*                       SetFilteredClassName()                         */
    1530             : /************************************************************************/
    1531             : 
    1532         786 : bool GMLReader::SetFilteredClassName(const char *pszClassName)
    1533             : {
    1534         786 :     CPLFree(m_pszFilteredClassName);
    1535         786 :     m_pszFilteredClassName = pszClassName ? CPLStrdup(pszClassName) : nullptr;
    1536             : 
    1537         786 :     m_nFilteredClassIndex = -1;
    1538         786 :     if (m_pszFilteredClassName != nullptr)
    1539             :     {
    1540         534 :         for (int i = 0; i < m_nClassCount; i++)
    1541             :         {
    1542        1002 :             if (strcmp(m_papoClass[i]->GetElementName(),
    1543         501 :                        m_pszFilteredClassName) == 0)
    1544             :             {
    1545          45 :                 m_nFilteredClassIndex = i;
    1546          45 :                 break;
    1547             :             }
    1548             :         }
    1549             :     }
    1550             : 
    1551         786 :     return true;
    1552             : }

Generated by: LCOV version 1.14