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

Generated by: LCOV version 1.14