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

Generated by: LCOV version 1.14