LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gmlas - ogrgmlasreader.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1523 1634 93.2 %
Date: 2025-07-09 17:50:03 Functions: 56 58 96.6 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * Project:  OGR
       3             :  * Purpose:  OGRGMLASDriver implementation
       4             :  * Author:   Even Rouault, <even dot rouault at spatialys dot com>
       5             :  *
       6             :  * Initial development funded by the European Earth observation programme
       7             :  * Copernicus
       8             :  *
       9             :  ******************************************************************************
      10             :  * Copyright (c) 2016, Even Rouault, <even dot rouault at spatialys dot com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "ogr_gmlas.h"
      16             : 
      17             : #include "ogr_p.h"
      18             : 
      19             : #include "cpl_json_header.h"
      20             : 
      21             : #include <algorithm>
      22             : 
      23             : IGMLASInputSourceClosing::~IGMLASInputSourceClosing() = default;
      24             : 
      25             : /************************************************************************/
      26             : /*                        GMLASBinInputStream                           */
      27             : /************************************************************************/
      28             : 
      29             : class GMLASBinInputStream : public BinInputStream
      30             : {
      31             :     VSILFILE *m_fp = nullptr;
      32             : 
      33             :     CPL_DISALLOW_COPY_ASSIGN(GMLASBinInputStream)
      34             : 
      35             :   public:
      36             :     explicit GMLASBinInputStream(VSILFILE *fp);
      37             :     virtual ~GMLASBinInputStream();
      38             : 
      39             :     virtual XMLFilePos curPos() const override;
      40             :     virtual XMLSize_t readBytes(XMLByte *const toFill,
      41             :                                 const XMLSize_t maxToRead) override;
      42             :     virtual const XMLCh *getContentType() const override;
      43             : };
      44             : 
      45             : /************************************************************************/
      46             : /*                        GMLASBinInputStream()                         */
      47             : /************************************************************************/
      48             : 
      49        3323 : GMLASBinInputStream::GMLASBinInputStream(VSILFILE *fp)
      50             : {
      51        3323 :     m_fp = fp;
      52        3323 :     VSIFSeekL(fp, 0, SEEK_SET);
      53        3323 : }
      54             : 
      55             : /************************************************************************/
      56             : /*                       ~GMLASBinInputStream()                         */
      57             : /************************************************************************/
      58             : 
      59        6646 : GMLASBinInputStream::~GMLASBinInputStream()
      60             : {
      61        6646 : }
      62             : 
      63             : /************************************************************************/
      64             : /*                                curPos()                              */
      65             : /************************************************************************/
      66             : 
      67           0 : XMLFilePos GMLASBinInputStream::curPos() const
      68             : {
      69           0 :     return static_cast<XMLFilePos>(VSIFTellL(m_fp));
      70             : }
      71             : 
      72             : /************************************************************************/
      73             : /*                               readBytes()                            */
      74             : /************************************************************************/
      75             : 
      76        8838 : XMLSize_t GMLASBinInputStream::readBytes(XMLByte *const toFill,
      77             :                                          const XMLSize_t maxToRead)
      78             : {
      79        8838 :     return static_cast<XMLSize_t>(VSIFReadL(toFill, 1, maxToRead, m_fp));
      80             : }
      81             : 
      82             : /************************************************************************/
      83             : /*                            getContentType()                          */
      84             : /************************************************************************/
      85             : 
      86           0 : const XMLCh *GMLASBinInputStream::getContentType() const
      87             : {
      88           0 :     return nullptr;
      89             : }
      90             : 
      91             : /************************************************************************/
      92             : /*                          GMLASInputSource()                          */
      93             : /************************************************************************/
      94             : 
      95        4070 : GMLASInputSource::GMLASInputSource(const char *pszFilename,
      96             :                                    const std::shared_ptr<VSIVirtualHandle> &fp,
      97        4070 :                                    MemoryManager *const manager)
      98        4070 :     : InputSource(manager), m_fp(fp), m_pnCounter(&m_nCounter),
      99        4070 :       m_osFilename(pszFilename)
     100             : {
     101             :     try
     102             :     {
     103        4070 :         XMLCh *pFilename = XMLString::transcode(pszFilename);
     104        4070 :         setPublicId(pFilename);
     105        4070 :         setSystemId(pFilename);
     106        4070 :         XMLString::release(&pFilename);
     107             :     }
     108           0 :     catch (const TranscodingException &e)
     109             :     {
     110           0 :         CPLError(CE_Failure, CPLE_AppDefined, "TranscodingException: %s",
     111           0 :                  transcode(e.getMessage()).c_str());
     112             :     }
     113        4070 : }
     114             : 
     115             : /************************************************************************/
     116             : /*                        SetClosingCallback()                          */
     117             : /************************************************************************/
     118             : 
     119        1107 : void GMLASInputSource::SetClosingCallback(IGMLASInputSourceClosing *cbk)
     120             : {
     121        1107 :     m_cbk = cbk;
     122        1107 : }
     123             : 
     124             : /************************************************************************/
     125             : /*                         ~GMLASInputSource()                          */
     126             : /************************************************************************/
     127             : 
     128        6569 : GMLASInputSource::~GMLASInputSource()
     129             : {
     130        4070 :     if (m_cbk)
     131        1107 :         m_cbk->notifyClosing(m_osFilename);
     132        6569 : }
     133             : 
     134             : /************************************************************************/
     135             : /*                              makeStream()                            */
     136             : /************************************************************************/
     137             : 
     138        3325 : BinInputStream *GMLASInputSource::makeStream() const
     139             : {
     140             :     // This is a lovely cheating around the const qualifier of this method !
     141             :     // We cannot modify m_nCounter directly, but we can change the value
     142             :     // pointed by m_pnCounter...
     143        3325 :     if (*m_pnCounter != 0)
     144             :     {
     145           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     146             :                  "makeStream() called several times on same GMLASInputSource");
     147           0 :         return nullptr;
     148             :     }
     149        3325 :     (*m_pnCounter)++;
     150        3325 :     if (m_fp == nullptr)
     151           2 :         return nullptr;
     152        3323 :     return new GMLASBinInputStream(m_fp.get());
     153             : }
     154             : 
     155             : /************************************************************************/
     156             : /*                            warning()                                 */
     157             : /************************************************************************/
     158             : 
     159           2 : void GMLASErrorHandler::warning(const SAXParseException &e)
     160             : {
     161           2 :     handle(e, CE_Warning);
     162           2 : }
     163             : 
     164             : /************************************************************************/
     165             : /*                             error()                                  */
     166             : /************************************************************************/
     167             : 
     168         226 : void GMLASErrorHandler::error(const SAXParseException &e)
     169             : {
     170         226 :     m_bFailed = true;
     171         226 :     handle(e, CE_Failure);
     172         226 : }
     173             : 
     174             : /************************************************************************/
     175             : /*                          fatalError()                                */
     176             : /************************************************************************/
     177             : 
     178           4 : void GMLASErrorHandler::fatalError(const SAXParseException &e)
     179             : {
     180           4 :     m_bFailed = true;
     181           4 :     handle(e, CE_Failure);
     182           4 : }
     183             : 
     184             : /************************************************************************/
     185             : /*                            handle()                                  */
     186             : /************************************************************************/
     187             : 
     188         232 : void GMLASErrorHandler::handle(const SAXParseException &e, CPLErr eErr)
     189             : {
     190         232 :     const XMLCh *resourceId(e.getPublicId());
     191             : 
     192         232 :     if (resourceId == nullptr || resourceId[0] == 0)
     193         112 :         resourceId = e.getSystemId();
     194             : 
     195         464 :     CPLString osErrorMsg(transcode(e.getMessage()));
     196         464 :     if (m_bSchemaFullChecking &&
     197         232 :         osErrorMsg.find("forbidden restriction of any particle") !=
     198             :             std::string::npos)
     199             :     {
     200           0 :         osErrorMsg += ". You may retry with the " +
     201           0 :                       CPLString(szSCHEMA_FULL_CHECKING_OPTION) +
     202           0 :                       "=NO open option";
     203             :     }
     204         464 :     else if (!m_bHandleMultipleImports &&
     205         232 :              osErrorMsg.find("not found") != std::string::npos)
     206             :     {
     207           8 :         osErrorMsg += ". You may retry with the " +
     208          24 :                       CPLString(szHANDLE_MULTIPLE_IMPORTS_OPTION) +
     209           8 :                       "=YES open option";
     210             :     }
     211             : 
     212         464 :     CPLString osFullErrorMsg;
     213         232 :     osFullErrorMsg.Printf("%s:%d:%d %s", transcode(resourceId).c_str(),
     214         464 :                           static_cast<int>(e.getLineNumber()),
     215         232 :                           static_cast<int>(e.getColumnNumber()),
     216         464 :                           osErrorMsg.c_str());
     217             : 
     218         445 :     if (m_bHideGMLTypeNotFound && m_osGMLTypeNotFoundError.empty() &&
     219         213 :         osErrorMsg.find(
     220             :             "http://www.opengis.net/gml/3.2:AbstractCRS' not found") !=
     221             :             std::string::npos)
     222             :     {
     223           2 :         m_osGMLTypeNotFoundError = std::move(osFullErrorMsg);
     224             :     }
     225         230 :     else if (m_bHideGMLTypeNotFound && !m_osGMLTypeNotFoundError.empty())
     226             :     {
     227             :         // do nothing
     228             :     }
     229             :     else
     230             :     {
     231         224 :         CPLError(eErr, CPLE_AppDefined, "%s", osFullErrorMsg.c_str());
     232             :     }
     233         232 : }
     234             : 
     235             : /************************************************************************/
     236             : /*                     GMLASBaseEntityResolver()                        */
     237             : /************************************************************************/
     238             : 
     239         240 : GMLASBaseEntityResolver::GMLASBaseEntityResolver(const CPLString &osBasePath,
     240         240 :                                                  GMLASXSDCache &oCache)
     241         240 :     : m_oCache(oCache)
     242             : {
     243         240 :     m_aosPathStack.push_back(osBasePath);
     244         240 : }
     245             : 
     246             : /************************************************************************/
     247             : /*                    ~GMLASBaseEntityResolver()                        */
     248             : /************************************************************************/
     249             : 
     250         290 : GMLASBaseEntityResolver::~GMLASBaseEntityResolver()
     251             : {
     252         240 :     CPLAssert(m_aosPathStack.size() == 1);
     253         290 : }
     254             : 
     255             : /************************************************************************/
     256             : /*                            notifyClosing()                           */
     257             : /************************************************************************/
     258             : 
     259             : /* Called by GMLASInputSource destructor. This is useful for use to */
     260             : /* know where a .xsd has been finished from processing. Note that we */
     261             : /* strongly depend on Xerces behavior here... */
     262        1107 : void GMLASBaseEntityResolver::notifyClosing(const CPLString &osFilename)
     263             : {
     264        1107 :     CPLDebug("GMLAS", "Closing %s", osFilename.c_str());
     265             : 
     266        1107 :     CPLAssert(m_aosPathStack.back() ==
     267             :               CPLString(CPLGetDirnameSafe(osFilename)));
     268        1107 :     m_aosPathStack.pop_back();
     269        1107 : }
     270             : 
     271             : /************************************************************************/
     272             : /*                            SetBasePath()                             */
     273             : /************************************************************************/
     274             : 
     275         281 : void GMLASBaseEntityResolver::SetBasePath(const CPLString &osBasePath)
     276             : {
     277         281 :     CPLAssert(m_aosPathStack.size() == 1);
     278         281 :     m_aosPathStack[0] = osBasePath;
     279         281 : }
     280             : 
     281             : /************************************************************************/
     282             : /*                         DoExtraSchemaProcessing()                    */
     283             : /************************************************************************/
     284             : 
     285         254 : void GMLASBaseEntityResolver::DoExtraSchemaProcessing(
     286             :     const CPLString & /*osFilename*/,
     287             :     const std::shared_ptr<VSIVirtualHandle> & /*fp*/)
     288             : {
     289         254 : }
     290             : 
     291             : /************************************************************************/
     292             : /*                         resolveEntity()                              */
     293             : /************************************************************************/
     294             : 
     295             : InputSource *
     296        1107 : GMLASBaseEntityResolver::resolveEntity(const XMLCh *const /*publicId*/,
     297             :                                        const XMLCh *const systemId)
     298             : {
     299             :     // Can happen on things like <xs:import
     300             :     // namespace="http://www.w3.org/XML/1998/namespace"/>
     301        1107 :     if (systemId == nullptr)
     302           0 :         return nullptr;
     303             : 
     304        2214 :     CPLString osSystemId(transcode(systemId));
     305             : 
     306        1107 :     if (osSystemId.find("/gml/2.1.2/") != std::string::npos)
     307           0 :         m_osGMLVersionFound = "2.1.2";
     308        1107 :     else if (osSystemId.find("/gml/3.1.1/") != std::string::npos)
     309          19 :         m_osGMLVersionFound = "3.1.1";
     310        1088 :     else if (osSystemId.find("/gml/3.2.1/") != std::string::npos)
     311          15 :         m_osGMLVersionFound = "3.2.1";
     312             : 
     313        1107 :     constexpr const char *GML_321_LOC_SUFFIX = "/gml/3.2.1/gml.xsd";
     314        1107 :     constexpr const char *GML_321_OGC_SCHEMA_LOC =
     315             :         "http://schemas.opengis.net/gml/3.2.1/gml.xsd";
     316        1107 :     if (osSystemId.size() > strlen(GML_321_LOC_SUFFIX) &&
     317         742 :         strcmp(osSystemId.c_str() + osSystemId.size() -
     318             :                    strlen(GML_321_LOC_SUFFIX),
     319        1849 :                GML_321_LOC_SUFFIX) == 0 &&
     320          15 :         osSystemId != GML_321_OGC_SCHEMA_LOC)
     321             :     {
     322           2 :         m_bFoundNonOfficialGMLSchemaLocation = true;
     323           2 :         if (m_bSubstituteWithOGCSchemaLocation)
     324           0 :             osSystemId = GML_321_OGC_SCHEMA_LOC;
     325             :     }
     326             : 
     327        2214 :     CPLString osNewPath;
     328             :     auto fp = std::shared_ptr<VSIVirtualHandle>(
     329        1107 :         m_oCache.Open(osSystemId, m_aosPathStack.back(), osNewPath),
     330        1107 :         VSIVirtualHandleCloser{});
     331             : 
     332        1107 :     if (fp != nullptr)
     333             :     {
     334        1105 :         m_oSetSchemaURLs.insert(osNewPath);
     335             : 
     336        1105 :         CPLDebug("GMLAS", "Opening %s", osNewPath.c_str());
     337        1105 :         DoExtraSchemaProcessing(osNewPath, fp);
     338             :     }
     339             : 
     340        1107 :     m_aosPathStack.push_back(CPLGetDirnameSafe(osNewPath).c_str());
     341        1107 :     GMLASInputSource *poIS = new GMLASInputSource(osNewPath, fp);
     342        1107 :     poIS->SetClosingCallback(this);
     343        1107 :     return poIS;
     344             : }
     345             : 
     346             : /************************************************************************/
     347             : /*                             Dump()                                   */
     348             : /************************************************************************/
     349             : 
     350        3517 : void GMLASReader::Context::Dump() const
     351             : {
     352        3517 :     CPLDebug("GMLAS", "Context");
     353        3517 :     CPLDebug("GMLAS", "  m_nLevel = %d", m_nLevel);
     354        3517 :     CPLDebug("GMLAS", "  m_poFeature = %p", m_poFeature);
     355        3517 :     if (CPLIsDebugEnabled())
     356             :     {
     357           0 :         if (m_poFeature)
     358           0 :             m_poFeature->DumpReadable(stderr);
     359             :     }
     360        3517 :     CPLDebug("GMLAS", "  m_poLayer = %p (%s)", m_poLayer,
     361        3517 :              m_poLayer ? m_poLayer->GetName() : "");
     362        3517 :     CPLDebug("GMLAS", "  m_poGroupLayer = %p (%s)", m_poGroupLayer,
     363        3517 :              m_poGroupLayer ? m_poGroupLayer->GetName() : "");
     364        3517 :     CPLDebug("GMLAS", "  m_nGroupLayerLevel = %d", m_nGroupLayerLevel);
     365        3517 :     CPLDebug("GMLAS", "  m_nLastFieldIdxGroupLayer = %d",
     366        3517 :              m_nLastFieldIdxGroupLayer);
     367        3517 :     CPLDebug("GMLAS", "  m_osCurSubXPath = %s", m_osCurSubXPath.c_str());
     368        3517 : }
     369             : 
     370             : /************************************************************************/
     371             : /*                             GMLASReader()                            */
     372             : /************************************************************************/
     373             : 
     374        1392 : GMLASReader::GMLASReader(GMLASXSDCache &oCache,
     375             :                          const GMLASXPathMatcher &oIgnoredXPathMatcher,
     376        1392 :                          GMLASXLinkResolver &oXLinkResolver)
     377             :     : m_oCache(oCache), m_oIgnoredXPathMatcher(oIgnoredXPathMatcher),
     378             :       m_oXLinkResolver(oXLinkResolver),
     379        2784 :       m_nMaxLevel(atoi(CPLGetConfigOption("GMLAS_XML_MAX_LEVEL", "100"))),
     380        1392 :       m_nMaxContentSize(static_cast<size_t>(
     381        2784 :           atoi(CPLGetConfigOption("GMLAS_XML_MAX_CONTENT_SIZE", "512000000")))),
     382             :       m_bWarnUnexpected(
     383        1392 :           CPLTestBool(CPLGetConfigOption("GMLAS_WARN_UNEXPECTED", "FALSE")))
     384             : {
     385        1392 : }
     386             : 
     387             : /************************************************************************/
     388             : /*                            ~GMLASReader()                            */
     389             : /************************************************************************/
     390             : 
     391        2784 : GMLASReader::~GMLASReader()
     392             : {
     393        1653 :     if (m_oCurCtxt.m_poFeature != nullptr && !m_aoStackContext.empty() &&
     394         261 :         m_oCurCtxt.m_poFeature != m_aoStackContext.back().m_poFeature)
     395             :     {
     396          10 :         CPLDebug("GMLAS", "Delete feature m_oCurCtxt.m_poFeature=%p",
     397             :                  m_oCurCtxt.m_poFeature);
     398          10 :         delete m_oCurCtxt.m_poFeature;
     399             :     }
     400        1854 :     for (size_t i = 0; i < m_aoStackContext.size(); i++)
     401             :     {
     402         663 :         if (i == 0 || m_aoStackContext[i].m_poFeature !=
     403         201 :                           m_aoStackContext[i - 1].m_poFeature)
     404             :         {
     405         462 :             CPLDebug("GMLAS",
     406             :                      "Delete feature m_aoStackContext[%d].m_poFeature=%p",
     407         462 :                      static_cast<int>(i), m_aoStackContext[i].m_poFeature);
     408         462 :             delete m_aoStackContext[i].m_poFeature;
     409             :         }
     410             :     }
     411             : 
     412        1392 :     if (!m_apsXMLNodeStack.empty())
     413             :     {
     414           0 :         CPLDestroyXMLNode(m_apsXMLNodeStack[0].psNode);
     415             :     }
     416        2784 : }
     417             : 
     418             : /************************************************************************/
     419             : /*                          SetLayerOfInterest()                        */
     420             : /************************************************************************/
     421             : 
     422        1258 : void GMLASReader::SetLayerOfInterest(OGRGMLASLayer *poLayer)
     423             : {
     424        1258 :     m_poLayerOfInterest = poLayer;
     425        1258 : }
     426             : 
     427             : /************************************************************************/
     428             : /*                      SetSWEDataArrayLayersRef()                      */
     429             : /************************************************************************/
     430             : 
     431        1267 : void GMLASReader::SetSWEDataArrayLayersRef(
     432             :     const std::vector<OGRGMLASLayer *> &ar)
     433             : {
     434        1267 :     m_apoSWEDataArrayLayersRef = ar;
     435        1267 :     m_bProcessSWEDataArray = !ar.empty();
     436        1267 : }
     437             : 
     438             : /************************************************************************/
     439             : /*                          LoadXSDInParser()                           */
     440             : /************************************************************************/
     441             : 
     442         284 : bool GMLASReader::LoadXSDInParser(
     443             :     SAX2XMLReader *poParser, GMLASXSDCache &oCache,
     444             :     GMLASBaseEntityResolver &oXSDEntityResolver, const CPLString &osBaseDirname,
     445             :     const CPLString &osXSDFilename, Grammar **ppoGrammar,
     446             :     bool bSchemaFullChecking, bool bHandleMultipleImports)
     447             : {
     448         284 :     if (ppoGrammar != nullptr)
     449         283 :         *ppoGrammar = nullptr;
     450             : 
     451             :     const CPLString osModifXSDFilename(
     452         269 :         (osXSDFilename.find("http://") != 0 &&
     453         269 :          osXSDFilename.find("https://") != 0 &&
     454         150 :          CPLIsFilenameRelative(osXSDFilename))
     455         284 :             ? CPLString(
     456         386 :                   CPLFormFilenameSafe(osBaseDirname, osXSDFilename, nullptr))
     457         852 :             : osXSDFilename);
     458             : 
     459         286 :     for (int iPass = 0; iPass <= 1; ++iPass)
     460             :     {
     461         286 :         CPLString osResolvedFilename;
     462             :         auto fpXSD = std::shared_ptr<VSIVirtualHandle>(
     463         286 :             oCache.Open(osModifXSDFilename, CPLString(), osResolvedFilename),
     464         286 :             VSIVirtualHandleCloser{});
     465         286 :         if (fpXSD == nullptr)
     466             :         {
     467           5 :             return false;
     468             :         }
     469             : 
     470         281 :         poParser->setFeature(XMLUni::fgXercesSchemaFullChecking,
     471         281 :                              bSchemaFullChecking);
     472         281 :         poParser->setFeature(XMLUni::fgXercesHandleMultipleImports,
     473         281 :                              bHandleMultipleImports);
     474             : 
     475             :         // Install a temporary entity resolved based on the current XSD
     476         281 :         CPLString osXSDDirname(CPLGetDirnameSafe(osModifXSDFilename));
     477         550 :         if (osXSDFilename.find("http://") == 0 ||
     478         269 :             osXSDFilename.find("https://") == 0)
     479             :         {
     480         131 :             osXSDDirname = osXSDFilename.substr(0, osXSDFilename.rfind('/'));
     481             :         }
     482         281 :         oXSDEntityResolver.SetBasePath(osXSDDirname);
     483         281 :         oXSDEntityResolver.DoExtraSchemaProcessing(osResolvedFilename, fpXSD);
     484         281 :         if (iPass == 1)
     485           2 :             oXSDEntityResolver.SetSubstituteWithOGCSchemaLocation(true);
     486             : 
     487         281 :         EntityResolver *poOldEntityResolver = poParser->getEntityResolver();
     488         281 :         poParser->setEntityResolver(&oXSDEntityResolver);
     489             : 
     490             :         // Install a temporary error handler
     491         281 :         GMLASErrorHandler oErrorHandler;
     492         281 :         oErrorHandler.SetSchemaFullCheckingEnabled(bSchemaFullChecking);
     493         281 :         oErrorHandler.SetHandleMultipleImportsEnabled(bHandleMultipleImports);
     494         281 :         if (iPass == 0)
     495         279 :             oErrorHandler.SetHideGMLTypeNotFound(true);
     496         281 :         ErrorHandler *poOldErrorHandler = poParser->getErrorHandler();
     497         281 :         poParser->setErrorHandler(&oErrorHandler);
     498             : 
     499         281 :         GMLASInputSource oSource(osResolvedFilename, fpXSD);
     500         281 :         const bool bCacheGrammar = true;
     501         281 :         Grammar *poGrammar = nullptr;
     502         281 :         std::string osLoadGrammarErrorMsg("loadGrammar failed");
     503             : 
     504             :         const int nMaxMem = std::min(
     505         281 :             2048, std::max(0, atoi(CPLGetConfigOption(
     506         281 :                                   "OGR_GMLAS_XERCES_MAX_MEMORY", "500"))));
     507             :         const std::string osMsgMaxMem = CPLSPrintf(
     508             :             "Xerces-C memory allocation exceeds %d MB. "
     509             :             "This can happen on schemas with a big value for maxOccurs. "
     510             :             "Define the OGR_GMLAS_XERCES_MAX_MEMORY configuration option to a "
     511             :             "bigger value (in MB) to increase that limitation, "
     512             :             "or 0 to remove it completely.",
     513         281 :             nMaxMem);
     514             :         const double dfTimeout =
     515         281 :             CPLAtof(CPLGetConfigOption("OGR_GMLAS_XERCES_MAX_TIME", "2"));
     516             :         const std::string osMsgTimeout = CPLSPrintf(
     517             :             "Processing in Xerces exceeded maximum allowed of %.3f s. "
     518             :             "This can happen on schemas with a big value for maxOccurs. "
     519             :             "Define the OGR_GMLAS_XERCES_MAX_TIME configuration option to a "
     520             :             "bigger value (in second) to increase that limitation, "
     521             :             "or 0 to remove it completely.",
     522         281 :             dfTimeout);
     523         281 :         OGRStartXercesLimitsForThisThread(
     524         281 :             static_cast<size_t>(nMaxMem) * 1024 * 1024, osMsgMaxMem.c_str(),
     525             :             dfTimeout, osMsgTimeout.c_str());
     526             :         try
     527             :         {
     528         278 :             poGrammar = poParser->loadGrammar(
     529         281 :                 oSource, Grammar::SchemaGrammarType, bCacheGrammar);
     530             :         }
     531           0 :         catch (const SAXException &e)
     532             :         {
     533           0 :             osLoadGrammarErrorMsg += ": " + transcode(e.getMessage());
     534             :         }
     535           0 :         catch (const XMLException &e)
     536             :         {
     537           0 :             osLoadGrammarErrorMsg += ": " + transcode(e.getMessage());
     538             :         }
     539           4 :         catch (const OutOfMemoryException &e)
     540             :         {
     541           2 :             if (strstr(CPLGetLastErrorMsg(), "configuration option") == nullptr)
     542             :             {
     543           0 :                 osLoadGrammarErrorMsg += ": " + transcode(e.getMessage());
     544             :             }
     545             :         }
     546           1 :         catch (const DOMException &e)
     547             :         {
     548             :             // Can happen with a .xsd that has a bad <?xml version="
     549             :             // declaration.
     550           1 :             osLoadGrammarErrorMsg += ": " + transcode(e.getMessage());
     551             :         }
     552         281 :         OGRStopXercesLimitsForThisThread();
     553             : 
     554             :         // Restore previous handlers
     555         281 :         poParser->setEntityResolver(poOldEntityResolver);
     556         281 :         poParser->setErrorHandler(poOldErrorHandler);
     557             : 
     558         281 :         if (poGrammar == nullptr)
     559             :         {
     560           3 :             if (!osLoadGrammarErrorMsg.empty())
     561             :             {
     562           3 :                 CPLError(CE_Failure, CPLE_AppDefined, "%s",
     563             :                          osLoadGrammarErrorMsg.c_str());
     564             :             }
     565           3 :             return false;
     566             :         }
     567         278 :         if (oErrorHandler.hasFailed())
     568             :         {
     569           4 :             if (iPass == 0 && !oErrorHandler.GetGMLTypeNotFoundError().empty())
     570             :             {
     571           2 :                 if (oXSDEntityResolver.GetFoundNonOfficialGMLSchemaLocation())
     572             :                 {
     573           2 :                     CPLDebug(
     574             :                         "GMLAS",
     575             :                         "Error '%s' encountered, but non-official GML schema "
     576             :                         "location has been imported. Retry with official one",
     577           2 :                         oErrorHandler.GetGMLTypeNotFoundError().c_str());
     578           2 :                     continue;
     579             :                 }
     580             :                 else
     581             :                 {
     582           0 :                     CPLError(CE_Failure, CPLE_AppDefined, "%s",
     583           0 :                              oErrorHandler.GetGMLTypeNotFoundError().c_str());
     584             :                 }
     585             :             }
     586           2 :             return false;
     587             :         }
     588             : 
     589         274 :         if (ppoGrammar != nullptr)
     590         273 :             *ppoGrammar = poGrammar;
     591             : 
     592         274 :         break;
     593             :     }
     594             : 
     595         274 :     return true;
     596             : }
     597             : 
     598             : /************************************************************************/
     599             : /*                                  Init()                              */
     600             : /************************************************************************/
     601             : 
     602        1392 : bool GMLASReader::Init(const char *pszFilename,
     603             :                        const std::shared_ptr<VSIVirtualHandle> &fp,
     604             :                        const std::map<CPLString, CPLString> &oMapURIToPrefix,
     605             :                        std::vector<std::unique_ptr<OGRGMLASLayer>> &apoLayers,
     606             :                        bool bValidate,
     607             :                        const std::vector<PairURIFilename> &aoXSDs,
     608             :                        bool bSchemaFullChecking, bool bHandleMultipleImports)
     609             : {
     610        1392 :     m_oMapURIToPrefix = oMapURIToPrefix;
     611        1392 :     m_apoLayers = &apoLayers;
     612        1392 :     m_bValidate = bValidate;
     613             : 
     614        1392 :     m_poSAXReader.reset(XMLReaderFactory::createXMLReader());
     615             : 
     616             :     // Commonly useful configuration.
     617             :     //
     618        1392 :     m_poSAXReader->setFeature(XMLUni::fgSAX2CoreNameSpaces, true);
     619        1392 :     m_poSAXReader->setFeature(XMLUni::fgSAX2CoreNameSpacePrefixes, true);
     620             : 
     621        1392 :     m_poSAXReader->setContentHandler(this);
     622        1392 :     m_poSAXReader->setLexicalHandler(this);
     623        1392 :     m_poSAXReader->setDTDHandler(this);
     624        1392 :     m_poSAXReader->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution,
     625        1392 :                               true);
     626             : 
     627        1392 :     m_oErrorHandler.SetSchemaFullCheckingEnabled(bSchemaFullChecking);
     628        1392 :     m_oErrorHandler.SetHandleMultipleImportsEnabled(bHandleMultipleImports);
     629        1392 :     m_poSAXReader->setErrorHandler(&m_oErrorHandler);
     630             : 
     631        1392 :     m_poSAXReader->setFeature(XMLUni::fgXercesSchemaFullChecking,
     632        1392 :                               bSchemaFullChecking);
     633        1392 :     m_poSAXReader->setFeature(XMLUni::fgXercesHandleMultipleImports,
     634        1392 :                               bHandleMultipleImports);
     635             : 
     636        1392 :     if (bValidate)
     637             :     {
     638             :         // Enable validation.
     639          50 :         m_poSAXReader->setFeature(XMLUni::fgSAX2CoreValidation, true);
     640          50 :         m_poSAXReader->setFeature(XMLUni::fgXercesSchema, true);
     641             : 
     642             : #ifndef __COVERITY__
     643             :         // We want all errors to be reported
     644             :         // coverity[unsafe_xml_parse_config]
     645          50 :         m_poSAXReader->setFeature(XMLUni::fgXercesValidationErrorAsFatal,
     646          50 :                                   false);
     647             : #endif
     648             : 
     649          50 :         CPLString osBaseDirname(CPLGetDirnameSafe(pszFilename));
     650             : 
     651             :         // In the case the schemas are explicitly passed, we must do special
     652             :         // processing
     653          50 :         if (!aoXSDs.empty())
     654             :         {
     655           1 :             GMLASBaseEntityResolver oXSDEntityResolver(CPLString(), m_oCache);
     656           2 :             for (size_t i = 0; i < aoXSDs.size(); i++)
     657             :             {
     658           1 :                 const CPLString osXSDFilename(aoXSDs[i].second);
     659           1 :                 if (!LoadXSDInParser(
     660             :                         m_poSAXReader.get(), m_oCache, oXSDEntityResolver,
     661             :                         osBaseDirname, osXSDFilename, nullptr,
     662             :                         bSchemaFullChecking, bHandleMultipleImports))
     663             :                 {
     664           0 :                     return false;
     665             :                 }
     666             :             }
     667             : 
     668             :             // Make sure our previously loaded schemas are used
     669           1 :             m_poSAXReader->setFeature(XMLUni::fgXercesUseCachedGrammarInParse,
     670           1 :                                       true);
     671             : 
     672             :             // Don't load schemas from any other source (e.g., from XML
     673             :             // document's xsi:schemaLocation attributes).
     674             :             //
     675           1 :             m_poSAXReader->setFeature(XMLUni::fgXercesLoadSchema, false);
     676             :         }
     677             : 
     678             :         // Install entity resolver based on XML file
     679             :         m_poEntityResolver =
     680          50 :             std::make_unique<GMLASBaseEntityResolver>(osBaseDirname, m_oCache);
     681          50 :         m_poSAXReader->setEntityResolver(m_poEntityResolver.get());
     682             :     }
     683             :     else
     684             :     {
     685             :         // Don't load schemas from any other source (e.g., from XML document's
     686             :         // xsi:schemaLocation attributes).
     687             :         //
     688        1342 :         m_poSAXReader->setFeature(XMLUni::fgXercesLoadSchema, false);
     689        1342 :         m_poSAXReader->setEntityResolver(this);
     690             :     }
     691             : 
     692        1392 :     m_fp = fp;
     693        1392 :     m_GMLInputSource = std::make_unique<GMLASInputSource>(pszFilename, m_fp);
     694             : 
     695             :     // Establish a map from layer's XPath to layer to speed-up parsing
     696       65653 :     for (auto &poLayer : *m_apoLayers)
     697             :     {
     698             :         const CPLString *posLayerXPath =
     699       64261 :             &(poLayer->GetFeatureClass().GetXPath());
     700       64261 :         if (poLayer->GetFeatureClass().IsRepeatedSequence())
     701             :         {
     702        5487 :             size_t iPosExtra = posLayerXPath->find(szEXTRA_SUFFIX);
     703        5487 :             if (iPosExtra != std::string::npos)
     704             :             {
     705        3320 :                 m_osLayerXPath = *posLayerXPath;
     706        3320 :                 m_osLayerXPath.resize(iPosExtra);
     707        3320 :                 posLayerXPath = &m_osLayerXPath;
     708             :             }
     709             :         }
     710             : 
     711       64261 :         const bool bIsGroup = poLayer->GetFeatureClass().IsGroup();
     712       64261 :         if (!bIsGroup)
     713             :         {
     714       62024 :             if (m_oMapXPathToLayer.find(*posLayerXPath) ==
     715      124048 :                 m_oMapXPathToLayer.end())
     716       60905 :                 m_oMapXPathToLayer[*posLayerXPath] = poLayer.get();
     717             :         }
     718             :         else
     719             :         {
     720       28392 :             for (const auto &[xpath, idx] :
     721       30629 :                  poLayer->GetMapFieldXPathToOGRFieldIdx())
     722             :             {
     723       28392 :                 if (idx != -1 && m_oMapFieldXPathToGroupLayer.find(xpath) ==
     724       28392 :                                      m_oMapFieldXPathToGroupLayer.end())
     725       14196 :                     m_oMapFieldXPathToGroupLayer[xpath] = poLayer.get();
     726             :             }
     727             :         }
     728             : 
     729       64261 :         if (poLayer->GetFeatureClass().IsRepeatedSequence())
     730        5487 :             m_oMapXPathToLayerRepeadedSequence[*posLayerXPath].push_back(
     731        5487 :                 poLayer.get());
     732             :     }
     733             : 
     734        1392 :     return true;
     735             : }
     736             : 
     737             : /************************************************************************/
     738             : /*                             IsArrayType()                            */
     739             : /************************************************************************/
     740             : 
     741      221088 : static bool IsArrayType(OGRFieldType eType)
     742             : {
     743      206454 :     return eType == OFTIntegerList || eType == OFTInteger64List ||
     744      427542 :            eType == OFTRealList || eType == OFTStringList;
     745             : }
     746             : 
     747             : /************************************************************************/
     748             : /*                                SetField()                            */
     749             : /************************************************************************/
     750             : 
     751      119725 : void GMLASReader::SetField(OGRFeature *poFeature, OGRGMLASLayer *poLayer,
     752             :                            int nAttrIdx, const CPLString &osAttrValue)
     753             : {
     754      119725 :     const OGRFieldType eType(poFeature->GetFieldDefnRef(nAttrIdx)->GetType());
     755      119725 :     if (osAttrValue.empty())
     756             :     {
     757        4599 :         if (eType == OFTString &&
     758        1238 :             !poFeature->GetFieldDefnRef(nAttrIdx)->IsNullable())
     759             :         {
     760          36 :             poFeature->SetField(nAttrIdx, "");
     761             :         }
     762             :     }
     763      116364 :     else if (eType == OFTDate || eType == OFTDateTime)
     764             :     {
     765             :         OGRField sField;
     766       36016 :         if (OGRParseXMLDateTime((m_bInitialPass) ? "1970-01-01T00:00:00"
     767       17846 :                                                  : osAttrValue.c_str(),
     768       18170 :                                 &sField))
     769             :         {
     770       18170 :             poFeature->SetField(nAttrIdx, &sField);
     771       18170 :         }
     772             :     }
     773             :     // Transform boolean values to something that OGR understands
     774      117826 :     else if (eType == OFTInteger &&
     775       19632 :              poFeature->GetFieldDefnRef(nAttrIdx)->GetSubType() == OFSTBoolean)
     776             :     {
     777        3200 :         if (osAttrValue == "true")
     778        3179 :             poFeature->SetField(nAttrIdx, TRUE);
     779             :         else
     780          21 :             poFeature->SetField(nAttrIdx, FALSE);
     781             :     }
     782       94994 :     else if (eType == OFTBinary)
     783             :     {
     784             :         const int nFCFieldIdx =
     785        2142 :             poLayer->GetFCFieldIndexFromOGRFieldIdx(nAttrIdx);
     786        2142 :         if (nFCFieldIdx >= 0)
     787             :         {
     788             :             const GMLASField &oField(
     789        2142 :                 poLayer->GetFeatureClass().GetFields()[nFCFieldIdx]);
     790        2142 :             if (m_bInitialPass)
     791             :             {
     792          36 :                 GByte b = 'X';
     793          36 :                 poFeature->SetField(nAttrIdx, 1, &b);
     794             :             }
     795        2106 :             else if (oField.GetType() == GMLAS_FT_BASE64BINARY)
     796             :             {
     797             :                 GByte *pabyBuffer =
     798        1053 :                     reinterpret_cast<GByte *>(CPLStrdup(osAttrValue));
     799        1053 :                 int nBytes = CPLBase64DecodeInPlace(pabyBuffer);
     800        1053 :                 poFeature->SetField(nAttrIdx, nBytes, pabyBuffer);
     801        1053 :                 CPLFree(pabyBuffer);
     802             :             }
     803             :             else
     804             :             {
     805        1053 :                 int nBytes = 0;
     806        1053 :                 GByte *pabyBuffer = CPLHexToBinary(osAttrValue, &nBytes);
     807        1053 :                 poFeature->SetField(nAttrIdx, nBytes, pabyBuffer);
     808        1053 :                 CPLFree(pabyBuffer);
     809             :             }
     810             :         }
     811             :     }
     812       92852 :     else if (IsArrayType(eType))
     813             :     {
     814             :         const int nFCFieldIdx =
     815        6436 :             poLayer->GetFCFieldIndexFromOGRFieldIdx(nAttrIdx);
     816       12872 :         if (nFCFieldIdx >= 0 &&
     817        6436 :             poLayer->GetFeatureClass().GetFields()[nFCFieldIdx].IsList())
     818             :         {
     819             :             char **papszTokens =
     820        6420 :                 CSLTokenizeString2(osAttrValue.c_str(), " ", 0);
     821        9600 :             if (eType == OFTIntegerList &&
     822        3180 :                 poFeature->GetFieldDefnRef(nAttrIdx)->GetSubType() ==
     823             :                     OFSTBoolean)
     824             :             {
     825        3162 :                 for (char **papszIter = papszTokens; *papszIter != nullptr;
     826             :                      ++papszIter)
     827             :                 {
     828        2102 :                     if (strcmp(*papszIter, "true") == 0)
     829             :                     {
     830        1042 :                         (*papszIter)[0] = '1';
     831        1042 :                         (*papszIter)[1] = '\0';
     832             :                     }
     833        1060 :                     else if (strcmp(*papszIter, "false") == 0)
     834             :                     {
     835        1042 :                         (*papszIter)[0] = '0';
     836        1042 :                         (*papszIter)[1] = '\0';
     837             :                     }
     838             :                 }
     839             :             }
     840        6420 :             poFeature->SetField(nAttrIdx, papszTokens);
     841        6420 :             CSLDestroy(papszTokens);
     842             :         }
     843          16 :         else if (eType == OFTStringList)
     844             :         {
     845           4 :             OGRField *psRawField = poFeature->GetRawFieldRef(nAttrIdx);
     846           4 :             if (OGR_RawField_IsUnset(psRawField))
     847             :             {
     848           2 :                 poFeature->SetField(nAttrIdx, osAttrValue.c_str());
     849             :             }
     850             :             else
     851             :             {
     852           2 :                 ++psRawField->StringList.nCount;
     853           2 :                 psRawField->StringList.paList = CSLAddString(
     854             :                     psRawField->StringList.paList, osAttrValue.c_str());
     855             :             }
     856             :         }
     857          12 :         else if (eType == OFTIntegerList)
     858             :         {
     859           4 :             OGRField *psRawField = poFeature->GetRawFieldRef(nAttrIdx);
     860           4 :             if (OGR_RawField_IsUnset(psRawField))
     861             :             {
     862           2 :                 psRawField->IntegerList.nCount = 1;
     863           2 :                 psRawField->IntegerList.paList = static_cast<int *>(
     864           2 :                     CPLMalloc(psRawField->IntegerList.nCount * sizeof(int)));
     865             :             }
     866             :             else
     867             :             {
     868           2 :                 ++psRawField->IntegerList.nCount;
     869           2 :                 psRawField->IntegerList.paList = static_cast<int *>(
     870           2 :                     CPLRealloc(psRawField->IntegerList.paList,
     871           2 :                                psRawField->IntegerList.nCount * sizeof(int)));
     872             :             }
     873           4 :             psRawField->IntegerList.paList[psRawField->IntegerList.nCount - 1] =
     874           4 :                 atoi(osAttrValue.c_str());
     875             :         }
     876           8 :         else if (eType == OFTInteger64List)
     877             :         {
     878           4 :             OGRField *psRawField = poFeature->GetRawFieldRef(nAttrIdx);
     879           4 :             if (OGR_RawField_IsUnset(psRawField))
     880             :             {
     881           2 :                 psRawField->Integer64List.nCount = 1;
     882           2 :                 psRawField->Integer64List.paList =
     883           2 :                     static_cast<GIntBig *>(CPLMalloc(
     884           2 :                         psRawField->Integer64List.nCount * sizeof(GIntBig)));
     885             :             }
     886             :             else
     887             :             {
     888           2 :                 ++psRawField->Integer64List.nCount;
     889           2 :                 psRawField->Integer64List.paList =
     890           2 :                     static_cast<GIntBig *>(CPLRealloc(
     891           2 :                         psRawField->Integer64List.paList,
     892           2 :                         psRawField->Integer64List.nCount * sizeof(GIntBig)));
     893             :             }
     894             :             psRawField->Integer64List
     895           8 :                 .paList[psRawField->Integer64List.nCount - 1] =
     896           4 :                 CPLAtoGIntBig(osAttrValue.c_str());
     897             :         }
     898             :         else
     899             :         {
     900           4 :             CPLAssert(eType == OFTRealList);
     901           4 :             OGRField *psRawField = poFeature->GetRawFieldRef(nAttrIdx);
     902           4 :             if (OGR_RawField_IsUnset(psRawField))
     903             :             {
     904           2 :                 psRawField->RealList.nCount = 1;
     905           2 :                 psRawField->RealList.paList = static_cast<double *>(
     906           2 :                     CPLMalloc(psRawField->RealList.nCount * sizeof(double)));
     907             :             }
     908             :             else
     909             :             {
     910           2 :                 ++psRawField->RealList.nCount;
     911           2 :                 psRawField->RealList.paList = static_cast<double *>(
     912           2 :                     CPLRealloc(psRawField->RealList.paList,
     913           2 :                                psRawField->RealList.nCount * sizeof(double)));
     914             :             }
     915           8 :             psRawField->RealList.paList[psRawField->RealList.nCount - 1] =
     916           4 :                 CPLAtof(osAttrValue.c_str());
     917             :         }
     918             :     }
     919             :     else
     920             :     {
     921       86416 :         poFeature->SetField(nAttrIdx, osAttrValue.c_str());
     922             :     }
     923      119725 : }
     924             : 
     925             : /************************************************************************/
     926             : /*                          PushFeatureReady()                          */
     927             : /************************************************************************/
     928             : 
     929       56567 : void GMLASReader::PushFeatureReady(std::unique_ptr<OGRFeature> &&poFeature,
     930             :                                    OGRGMLASLayer *poLayer)
     931             : {
     932             : #ifdef DEBUG_VERBOSE
     933             :     CPLDebug("GMLAS", "PushFeatureReady(%p / %s / %s)", poFeature,
     934             :              poFeature->GetDefnRef()->GetName(), poLayer->GetName());
     935             : #endif
     936             : 
     937             :     m_aoFeaturesReady.emplace_back(
     938       56567 :         std::make_pair(std::move(poFeature), poLayer));
     939       56567 : }
     940             : 
     941             : /************************************************************************/
     942             : /*                          CreateNewFeature                            */
     943             : /************************************************************************/
     944             : 
     945       52425 : void GMLASReader::CreateNewFeature(const CPLString &osLocalname)
     946             : {
     947       52425 :     m_oCurCtxt.m_poFeature =
     948       52425 :         new OGRFeature(m_oCurCtxt.m_poLayer->GetLayerDefn());
     949             : #ifdef DEBUG_VERBOSE
     950             :     CPLDebug("GMLAS", "CreateNewFeature(element=%s / layer=%s) = %p",
     951             :              osLocalname.c_str(), m_oCurCtxt.m_poLayer->GetName(),
     952             :              m_oCurCtxt.m_poFeature);
     953             : #endif
     954             :     // Assign FID (1, ...). Only for OGR compliance, but definitely
     955             :     // not a unique ID among datasets with the same schema
     956       52425 :     ++m_oMapGlobalCounter[m_oCurCtxt.m_poLayer];
     957       52425 :     const int nGlobalCounter = m_oMapGlobalCounter[m_oCurCtxt.m_poLayer];
     958       52425 :     m_oCurCtxt.m_poFeature->SetFID(nGlobalCounter);
     959             : 
     960             :     // Find parent ID
     961       52425 :     CPLString osParentId;
     962      103585 :     if (!m_aoStackContext.empty() &&
     963       51160 :         m_oCurCtxt.m_poLayer->GetParentIDFieldIdx() >= 0)
     964             :     {
     965       39407 :         CPLAssert(m_aoStackContext.back().m_poLayer->GetIDFieldIdx() >= 0);
     966       39407 :         osParentId = m_aoStackContext.back().m_poFeature->GetFieldAsString(
     967       39407 :             m_aoStackContext.back().m_poLayer->GetIDFieldIdx());
     968       78814 :         m_oCurCtxt.m_poFeature->SetField(
     969       39407 :             m_oCurCtxt.m_poLayer->GetParentIDFieldIdx(), osParentId.c_str());
     970             :     }
     971             : 
     972             :     // Should we generate a unique (child) ID from the parent ID ?
     973       52425 :     if (m_oCurCtxt.m_poLayer->IsGeneratedIDField())
     974             :     {
     975             :         // Local IDs (ie related to a parent feature are fine, but when
     976             :         // we might have cycles, that doesn't work anymore
     977             :         /*
     978             :         ++m_oCurCtxt.m_oMapCounter[m_oCurCtxt.m_poLayer];
     979             :         const int nCounter =
     980             :             m_oCurCtxt.m_oMapCounter[m_oCurCtxt.m_poLayer];*/
     981       51337 :         const int nCounter = nGlobalCounter;
     982             : 
     983       51337 :         CPLString osGeneratedID = (osParentId.empty() ? m_osHash : osParentId) +
     984      154011 :                                   "_" + osLocalname +
     985      102674 :                                   CPLSPrintf("_%d", nCounter);
     986       51337 :         m_oCurCtxt.m_poFeature->SetField(m_oCurCtxt.m_poLayer->GetIDFieldIdx(),
     987             :                                          osGeneratedID.c_str());
     988             :     }
     989             : 
     990       52425 :     m_nCurFieldIdx = -1;
     991       52425 : }
     992             : 
     993             : /************************************************************************/
     994             : /*                         AttachAsLastChild()                          */
     995             : /************************************************************************/
     996             : 
     997             : /* Attach element as the last child of its parent */
     998        2027 : void GMLASReader::AttachAsLastChild(CPLXMLNode *psNode)
     999             : {
    1000        2027 :     NodeLastChild &sNodeLastChild = m_apsXMLNodeStack.back();
    1001        2027 :     CPLXMLNode *psLastChildParent = sNodeLastChild.psLastChild;
    1002             : 
    1003        2027 :     if (psLastChildParent == nullptr)
    1004             :     {
    1005         774 :         CPLAssert(sNodeLastChild.psNode);
    1006         774 :         sNodeLastChild.psNode->psChild = psNode;
    1007             :     }
    1008             :     else
    1009             :     {
    1010        1253 :         psLastChildParent->psNext = psNode;
    1011             :     }
    1012        2027 :     sNodeLastChild.psLastChild = psNode;
    1013        2027 : }
    1014             : 
    1015             : /************************************************************************/
    1016             : /*                         BuildXMLBlobStartElement()                   */
    1017             : /************************************************************************/
    1018             : 
    1019        9607 : void GMLASReader::BuildXMLBlobStartElement(const CPLString &osXPath,
    1020             :                                            const Attributes &attrs)
    1021             : {
    1022        9607 :     if (FillTextContent())
    1023             :     {
    1024        6357 :         m_osTextContent += "<";
    1025        6357 :         m_osTextContent += osXPath;
    1026             :     }
    1027             : 
    1028        9607 :     CPLXMLNode *psNode = nullptr;
    1029        9607 :     if (m_nCurGeomFieldIdx >= 0 || m_nSWEDataArrayLevel >= 0 ||
    1030        7909 :         m_nSWEDataRecordLevel >= 0)
    1031             :     {
    1032        1785 :         psNode = CPLCreateXMLNode(nullptr, CXT_Element, osXPath);
    1033        1785 :         if (!m_apsXMLNodeStack.empty())
    1034             :         {
    1035        1348 :             AttachAsLastChild(psNode);
    1036             :         }
    1037             :     }
    1038             : 
    1039        9607 :     CPLXMLNode *psLastChild = nullptr;
    1040       11993 :     for (unsigned int i = 0; i < attrs.getLength(); i++)
    1041             :     {
    1042             :         const CPLString &osAttrNSPrefix(
    1043             :             m_osAttrNSPrefix =
    1044        2386 :                 m_oMapURIToPrefix[transcode(attrs.getURI(i), m_osAttrNSUri)]);
    1045             :         const CPLString &osAttrLocalname(
    1046        2386 :             transcode(attrs.getLocalName(i), m_osAttrLocalName));
    1047             :         const CPLString &osAttrValue(
    1048        2386 :             transcode(attrs.getValue(i), m_osAttrValue));
    1049        2386 :         CPLString &osAttrXPath(m_osAttrXPath);
    1050        2386 :         if (!osAttrNSPrefix.empty())
    1051             :         {
    1052        1210 :             osAttrXPath.reserve(osAttrNSPrefix.size() + 1 +
    1053         605 :                                 osAttrLocalname.size());
    1054         605 :             osAttrXPath = osAttrNSPrefix;
    1055         605 :             osAttrXPath += ":";
    1056         605 :             osAttrXPath += osAttrLocalname;
    1057             :         }
    1058             :         else
    1059             :         {
    1060        1781 :             osAttrXPath = osAttrLocalname;
    1061             :         }
    1062             : 
    1063        2386 :         if (psNode != nullptr)
    1064             :         {
    1065             :             CPLXMLNode *psAttrNode =
    1066         879 :                 CPLCreateXMLNode(nullptr, CXT_Attribute, osAttrXPath);
    1067         879 :             CPLCreateXMLNode(psAttrNode, CXT_Text, osAttrValue);
    1068             : 
    1069         879 :             if (psLastChild == nullptr)
    1070             :             {
    1071         773 :                 psNode->psChild = psAttrNode;
    1072             :             }
    1073             :             else
    1074             :             {
    1075         106 :                 psLastChild->psNext = psAttrNode;
    1076             :             }
    1077         879 :             psLastChild = psAttrNode;
    1078             :         }
    1079             : 
    1080        2386 :         if (FillTextContent())
    1081             :         {
    1082        1054 :             m_osTextContent += " ";
    1083        1054 :             m_osTextContent += osAttrXPath;
    1084        1054 :             m_osTextContent += "=\"";
    1085        1054 :             char *pszEscaped = CPLEscapeString(
    1086        1054 :                 osAttrValue.c_str(), static_cast<int>(osAttrValue.size()),
    1087             :                 CPLES_XML);
    1088        1054 :             m_osTextContent += pszEscaped;
    1089        1054 :             CPLFree(pszEscaped);
    1090        1054 :             m_osTextContent += '"';
    1091             :         }
    1092             :     }
    1093        9607 :     if (FillTextContent())
    1094        6357 :         m_osTextContent += ">";
    1095             : 
    1096        9607 :     if (psNode != nullptr)
    1097             :     {
    1098             :         /* Push the element on the stack */
    1099        1785 :         NodeLastChild sNewNodeLastChild;
    1100        1785 :         sNewNodeLastChild.psNode = psNode;
    1101        1785 :         sNewNodeLastChild.psLastChild = psLastChild;
    1102        1785 :         m_apsXMLNodeStack.push_back(sNewNodeLastChild);
    1103             : #ifdef DEBUG_VERBOSE
    1104             :         CPLDebug("GMLAS", "m_apsXMLNodeStack.push_back()");
    1105             : #endif
    1106             :     }
    1107             : 
    1108        9607 :     if (m_osTextContent.size() > m_nMaxContentSize)
    1109             :     {
    1110           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
    1111             :                  "Too much data in a single element");
    1112           0 :         m_bParsingError = true;
    1113             :     }
    1114        9607 : }
    1115             : 
    1116             : /************************************************************************/
    1117             : /*                          GetLayerByXPath()                           */
    1118             : /************************************************************************/
    1119             : 
    1120       16349 : OGRGMLASLayer *GMLASReader::GetLayerByXPath(const CPLString &osXPath)
    1121             : {
    1122      331871 :     for (const auto &poLayer : *m_apoLayers)
    1123             :     {
    1124      331871 :         if (poLayer->GetFeatureClass().GetXPath() == osXPath)
    1125             :         {
    1126       16349 :             return poLayer.get();
    1127             :         }
    1128             :     }
    1129           0 :     return nullptr;
    1130             : }
    1131             : 
    1132             : /************************************************************************/
    1133             : /*                            PushContext()                             */
    1134             : /************************************************************************/
    1135             : 
    1136       60994 : void GMLASReader::PushContext(const Context &oContext)
    1137             : {
    1138       60994 :     m_aoStackContext.push_back(oContext);
    1139             : #ifdef DEBUG_VERBOSE
    1140             :     CPLDebug("GMLAS", "Pushing new context:");
    1141             :     oContext.Dump();
    1142             : #endif
    1143       60994 : }
    1144             : 
    1145             : /************************************************************************/
    1146             : /*                            PopContext()                              */
    1147             : /************************************************************************/
    1148             : 
    1149       60532 : void GMLASReader::PopContext()
    1150             : {
    1151             : #ifdef DEBUG_VERBOSE
    1152             :     if (!m_aoStackContext.empty())
    1153             :     {
    1154             :         CPLDebug("GMLAS", "Popping up context:");
    1155             :         m_aoStackContext.back().Dump();
    1156             :     }
    1157             : #endif
    1158       60532 :     m_aoStackContext.pop_back();
    1159             : #ifdef DEBUG_VERBOSE
    1160             :     if (!m_aoStackContext.empty())
    1161             :     {
    1162             :         CPLDebug("GMLAS", "New top of stack is:");
    1163             :         m_aoStackContext.back().Dump();
    1164             :     }
    1165             : #endif
    1166       60532 : }
    1167             : 
    1168             : /************************************************************************/
    1169             : /*                             startElement()                           */
    1170             : /************************************************************************/
    1171             : 
    1172             : /* <xs:group ref="somegroup" maxOccurs="unbounded"/> are particularly hard to
    1173             :    deal with since we cannot easily know when the corresponding subfeature
    1174             :    is exactly terminated.
    1175             : 
    1176             :    Let's consider:
    1177             : 
    1178             :         <xs:group name="somegroup">
    1179             :             <xs:choice>
    1180             :                 <xs:element name="first_elt_of_group" type="xs:string"/>
    1181             :                 <xs:element name="second_elt_of_group" type="xs:string"/>
    1182             :             </xs:choice>
    1183             :         </xs:group>
    1184             : 
    1185             :         <xs:group name="another_group">
    1186             :             <xs:choice>
    1187             :                 <xs:element name="first_elt_of_another_group" type="xs:string"/>
    1188             :             </xs:choice>
    1189             :         </xs:group>
    1190             : 
    1191             :    There are different cases :
    1192             :     *
    1193             :               <first_elt_of_group>...</first_elt_of_group>
    1194             :               <second_elt_of_group>...</first_elt_of_group>
    1195             :               <first_elt_of_group>  <!-- we are here at startElement() -->
    1196             :                 ...
    1197             :               </first_elt_of_group>
    1198             : 
    1199             :     *
    1200             :               <first_elt_of_group>...</first_elt_of_group>
    1201             :               <first_elt_of_group>  <!-- we are here at startElement() -->
    1202             :                 ...</first_elt_of_group>
    1203             : 
    1204             :     *
    1205             :               <first_elt_of_group>...</first_elt_of_group>
    1206             :               <first_elt_of_another_group>  <!-- we are here at startElement()
    1207             :    -->
    1208             :                 ...</first_elt_of_another_group>
    1209             : 
    1210             :     *
    1211             :               <first_elt_of_group>...</first_elt_of_group>
    1212             :               <some_other_elt>  <!-- we are here at startElement() -->
    1213             :                 ...</some_other_elt>
    1214             : 
    1215             :     *
    1216             :             <first_elt>...</first_elt>
    1217             :             <second_elt><sub>...</sub></second_elt>
    1218             :             <first_elt> <-- here -->
    1219             :                 ...</first_elt>
    1220             :     *
    1221             :                 <first_elt_of_group>...</first_elt_of_group>
    1222             :             </end_of_enclosing_element>   <!-- we are here at endElement() -->
    1223             : */
    1224      162844 : void GMLASReader::startElement(const XMLCh *const uri,
    1225             :                                const XMLCh *const localname,
    1226             :                                const XMLCh *const
    1227             : #ifdef DEBUG_VERBOSE
    1228             :                                    qname
    1229             : #endif
    1230             :                                ,
    1231             :                                const Attributes &attrs)
    1232             : {
    1233      162844 :     m_nEntityCounter = 0;
    1234             : 
    1235      162844 :     const CPLString &osLocalname(transcode(localname, m_osLocalname));
    1236      162844 :     const CPLString &osNSURI(transcode(uri, m_osNSUri));
    1237      162844 :     const CPLString &osNSPrefix(m_osNSPrefix = m_oMapURIToPrefix[osNSURI]);
    1238      162844 :     if (osNSPrefix.empty())
    1239        5653 :         m_osXPath = osLocalname;
    1240             :     else
    1241             :     {
    1242      157191 :         m_osXPath.reserve(osNSPrefix.size() + 1 + osLocalname.size());
    1243      157191 :         m_osXPath = osNSPrefix;
    1244      157191 :         m_osXPath += ":";
    1245      157191 :         m_osXPath += osLocalname;
    1246             :     }
    1247      162844 :     const CPLString &osXPath(m_osXPath);
    1248             : #ifdef DEBUG_VERBOSE
    1249             :     CPLDebug("GMLAS", "startElement(%s / %s)", transcode(qname).c_str(),
    1250             :              osXPath.c_str());
    1251             : #endif
    1252      162844 :     m_anStackXPathLength.push_back(osXPath.size());
    1253      162844 :     if (!m_osCurXPath.empty())
    1254      161605 :         m_osCurXPath += "/";
    1255      162844 :     m_osCurXPath += osXPath;
    1256             : 
    1257             : #if 0
    1258             :     CPLString osSubXPathBefore(m_osCurSubXPath);
    1259             : #endif
    1260      162844 :     if (!m_osCurSubXPath.empty())
    1261             :     {
    1262      161362 :         m_osCurSubXPath += "/";
    1263      161362 :         m_osCurSubXPath += osXPath;
    1264             :     }
    1265             : 
    1266      162844 :     if (m_bProcessSWEDataArray && m_nSWEDataArrayLevel < 0 &&
    1267         100 :         m_nSWEDataRecordLevel < 0 && m_nCurGeomFieldIdx < 0)
    1268             :     {
    1269          58 :         if (osNSURI == szSWE_URI &&
    1270          20 :             (osLocalname == "DataArray" || osLocalname == "DataStream"))
    1271             :         {
    1272          12 :             if (m_nCurFieldIdx >= 0)
    1273             :             {
    1274             :                 m_osSWEDataArrayParentField =
    1275          12 :                     m_oCurCtxt.m_poFeature->GetFieldDefnRef(m_nCurFieldIdx)
    1276          12 :                         ->GetNameRef();
    1277             :             }
    1278             :             else
    1279             :             {
    1280           0 :                 m_osSWEDataArrayParentField.clear();
    1281             :             }
    1282          12 :             m_nSWEDataArrayLevel = m_nLevel;
    1283             :         }
    1284             :     }
    1285             : 
    1286             :     // Deal with XML content
    1287      162844 :     if (m_bIsXMLBlob || m_nSWEDataArrayLevel >= 0 || m_nSWEDataRecordLevel >= 0)
    1288             :     {
    1289        7914 :         BuildXMLBlobStartElement(osXPath, attrs);
    1290             :     }
    1291             : 
    1292      162844 :     if (m_bIsXMLBlob)
    1293             :     {
    1294        7832 :         m_nLevel++;
    1295        7832 :         return;
    1296             :     }
    1297             : 
    1298      155012 :     if (m_nLevel == m_nMaxLevel)
    1299             :     {
    1300           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Too deeply nested XML content");
    1301           0 :         m_bParsingError = true;
    1302           0 :         return;
    1303             :     }
    1304             : 
    1305      155012 :     if (m_bInitialPass)
    1306             :     {
    1307             :         // Collect the gml:boundedBy/gml:Envelope@srsDimension attribute
    1308        6169 :         if (m_bInGMLBoundedByLevel1 && m_nLevel == 2 &&
    1309           1 :             m_osXPath == "gml:Envelope")
    1310             :         {
    1311           2 :             for (unsigned int i = 0; i < attrs.getLength(); i++)
    1312             :             {
    1313             :                 const CPLString &osAttrLocalname(
    1314           1 :                     transcode(attrs.getLocalName(i), m_osAttrLocalName));
    1315           1 :                 if (osAttrLocalname == "srsDimension")
    1316             :                 {
    1317             :                     const CPLString &osAttrValue(
    1318           1 :                         transcode(attrs.getValue(i), m_osAttrValue));
    1319           1 :                     m_nDefaultSrsDimension = atoi(osAttrValue.c_str());
    1320             :                 }
    1321             :             }
    1322             :         }
    1323        6168 :         m_bInGMLBoundedByLevel1 =
    1324        6168 :             (m_nLevel == 1 && m_osXPath == "gml:boundedBy");
    1325             :     }
    1326             : 
    1327      155012 :     CPLAssert(m_aoFeaturesReady.empty());
    1328             : 
    1329             :     // Look which layer might match the current XPath
    1330      155012 :     OGRGMLASLayer *poLayer = nullptr;
    1331      155012 :     bool bIsMatchingGroup = false;
    1332             : 
    1333             :     {
    1334             :         const auto oIter = m_oMapXPathToLayer.find(
    1335      155012 :             m_osCurSubXPath.empty()
    1336             :                 ?
    1337             :                 // Case where we haven't yet entered the top-level element, which
    1338             :                 // may be in container elements
    1339             : 
    1340             :                 osXPath
    1341             :                 :
    1342             : 
    1343             :                 // Case where we are a sub-element of a top-level feature
    1344      155012 :                 m_osCurSubXPath);
    1345             : 
    1346      155012 :         if (oIter != m_oMapXPathToLayer.end())
    1347             :         {
    1348       29347 :             poLayer = oIter->second;
    1349             :         }
    1350             :     }
    1351      155012 :     if (!poLayer)
    1352             :     {
    1353      125665 :         const auto oIter = m_oMapFieldXPathToGroupLayer.find(m_osCurSubXPath);
    1354             :         // Case where we are a sub-element of a (repeated) group of a
    1355             :         // top-level feature
    1356      125665 :         if (oIter != m_oMapFieldXPathToGroupLayer.end())
    1357             :         {
    1358       19416 :             poLayer = oIter->second;
    1359       19416 :             bIsMatchingGroup = true;
    1360             :         }
    1361             :     }
    1362      155012 :     if (!poLayer && m_oCurCtxt.m_poLayer != nullptr)
    1363             :     {
    1364             :         const auto oIter = m_oMapXPathToLayerRepeadedSequence.find(
    1365      106032 :             m_oCurCtxt.m_poLayer->GetFeatureClass().GetXPath());
    1366      106032 :         if (oIter != m_oMapXPathToLayerRepeadedSequence.end())
    1367             :         {
    1368             :             // Needed to handle sequence_1_unbounded_non_simplifiable.subelement
    1369             :             // case of data/gmlas_test1.xml
    1370      228242 :             for (auto *poLayerIter : oIter->second)
    1371             :             {
    1372             :                 const bool bIsMatchingRepeatedSequence =
    1373      300148 :                     m_oCurCtxt.m_poLayer != poLayerIter &&
    1374      148021 :                     poLayerIter->GetOGRFieldIndexFromXPath(m_osCurSubXPath) >=
    1375      152127 :                         0;
    1376      152127 :                 if (bIsMatchingRepeatedSequence)
    1377             :                 {
    1378        3954 :                     poLayer = poLayerIter;
    1379        3954 :                     break;
    1380             :                 }
    1381             :             }
    1382             :         }
    1383             :     }
    1384      257090 :     if (!poLayer && !m_osCurSubXPath.empty() &&
    1385      102078 :         m_oCurCtxt.m_poGroupLayer != nullptr)
    1386             :     {
    1387        5286 :         for (auto &poLayerIter : *m_apoLayers)
    1388             :         {
    1389             :             const int nTmpIdx =
    1390        5286 :                 poLayerIter->GetOGRFieldIndexFromXPath(m_osCurSubXPath);
    1391        5286 :             if (nTmpIdx >= 0 || nTmpIdx == IDX_COMPOUND_FOLDED)
    1392             :             {
    1393             :                 // Case where we go back from a sub-element of a (repeated) group
    1394             :                 // of a top-level feature to a regular sub-element of that top-level
    1395             :                 // feature
    1396        1762 :                 poLayer = poLayerIter.get();
    1397        1762 :                 break;
    1398             :             }
    1399             :         }
    1400             :     }
    1401             : 
    1402      155012 :     if (poLayer)
    1403             :     {
    1404             : #ifdef DEBUG_VERBOSE
    1405             :         CPLDebug("GMLAS", "Matches layer %s (%s)", poLayer->GetName(),
    1406             :                  poLayer->GetFeatureClass().GetXPath().c_str());
    1407             : #endif
    1408             : 
    1409      105931 :         if (poLayer->GetParent() != nullptr &&
    1410      105931 :             poLayer->GetParent()->GetFeatureClass().IsRepeatedSequence() &&
    1411        3517 :             m_oCurCtxt.m_poGroupLayer != poLayer->GetParent())
    1412             :         {
    1413             :             // Yuck! Simulate top-level element of a group if we directly
    1414             :             // jump into a nested class of it !
    1415             :             /* Something like
    1416             :                     <xs:group name="group">
    1417             :                         <xs:sequence>
    1418             :                             <xs:element name="optional_elt" type="xs:string"
    1419             :                    minOccurs="0"/> <xs:element name="elt"> <xs:complexType>
    1420             :                                     <xs:sequence>
    1421             :                                         <xs:element name="subelt"
    1422             :                    type="xs:dateTime" maxOccurs="unbounded"/>
    1423             :                                     </xs:sequence>
    1424             :                                 </xs:complexType>
    1425             :                             </xs:element>
    1426             :                         </xs:sequence>
    1427             :                     </xs:group>
    1428             : 
    1429             :                     <top_element>
    1430             :                         <elt><subelt>...</subelt></elt>
    1431             :                     </top_element>
    1432             :                 */
    1433           1 :             m_oCurCtxt.m_poLayer = poLayer->GetParent();
    1434           1 :             m_oCurCtxt.m_poGroupLayer = m_oCurCtxt.m_poLayer;
    1435           1 :             m_oCurCtxt.m_nLevel = m_nLevel;
    1436           1 :             m_oCurCtxt.m_nLastFieldIdxGroupLayer = -1;
    1437           1 :             CreateNewFeature(m_oCurCtxt.m_poLayer->GetName());
    1438             :         }
    1439             : 
    1440       54479 :         bool bPushNewState = true;
    1441       54479 :         if (bIsMatchingGroup)
    1442             :         {
    1443       19416 :             int nFieldIdx = poLayer->GetOGRFieldIndexFromXPath(m_osCurSubXPath);
    1444       19416 :             bool bPushNewFeature = false;
    1445       19416 :             if (m_oCurCtxt.m_poGroupLayer == nullptr)
    1446             :             {
    1447        1791 :                 m_oCurCtxt.m_poFeature = nullptr;
    1448             :             }
    1449       17625 :             else if (nFieldIdx < 0)
    1450             :             {
    1451        6993 :                 bPushNewState = false;
    1452             :             }
    1453       10632 :             else if (m_oCurCtxt.m_nGroupLayerLevel == m_nLevel &&
    1454        7107 :                      m_oCurCtxt.m_poGroupLayer != poLayer)
    1455             :             {
    1456             : #ifdef DEBUG_VERBOSE
    1457             :                 CPLDebug("GMLAS", "new feature: group case 1");
    1458             : #endif
    1459             :                 /* Case like:
    1460             :                             <first_elt_of_group>...</first_elt_of_group>
    1461             :                             <first_elt_of_another_group>  <!-- we are here at
    1462             :                        startElement() -->
    1463             :                                 ...</first_elt_of_group>
    1464             :                     */
    1465           1 :                 bPushNewFeature = true;
    1466             :             }
    1467       28368 :             else if (m_oCurCtxt.m_nGroupLayerLevel == m_nLevel &&
    1468        7106 :                      m_oCurCtxt.m_poGroupLayer == poLayer &&
    1469       19510 :                      nFieldIdx == m_oCurCtxt.m_nLastFieldIdxGroupLayer &&
    1470        1773 :                      !IsArrayType(
    1471        1773 :                          m_oCurCtxt.m_poFeature->GetFieldDefnRef(nFieldIdx)
    1472             :                              ->GetType()))
    1473             :             {
    1474             : #ifdef DEBUG_VERBOSE
    1475             :                 CPLDebug("GMLAS", "new feature: group case 2");
    1476             : #endif
    1477             :                 /* Case like:
    1478             :                         <first_elt>...</first_elt>
    1479             :                         <first_elt> <-- here -->
    1480             :                     */
    1481         873 :                 bPushNewFeature = true;
    1482             :             }
    1483        9758 :             else if (m_oCurCtxt.m_nGroupLayerLevel == m_nLevel &&
    1484        6233 :                      nFieldIdx < m_oCurCtxt.m_nLastFieldIdxGroupLayer)
    1485             :             {
    1486             : #ifdef DEBUG_VERBOSE
    1487             :                 CPLDebug("GMLAS", "new feature: group case nFieldIdx < "
    1488             :                                   "m_oCurCtxt.m_nLastFieldIdxGroupLayer");
    1489             : #endif
    1490             :                 /* Case like:
    1491             :                             <first_elt_of_group>...</first_elt_of_group>
    1492             :                             <second_elt_of_group>...</first_elt_of_group>
    1493             :                             <first_elt_of_group>  <!-- we are here at
    1494             :                        startElement() -->
    1495             :                                 ...
    1496             :                             </first_elt_of_group>
    1497             :                     */
    1498        2652 :                 bPushNewFeature = true;
    1499             :             }
    1500        7106 :             else if (m_oCurCtxt.m_nGroupLayerLevel == m_nLevel + 1 &&
    1501           0 :                      m_oCurCtxt.m_poGroupLayer == poLayer)
    1502             :             {
    1503             : #ifdef DEBUG_VERBOSE
    1504             :                 CPLDebug("GMLAS", "new feature: group case 3");
    1505             : #endif
    1506             :                 /* Case like:
    1507             :                         <first_elt>...</first_elt>
    1508             :                         <second_elt><sub>...</sub></second_elt>
    1509             :                         <first_elt> <-- here -->
    1510             :                             ...</first_elt>
    1511             :                     */
    1512           0 :                 bPushNewFeature = true;
    1513             :             }
    1514       19416 :             if (bPushNewFeature)
    1515             :             {
    1516        3526 :                 CPLAssert(m_oCurCtxt.m_poFeature);
    1517        3526 :                 CPLAssert(m_oCurCtxt.m_poGroupLayer);
    1518             :                 // CPLDebug("GMLAS", "Feature ready");
    1519        3526 :                 PushFeatureReady(
    1520        7052 :                     std::unique_ptr<OGRFeature>(m_oCurCtxt.m_poFeature),
    1521             :                     m_oCurCtxt.m_poGroupLayer);
    1522        3526 :                 m_oCurCtxt.m_poFeature = nullptr;
    1523        3526 :                 m_nCurFieldIdx = -1;
    1524             :             }
    1525       19416 :             m_oCurCtxt.m_poLayer = poLayer;
    1526       19416 :             m_oCurCtxt.m_poGroupLayer = poLayer;
    1527       19416 :             m_oCurCtxt.m_nGroupLayerLevel = m_nLevel;
    1528       19416 :             if (nFieldIdx >= 0)
    1529       12423 :                 m_oCurCtxt.m_nLastFieldIdxGroupLayer = nFieldIdx;
    1530             :         }
    1531             :         else
    1532             :         {
    1533       36826 :             if (m_oCurCtxt.m_nGroupLayerLevel == m_nLevel &&
    1534        1763 :                 poLayer == m_aoStackContext.back().m_poLayer)
    1535             :             {
    1536             :                 // This is the case where we switch from an element that was
    1537             :                 // in a group to a regular element of the same level
    1538             : 
    1539             :                 // Push group feature as ready
    1540        1762 :                 CPLAssert(m_oCurCtxt.m_poFeature);
    1541             : 
    1542             :                 // CPLDebug("GMLAS", "Feature ready");
    1543        1762 :                 PushFeatureReady(
    1544        3524 :                     std::unique_ptr<OGRFeature>(m_oCurCtxt.m_poFeature),
    1545             :                     m_oCurCtxt.m_poGroupLayer);
    1546             : 
    1547             :                 // Restore "top-level" context
    1548        1762 :                 CPLAssert(!m_aoStackContext.empty());
    1549        1762 :                 m_oCurCtxt = m_aoStackContext.back();
    1550        1762 :                 bPushNewState = false;
    1551             :             }
    1552             :             else
    1553             :             {
    1554       33301 :                 if (m_oCurCtxt.m_poGroupLayer)
    1555             :                 {
    1556        7034 :                     Context oContext;
    1557        3517 :                     oContext = m_oCurCtxt;
    1558        3517 :                     oContext.m_nLevel = -1;
    1559        3517 :                     oContext.Dump();
    1560        3517 :                     PushContext(oContext);
    1561             :                 }
    1562             : 
    1563       33301 :                 m_oCurCtxt.m_poFeature = nullptr;
    1564       33301 :                 m_oCurCtxt.m_poGroupLayer = nullptr;
    1565       33301 :                 m_oCurCtxt.m_nGroupLayerLevel = -1;
    1566       33301 :                 m_oCurCtxt.m_nLastFieldIdxGroupLayer = -1;
    1567       33301 :                 m_oCurCtxt.m_poLayer = poLayer;
    1568       33301 :                 if (m_aoStackContext.empty())
    1569        1265 :                     m_osCurSubXPath = osXPath;
    1570             :             }
    1571             :         }
    1572             : 
    1573       54479 :         if (m_oCurCtxt.m_poFeature == nullptr)
    1574             :         {
    1575       38618 :             CPLAssert(bPushNewState);
    1576       38618 :             CreateNewFeature(osLocalname);
    1577             :         }
    1578             : 
    1579       54479 :         if (bPushNewState)
    1580             :         {
    1581       91448 :             Context oContext;
    1582       45724 :             oContext = m_oCurCtxt;
    1583       45724 :             oContext.m_nLevel = m_nLevel;
    1584       45724 :             PushContext(oContext);
    1585       45724 :             m_oCurCtxt.m_oMapCounter.clear();
    1586             :         }
    1587             :     }
    1588             : 
    1589      155012 :     if (m_oCurCtxt.m_poLayer)
    1590             :     {
    1591             : #ifdef DEBUG_VERBOSE
    1592             :         CPLDebug("GMLAS", "Current layer: %s", m_oCurCtxt.m_poLayer->GetName());
    1593             : #endif
    1594             : 
    1595      154795 :         bool bHasProcessedAttributes = false;
    1596             : 
    1597             :         // Find if we can match this element with one of our fields
    1598             :         int idx =
    1599      154795 :             m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(m_osCurSubXPath);
    1600      309590 :         int geom_idx = m_oCurCtxt.m_poLayer->GetOGRGeomFieldIndexFromXPath(
    1601      154795 :             m_osCurSubXPath);
    1602             : 
    1603      154795 :         if (idx < 0 && idx != IDX_COMPOUND_FOLDED)
    1604             :         {
    1605             :             /* Special case for a layer that matches everything, as found */
    1606             :             /* in swe:extension */
    1607       20499 :             idx = m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(
    1608       40998 :                 m_oCurCtxt.m_poLayer->GetFeatureClass().GetXPath() +
    1609             :                 szMATCH_ALL);
    1610       22177 :             if (idx >= 0 &&
    1611        1678 :                 m_oCurCtxt.m_poLayer->GetFeatureClass().GetFields().size() > 1)
    1612             :             {
    1613             :                 // But only match this wildcard field if it is the only child
    1614             :                 // of the feature class, otherwise that is going to prevent
    1615             :                 // matching regular fields
    1616             :                 // Practical case  the <any processContents="lax" minOccurs="0"
    1617             :                 // maxOccurs="unbounded"> declaratin of
    1618             :                 // http://schemas.earthresourceml.org/earthresourceml-lite/1.0/erml-lite.xsd
    1619             :                 // http://services.ga.gov.au/earthresource/ows?service=wfs&version=2.0.0&request=GetFeature&typenames=erl:CommodityResourceView&count=10
    1620             :                 // FIXME: currently we will thus ignore those extra content
    1621             :                 // See ogr_gmlas_any_field_at_end_of_declaration test case
    1622           6 :                 idx = -1;
    1623             :             }
    1624             :         }
    1625      154795 :         if (idx < 0 && geom_idx < 0 && geom_idx != IDX_COMPOUND_FOLDED)
    1626             :         {
    1627             :             /* Special case for a layer that is a made of only a geometry */
    1628       34152 :             geom_idx = m_oCurCtxt.m_poLayer->GetOGRGeomFieldIndexFromXPath(
    1629       68304 :                 m_oCurCtxt.m_poLayer->GetFeatureClass().GetXPath() +
    1630             :                 szMATCH_ALL);
    1631             :         }
    1632             : 
    1633      154795 :         if (idx >= 0 || geom_idx >= 0)
    1634             :         {
    1635             :             // Sanity check. Shouldn't normally happen !
    1636      241290 :             if (m_oCurCtxt.m_poFeature == nullptr ||
    1637      120645 :                 m_oCurCtxt.m_poLayer->GetLayerDefn() !=
    1638      120645 :                     m_oCurCtxt.m_poFeature->GetDefnRef())
    1639             :             {
    1640           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1641             :                          "Inconsistent m_poLayer / m_poFeature state");
    1642           0 :                 m_bParsingError = true;
    1643           0 :                 return;
    1644             :             }
    1645             : 
    1646      120645 :             bool bPushNewFeature = false;
    1647             :             const int nFCFieldIdx =
    1648             :                 (idx >= 0)
    1649      120645 :                     ? m_oCurCtxt.m_poLayer->GetFCFieldIndexFromOGRFieldIdx(idx)
    1650         368 :                     : m_oCurCtxt.m_poLayer->GetFCFieldIndexFromOGRGeomFieldIdx(
    1651      120645 :                           geom_idx);
    1652             : 
    1653             :             /* For cases like
    1654             :                     <xs:element name="element_compound">
    1655             :                         <xs:complexType>
    1656             :                             <xs:sequence maxOccurs="unbounded">
    1657             :                                 <xs:element name="subelement1"
    1658             :                type="xs:string"/> <xs:element name="subelement2"
    1659             :                type="xs:string"/>
    1660             :                             </xs:sequence>
    1661             :                         </xs:complexType>
    1662             :                     </xs:element>
    1663             : 
    1664             :                     <element_compound>
    1665             :                         <subelement1>a</subelement>
    1666             :                         <subelement2>b</subelement>
    1667             :                         <subelement1>c</subelement>
    1668             :                         <subelement2>d</subelement>
    1669             :                     </element_compound>
    1670             :             */
    1671             : 
    1672      120645 :             if (idx >= 0 && idx < m_nCurFieldIdx)
    1673             :             {
    1674             : #ifdef DEBUG_VERBOSE
    1675             :                 CPLDebug("GMLAS", "new feature: idx < m_nCurFieldIdx");
    1676             : #endif
    1677           2 :                 bPushNewFeature = true;
    1678             :             }
    1679             : 
    1680             :             /* For cases like
    1681             :                     <xs:element name="element_compound">
    1682             :                         <xs:complexType>
    1683             :                             <xs:sequence maxOccurs="unbounded">
    1684             :                                 <xs:element name="subelement"
    1685             :                type="xs:dateTime"/>
    1686             :                             </xs:sequence>
    1687             :                         </xs:complexType>
    1688             :                     </xs:element>
    1689             : 
    1690             :                     <element_compound>
    1691             :                         <subelement>2012-01-01T12:34:56Z</subelement>
    1692             :                         <subelement>2012-01-02T12:34:56Z</subelement>
    1693             :                     </element_compound>
    1694             :             */
    1695      120275 :             else if (idx >= 0 && idx == m_nCurFieldIdx &&
    1696       13385 :                      !IsArrayType(
    1697       13385 :                          m_oCurCtxt.m_poFeature->GetFieldDefnRef(m_nCurFieldIdx)
    1698      240918 :                              ->GetType()) &&
    1699             :                      // Make sure this isn't a repeated geometry as well
    1700           0 :                      !(geom_idx >= 0 && nFCFieldIdx >= 0 &&
    1701           0 :                        m_oCurCtxt.m_poLayer->GetFeatureClass()
    1702           0 :                                .GetFields()[nFCFieldIdx]
    1703           0 :                                .GetMaxOccurs() > 1))
    1704             :             {
    1705        2055 :                 bPushNewFeature = true;
    1706             :             }
    1707             : 
    1708             :             // Make sure we are in a repeated sequence, otherwise this is
    1709             :             // invalid XML
    1710      122702 :             if (bPushNewFeature &&
    1711      120649 :                 !m_oCurCtxt.m_poLayer->GetFeatureClass().IsRepeatedSequence() &&
    1712             :                 // Case of element within xs:choice
    1713           4 :                 !(idx >= 0 && nFCFieldIdx >= 0 &&
    1714           4 :                   m_oCurCtxt.m_poLayer->GetFeatureClass()
    1715           4 :                       .GetFields()[nFCFieldIdx]
    1716           4 :                       .MayAppearOutOfOrder()))
    1717             :             {
    1718           4 :                 bPushNewFeature = false;
    1719           4 :                 CPLError(CE_Warning, CPLE_AppDefined, "Unexpected element %s",
    1720             :                          m_osCurSubXPath.c_str());
    1721             :             }
    1722             : 
    1723      120645 :             if (bPushNewFeature)
    1724             :             {
    1725             :                 // CPLDebug("GMLAS", "Feature ready");
    1726        2053 :                 PushFeatureReady(
    1727        4106 :                     std::unique_ptr<OGRFeature>(m_oCurCtxt.m_poFeature),
    1728             :                     m_oCurCtxt.m_poLayer);
    1729        4106 :                 Context oContext = m_aoStackContext.back();
    1730        2053 :                 m_aoStackContext.pop_back();
    1731        2053 :                 CreateNewFeature(osLocalname);
    1732        2053 :                 oContext.m_poFeature = m_oCurCtxt.m_poFeature;
    1733        2053 :                 m_aoStackContext.push_back(std::move(oContext));
    1734        2053 :                 m_oCurCtxt.m_oMapCounter.clear();
    1735             :             }
    1736             : 
    1737      120645 :             if (m_nCurFieldIdx != idx)
    1738             :             {
    1739      108945 :                 m_osTextContentList.Clear();
    1740      108945 :                 m_nTextContentListEstimatedSize = 0;
    1741             :             }
    1742      120645 :             m_nCurFieldIdx = idx;
    1743      120645 :             m_nCurGeomFieldIdx = geom_idx;
    1744      120645 :             m_nCurFieldLevel = m_nLevel + 1;
    1745      120645 :             m_osTextContent.clear();
    1746      120645 :             m_bIsXMLBlob = false;
    1747      120645 :             m_bIsXMLBlobIncludeUpper = false;
    1748             : 
    1749             : #ifdef DEBUG_VERBOSE
    1750             :             if (idx >= 0)
    1751             :             {
    1752             :                 CPLDebug("GMLAS", "Matches field %s",
    1753             :                          m_oCurCtxt.m_poLayer->GetLayerDefn()
    1754             :                              ->GetFieldDefn(idx)
    1755             :                              ->GetNameRef());
    1756             :             }
    1757             :             if (geom_idx >= 0)
    1758             :             {
    1759             :                 CPLDebug("GMLAS", "Matches geometry field %s",
    1760             :                          m_oCurCtxt.m_poLayer->GetLayerDefn()
    1761             :                              ->GetGeomFieldDefn(geom_idx)
    1762             :                              ->GetNameRef());
    1763             :             }
    1764             : #endif
    1765      120645 :             if (nFCFieldIdx >= 0)
    1766             :             {
    1767      120645 :                 const GMLASField &oField(m_oCurCtxt.m_poLayer->GetFeatureClass()
    1768      120645 :                                              .GetFields()[nFCFieldIdx]);
    1769      120645 :                 if (m_nSWEDataArrayLevel < 0 && m_nSWEDataRecordLevel < 0)
    1770             :                 {
    1771      235840 :                     m_bIsXMLBlob = (oField.GetType() == GMLAS_FT_ANYTYPE ||
    1772      115248 :                                     m_nCurGeomFieldIdx != -1);
    1773             :                 }
    1774      120645 :                 m_bIsXMLBlobIncludeUpper =
    1775      120645 :                     m_bIsXMLBlob && oField.GetIncludeThisEltInBlob();
    1776      120645 :                 if (m_bIsXMLBlobIncludeUpper)
    1777             :                 {
    1778        1688 :                     BuildXMLBlobStartElement(osXPath, attrs);
    1779        1688 :                     m_nLevel++;
    1780        1688 :                     return;
    1781             :                 }
    1782             : 
    1783             :                 // Figure out if it is an element that calls for a related
    1784             :                 // top-level feature (but without junction table)
    1785      118957 :                 if (oField.GetCategory() ==
    1786             :                     GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK)
    1787             :                 {
    1788             :                     const CPLString &osNestedXPath(
    1789        7157 :                         oField.GetRelatedClassXPath());
    1790        7157 :                     CPLAssert(!osNestedXPath.empty());
    1791        7157 :                     OGRGMLASLayer *poSubLayer = GetLayerByXPath(osNestedXPath);
    1792        7157 :                     if (poSubLayer && m_nCurFieldIdx >= 0)
    1793             :                     {
    1794        7157 :                         int nOldCurFieldIdx = m_nCurFieldIdx;
    1795        7157 :                         OGRFeature *poOldCurFeature = m_oCurCtxt.m_poFeature;
    1796        7157 :                         OGRGMLASLayer *poOldLayer = m_oCurCtxt.m_poLayer;
    1797        7157 :                         m_oCurCtxt.m_poLayer = poSubLayer;
    1798        7157 :                         CreateNewFeature(osLocalname);
    1799             : 
    1800        7157 :                         m_oCurCtxt.m_poGroupLayer = nullptr;
    1801        7157 :                         m_oCurCtxt.m_nGroupLayerLevel = -1;
    1802        7157 :                         m_oCurCtxt.m_nLastFieldIdxGroupLayer = -1;
    1803             : 
    1804             :                         // Install new context
    1805       14314 :                         Context oContext;
    1806        7157 :                         oContext = m_oCurCtxt;
    1807        7157 :                         oContext.m_nLevel = m_nLevel;
    1808        7157 :                         oContext.m_osCurSubXPath = m_osCurSubXPath;
    1809        7157 :                         m_osCurSubXPath = osNestedXPath;
    1810             : #ifdef DEBUG_VERBOSE
    1811             :                         CPLDebug("GMLAS",
    1812             :                                  "Installing new m_osCurSubXPath from %s to %s",
    1813             :                                  oContext.m_osCurSubXPath.c_str(),
    1814             :                                  m_osCurSubXPath.c_str());
    1815             : #endif
    1816        7157 :                         PushContext(oContext);
    1817        7157 :                         m_oCurCtxt.m_oMapCounter.clear();
    1818             : 
    1819             :                         // Process attributes now because we might need to
    1820             :                         // fetch the child id from them
    1821        7157 :                         ProcessAttributes(attrs);
    1822        7157 :                         bHasProcessedAttributes = true;
    1823             : 
    1824             :                         CPLString osChildId(
    1825        7157 :                             m_oCurCtxt.m_poFeature->GetFieldAsString(
    1826       14314 :                                 m_oCurCtxt.m_poLayer->GetIDFieldIdx()));
    1827        7157 :                         SetField(poOldCurFeature, poOldLayer, nOldCurFieldIdx,
    1828             :                                  osChildId);
    1829             : 
    1830          29 :                         if (m_bProcessSWEDataRecord && !m_bIsXMLBlob &&
    1831          29 :                             m_nSWEDataArrayLevel < 0 &&
    1832        7191 :                             m_nSWEDataRecordLevel < 0 &&
    1833           5 :                             osNestedXPath == "swe:DataRecord")
    1834             :                         {
    1835           5 :                             m_nSWEDataRecordLevel = m_nLevel;
    1836           5 :                             BuildXMLBlobStartElement(osXPath, attrs);
    1837             :                         }
    1838             :                     }
    1839             :                 }
    1840      118957 :             }
    1841             :         }
    1842             : 
    1843             : #if 0
    1844             :         // Case where we have an abstract type and don't know its realizations
    1845             :         else if ( idx != IDX_COMPOUND_FOLDED &&
    1846             :             (idx = m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(
    1847             :                                     osSubXPathBefore + "/" + "*")) >= 0 &&
    1848             :             m_oCurCtxt.m_poGroupLayer == NULL )
    1849             :         {
    1850             :             m_nCurFieldIdx = idx;
    1851             :             m_nCurFieldLevel = m_nLevel + 1;
    1852             :             m_osTextContent.clear();
    1853             :             m_bIsXMLBlob = true;
    1854             :             m_bIsXMLBlobIncludeUpper = true;
    1855             :             BuildXMLBlobStartElement(osNSPrefix, osLocalname, attrs);
    1856             :             m_nLevel ++;
    1857             :             return;
    1858             :         }
    1859             : #endif
    1860             : 
    1861       34150 :         else if (m_nLevel > m_aoStackContext.back().m_nLevel)
    1862             :         {
    1863             :             // Figure out if it is an element that calls from a related
    1864             :             // top-level feature with a junction table
    1865             :             const std::vector<GMLASField> &aoFields =
    1866       20610 :                 m_oCurCtxt.m_poLayer->GetFeatureClass().GetFields();
    1867     1496660 :             for (size_t i = 0; i < aoFields.size(); ++i)
    1868             :             {
    1869     1480650 :                 if (aoFields[i].GetCategory() ==
    1870     1531730 :                         GMLASField::PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE &&
    1871       51085 :                     aoFields[i].GetXPath() == m_osCurSubXPath)
    1872             :                 {
    1873             :                     const CPLString &osAbstractElementXPath(
    1874        4596 :                         aoFields[i].GetAbstractElementXPath());
    1875             :                     const CPLString &osNestedXPath(
    1876        4596 :                         aoFields[i].GetRelatedClassXPath());
    1877        4596 :                     CPLAssert(!osAbstractElementXPath.empty());
    1878        4596 :                     CPLAssert(!osNestedXPath.empty());
    1879             : 
    1880        4596 :                     OGRGMLASLayer *poJunctionLayer = GetLayerByXPath(
    1881        9192 :                         GMLASSchemaAnalyzer::BuildJunctionTableXPath(
    1882             :                             osAbstractElementXPath, osNestedXPath));
    1883        4596 :                     OGRGMLASLayer *poSubLayer = GetLayerByXPath(osNestedXPath);
    1884             : 
    1885        4596 :                     if (poSubLayer && poJunctionLayer)
    1886             :                     {
    1887             :                         CPLString osParentId(
    1888        4596 :                             m_oCurCtxt.m_poFeature->GetFieldAsString(
    1889        9192 :                                 m_oCurCtxt.m_poLayer->GetIDFieldIdx()));
    1890             : 
    1891             :                         // Create child feature
    1892        4596 :                         m_oCurCtxt.m_poLayer = poSubLayer;
    1893        4596 :                         CreateNewFeature(osLocalname);
    1894             : 
    1895        4596 :                         ++m_oMapGlobalCounter[poJunctionLayer];
    1896             :                         const int nGlobalCounter =
    1897        4596 :                             m_oMapGlobalCounter[poJunctionLayer];
    1898             : 
    1899        4596 :                         ++m_oCurCtxt.m_oMapCounter[poJunctionLayer];
    1900             :                         const int nCounter =
    1901        4596 :                             m_oCurCtxt.m_oMapCounter[poJunctionLayer];
    1902             : 
    1903        4596 :                         m_oCurCtxt.m_poGroupLayer = nullptr;
    1904        4596 :                         m_oCurCtxt.m_nGroupLayerLevel = -1;
    1905        4596 :                         m_oCurCtxt.m_nLastFieldIdxGroupLayer = -1;
    1906             : 
    1907             :                         // Install new context
    1908        9192 :                         Context oContext;
    1909        4596 :                         oContext = m_oCurCtxt;
    1910        4596 :                         oContext.m_nLevel = m_nLevel;
    1911        4596 :                         oContext.m_osCurSubXPath = m_osCurSubXPath;
    1912        4596 :                         m_osCurSubXPath = osNestedXPath;
    1913             : #ifdef DEBUG_VERBOSE
    1914             :                         CPLDebug("GMLAS",
    1915             :                                  "Installing new m_osCurSubXPath from %s to %s",
    1916             :                                  oContext.m_osCurSubXPath.c_str(),
    1917             :                                  m_osCurSubXPath.c_str());
    1918             : #endif
    1919        4596 :                         PushContext(oContext);
    1920        4596 :                         m_oCurCtxt.m_oMapCounter.clear();
    1921             : 
    1922             :                         // Process attributes now because we might need to
    1923             :                         // fetch the child id from them
    1924        4596 :                         ProcessAttributes(attrs);
    1925        4596 :                         bHasProcessedAttributes = true;
    1926             : 
    1927             :                         CPLString osChildId(
    1928        4596 :                             m_oCurCtxt.m_poFeature->GetFieldAsString(
    1929        9192 :                                 m_oCurCtxt.m_poLayer->GetIDFieldIdx()));
    1930             : 
    1931             :                         // Create junction feature
    1932             :                         auto poJunctionFeature = std::make_unique<OGRFeature>(
    1933        9192 :                             poJunctionLayer->GetLayerDefn());
    1934        4596 :                         poJunctionFeature->SetFID(nGlobalCounter);
    1935        4596 :                         poJunctionFeature->SetField(szOCCURRENCE, nCounter);
    1936        4596 :                         poJunctionFeature->SetField(szPARENT_PKID, osParentId);
    1937        4596 :                         poJunctionFeature->SetField(szCHILD_PKID, osChildId);
    1938        4596 :                         PushFeatureReady(std::move(poJunctionFeature),
    1939             :                                          poJunctionLayer);
    1940             :                     }
    1941        4596 :                     idx = IDX_COMPOUND_FOLDED;
    1942             : 
    1943        4596 :                     break;
    1944             :                 }
    1945             :             }
    1946             : 
    1947       20610 :             m_nCurFieldIdx = -1;
    1948       20610 :             m_nCurGeomFieldIdx = -1;
    1949       20933 :             if (idx != IDX_COMPOUND_FOLDED && m_nLevelSilentIgnoredXPath < 0 &&
    1950             : 
    1951             :                 // Detect if we are in a situation where elements like
    1952             :                 // <foo xsi:nil="true"/> have no corresponding OGR field
    1953             :                 // because of the use of remove_unused_fields=true
    1954         323 :                 !(m_oCurCtxt.m_poLayer->GetFCFieldIndexFromXPath(
    1955         323 :                       m_osCurSubXPath) >= 0 &&
    1956          78 :                   attrs.getLength() == 1 &&
    1957       20610 :                   m_oMapURIToPrefix[transcode(attrs.getURI(0))] ==
    1958             :                       szXSI_PREFIX &&
    1959       20610 :                   transcode(attrs.getLocalName(0)) == szNIL))
    1960             :             {
    1961         646 :                 CPLString osMatchedXPath;
    1962         323 :                 if (m_oIgnoredXPathMatcher.MatchesRefXPath(m_osCurSubXPath,
    1963             :                                                            osMatchedXPath))
    1964             :                 {
    1965           0 :                     if (m_oMapIgnoredXPathToWarn[osMatchedXPath])
    1966             :                     {
    1967           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    1968             :                                  "Element with xpath=%s found in document but "
    1969             :                                  "ignored according to configuration",
    1970             :                                  m_osCurSubXPath.c_str());
    1971             :                     }
    1972             :                     else
    1973             :                     {
    1974           0 :                         CPLDebug("GMLAS",
    1975             :                                  "Element with xpath=%s found in document but "
    1976             :                                  "ignored according to configuration",
    1977             :                                  m_osCurSubXPath.c_str());
    1978             :                     }
    1979           0 :                     m_nLevelSilentIgnoredXPath = m_nLevel;
    1980             :                 }
    1981             :                 else
    1982             :                 {
    1983         323 :                     if (m_bWarnUnexpected)
    1984             :                     {
    1985           9 :                         CPLError(CE_Warning, CPLE_AppDefined,
    1986             :                                  "Unexpected element with xpath=%s "
    1987             :                                  "(subxpath=%s) found",
    1988             :                                  m_osCurXPath.c_str(), m_osCurSubXPath.c_str());
    1989             :                     }
    1990             :                     else
    1991             :                     {
    1992         314 :                         CPLDebug("GMLAS",
    1993             :                                  "Unexpected element with xpath=%s "
    1994             :                                  "(subxpath=%s) found",
    1995             :                                  m_osCurXPath.c_str(), m_osCurSubXPath.c_str());
    1996             :                     }
    1997             :                 }
    1998             :             }
    1999             :         }
    2000             :         else
    2001             :         {
    2002       13540 :             m_nCurFieldIdx = -1;
    2003       13540 :             m_nCurGeomFieldIdx = -1;
    2004             :         }
    2005             : 
    2006      153107 :         if (!bHasProcessedAttributes && m_nLevelSilentIgnoredXPath < 0)
    2007      141354 :             ProcessAttributes(attrs);
    2008             :     }
    2009             :     else
    2010             :     {
    2011         217 :         m_nCurFieldIdx = -1;
    2012         217 :         m_nCurGeomFieldIdx = -1;
    2013             :     }
    2014             : 
    2015      153324 :     m_nLevel++;
    2016             : }
    2017             : 
    2018             : /************************************************************************/
    2019             : /*                          ProcessAttributes()                         */
    2020             : /************************************************************************/
    2021             : 
    2022      153107 : void GMLASReader::ProcessAttributes(const Attributes &attrs)
    2023             : {
    2024             :     // Browse through attributes and match them with one of our fields
    2025      153107 :     m_osAttrXPath = m_osCurSubXPath;
    2026      153107 :     m_osAttrXPath += '/';
    2027      153107 :     m_osAttrXPath += szAT_ANY_ATTR;
    2028             :     const int nWildcardAttrIdx =
    2029      153107 :         m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(m_osAttrXPath);
    2030      153107 :     json_object *poWildcard = nullptr;
    2031             : 
    2032      179780 :     for (unsigned int i = 0; i < attrs.getLength(); i++)
    2033             :     {
    2034             :         const CPLString &osAttrNSPrefix(
    2035             :             m_osAttrNSPrefix =
    2036       26673 :                 m_oMapURIToPrefix[transcode(attrs.getURI(i), m_osAttrNSUri)]);
    2037             :         const CPLString &osAttrLocalname(
    2038       26673 :             transcode(attrs.getLocalName(i), m_osAttrLocalName));
    2039             :         const CPLString &osAttrValue(
    2040       26673 :             transcode(attrs.getValue(i), m_osAttrValue));
    2041       26673 :         CPLString &osAttrXPath(m_osAttrXPath);
    2042       26673 :         if (!osAttrNSPrefix.empty())
    2043             :         {
    2044        8939 :             osAttrXPath.reserve(m_osCurSubXPath.size() + 2 +
    2045       17878 :                                 osAttrNSPrefix.size() + 1 +
    2046        8939 :                                 osAttrLocalname.size());
    2047        8939 :             osAttrXPath = m_osCurSubXPath;
    2048        8939 :             osAttrXPath += "/@";
    2049        8939 :             osAttrXPath += osAttrNSPrefix;
    2050        8939 :             osAttrXPath += ":";
    2051        8939 :             osAttrXPath += osAttrLocalname;
    2052             :         }
    2053             :         else
    2054             :         {
    2055       35468 :             osAttrXPath.reserve(m_osCurSubXPath.size() + 2 +
    2056       17734 :                                 osAttrLocalname.size());
    2057       17734 :             osAttrXPath = m_osCurSubXPath;
    2058       17734 :             osAttrXPath += "/@";
    2059       17734 :             osAttrXPath += osAttrLocalname;
    2060             :         }
    2061             : 
    2062             :         // CPLDebug("GMLAS", "Attr %s=%s", osAttrXPath.c_str(),
    2063             :         // osAttrValue.c_str());
    2064             : 
    2065             :         const int nAttrIdx =
    2066       26673 :             m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(osAttrXPath);
    2067             :         int nFCIdx;
    2068       26673 :         if (nAttrIdx >= 0)
    2069             :         {
    2070             :             const OGRFieldType eType(
    2071       19162 :                 m_oCurCtxt.m_poFeature->GetFieldDefnRef(nAttrIdx)->GetType());
    2072       19162 :             if (osAttrValue.empty() && eType == OFTString)
    2073             :             {
    2074           0 :                 m_oCurCtxt.m_poFeature->SetField(nAttrIdx, "");
    2075             :             }
    2076             :             else
    2077             :             {
    2078       19162 :                 SetField(m_oCurCtxt.m_poFeature, m_oCurCtxt.m_poLayer, nAttrIdx,
    2079             :                          osAttrValue);
    2080             :             }
    2081             : 
    2082       19217 :             if (osAttrNSPrefix == szXLINK_PREFIX && osAttrLocalname == szHREF &&
    2083          55 :                 !osAttrValue.empty())
    2084             :             {
    2085          55 :                 ProcessXLinkHref(nAttrIdx, osAttrXPath, osAttrValue);
    2086             :             }
    2087             : 
    2088       37799 :             if (m_oXLinkResolver.GetConf().m_bResolveInternalXLinks &&
    2089       18637 :                 m_bInitialPass)
    2090             :             {
    2091             :                 nFCIdx =
    2092         420 :                     m_oCurCtxt.m_poLayer->GetFCFieldIndexFromXPath(osAttrXPath);
    2093         840 :                 if (nFCIdx >= 0 && m_oCurCtxt.m_poLayer->GetFeatureClass()
    2094         420 :                                            .GetFields()[nFCIdx]
    2095         420 :                                            .GetType() == GMLAS_FT_ID)
    2096             :                 {
    2097             :                     // We don't check that there's no existing id in the map
    2098             :                     // This is normally forbidden by the xs:ID rules
    2099             :                     // If not respected by the document, this should not lead to
    2100             :                     // crashes
    2101          42 :                     m_oMapElementIdToLayer[osAttrValue] = m_oCurCtxt.m_poLayer;
    2102             : 
    2103          42 :                     if (m_oCurCtxt.m_poLayer->IsGeneratedIDField())
    2104             :                     {
    2105          19 :                         m_oMapElementIdToPKID[osAttrValue] =
    2106          38 :                             CPLString(m_oCurCtxt.m_poFeature->GetFieldAsString(
    2107          38 :                                 m_oCurCtxt.m_poLayer->GetIDFieldIdx()));
    2108             :                     }
    2109             :                 }
    2110             :             }
    2111             :         }
    2112             : 
    2113        7511 :         else if (osAttrNSPrefix == szXSI_PREFIX && osAttrLocalname == szNIL)
    2114             :         {
    2115        1161 :             if (osAttrValue == "true")
    2116             :             {
    2117             :                 const int nMainAttrIdx =
    2118        2322 :                     m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(
    2119        1161 :                         m_osCurSubXPath);
    2120        1161 :                 if (nMainAttrIdx >= 0)
    2121             :                 {
    2122        1140 :                     m_oCurCtxt.m_poFeature->SetFieldNull(nMainAttrIdx);
    2123             :                 }
    2124             :                 else
    2125             :                 {
    2126             :                     const int nHrefAttrIdx =
    2127          21 :                         m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(
    2128          42 :                             m_osCurSubXPath + "/@" + szXLINK_PREFIX + ":" +
    2129             :                             szHREF);
    2130          21 :                     if (nHrefAttrIdx >= 0)
    2131             :                     {
    2132           0 :                         m_oCurCtxt.m_poFeature->SetFieldNull(nHrefAttrIdx);
    2133             :                     }
    2134             :                 }
    2135             :             }
    2136             :         }
    2137             : 
    2138        6350 :         else if (osAttrNSPrefix != szXMLNS_PREFIX &&
    2139        3003 :                  osAttrLocalname != szXMLNS_PREFIX &&
    2140        2953 :                  !(osAttrNSPrefix == szXSI_PREFIX &&
    2141        1129 :                    osAttrLocalname == szSCHEMA_LOCATION) &&
    2142        1840 :                  !(osAttrNSPrefix == szXSI_PREFIX &&
    2143        9353 :                    osAttrLocalname == szNO_NAMESPACE_SCHEMA_LOCATION) &&
    2144             :                  // Do not warn about fixed attributes on geometry properties
    2145        1824 :                  !(m_nCurGeomFieldIdx >= 0 &&
    2146           0 :                    ((osAttrNSPrefix == szXLINK_PREFIX &&
    2147           0 :                      osAttrLocalname == szTYPE) ||
    2148           0 :                     (osAttrNSPrefix == "" && osAttrLocalname == szOWNS))))
    2149             :         {
    2150        3648 :             CPLString osMatchedXPath;
    2151        1824 :             if (nWildcardAttrIdx >= 0)
    2152             :             {
    2153        1812 :                 if (poWildcard == nullptr)
    2154        1812 :                     poWildcard = json_object_new_object();
    2155        3624 :                 CPLString osKey;
    2156        1812 :                 if (!osAttrNSPrefix.empty())
    2157           0 :                     osKey = osAttrNSPrefix + ":" + osAttrLocalname;
    2158             :                 else
    2159        1812 :                     osKey = osAttrLocalname;
    2160        1812 :                 json_object_object_add(poWildcard, osKey,
    2161             :                                        json_object_new_string(osAttrValue));
    2162             :             }
    2163          30 :             else if (m_bValidate &&
    2164           6 :                      (nFCIdx = m_oCurCtxt.m_poLayer->GetFCFieldIndexFromXPath(
    2165          18 :                           osAttrXPath)) >= 0 &&
    2166           6 :                      !m_oCurCtxt.m_poLayer->GetFeatureClass()
    2167           3 :                           .GetFields()[nFCIdx]
    2168           3 :                           .GetFixedValue()
    2169           3 :                           .empty())
    2170             :             {
    2171             :                 // In validation mode, fixed attributes not present in the
    2172             :                 // document are still reported, which cause spurious warnings
    2173             :             }
    2174          27 :             else if (m_bValidate &&
    2175           5 :                      (nFCIdx = m_oCurCtxt.m_poLayer->GetFCFieldIndexFromXPath(
    2176           2 :                           osAttrXPath)) >= 0 &&
    2177           4 :                      !m_oCurCtxt.m_poLayer->GetFeatureClass()
    2178           2 :                           .GetFields()[nFCIdx]
    2179           2 :                           .GetDefaultValue()
    2180          18 :                           .empty() &&
    2181           2 :                      m_oCurCtxt.m_poLayer->GetFeatureClass()
    2182           2 :                              .GetFields()[nFCIdx]
    2183           4 :                              .GetDefaultValue() == m_osAttrValue)
    2184             :             {
    2185             :                 // In validation mode, default attributes not present in the
    2186             :                 // document are still reported, which cause spurious warnings
    2187             :             }
    2188           9 :             else if (m_oIgnoredXPathMatcher.MatchesRefXPath(osAttrXPath,
    2189             :                                                             osMatchedXPath))
    2190             :             {
    2191           2 :                 if (m_oMapIgnoredXPathToWarn[osMatchedXPath])
    2192             :                 {
    2193           1 :                     CPLError(CE_Warning, CPLE_AppDefined,
    2194             :                              "Attribute with xpath=%s found in document but "
    2195             :                              "ignored according to configuration",
    2196             :                              osAttrXPath.c_str());
    2197             :                 }
    2198             :                 else
    2199             :                 {
    2200           1 :                     CPLDebug("GMLAS",
    2201             :                              "Attribute with xpath=%s found in document but "
    2202             :                              "ignored according to configuration",
    2203             :                              osAttrXPath.c_str());
    2204             :                 }
    2205             :             }
    2206             :             else
    2207             :             {
    2208           7 :                 if (m_bWarnUnexpected)
    2209             :                 {
    2210           5 :                     CPLError(CE_Warning, CPLE_AppDefined,
    2211             :                              "Unexpected attribute with xpath=%s found",
    2212             :                              osAttrXPath.c_str());
    2213             :                 }
    2214             :                 else
    2215             :                 {
    2216             :                     // Emit debug message if unexpected attribute
    2217           2 :                     CPLDebug("GMLAS",
    2218             :                              "Unexpected attribute with xpath=%s found",
    2219             :                              osAttrXPath.c_str());
    2220             :                 }
    2221             :             }
    2222             :         }
    2223             :     }
    2224             : 
    2225             :     // Store wildcard attributes
    2226      153107 :     if (poWildcard != nullptr)
    2227             :     {
    2228        1812 :         SetField(m_oCurCtxt.m_poFeature, m_oCurCtxt.m_poLayer, nWildcardAttrIdx,
    2229             :                  json_object_get_string(poWildcard));
    2230        1812 :         json_object_put(poWildcard);
    2231             :     }
    2232             : 
    2233             :     // Process fixed and default values, except when doing the initial scan
    2234             :     // so as to avoid the bRemoveUnusedFields logic to be confused
    2235      153107 :     if (!m_bInitialPass)
    2236             :     {
    2237      147040 :         const int nFieldCount = m_oCurCtxt.m_poFeature->GetFieldCount();
    2238             :         const std::vector<GMLASField> &aoFields =
    2239      147040 :             m_oCurCtxt.m_poLayer->GetFeatureClass().GetFields();
    2240     5399420 :         for (int i = 0; i < nFieldCount; i++)
    2241             :         {
    2242             :             const int nFCIdx =
    2243     5252380 :                 m_oCurCtxt.m_poLayer->GetFCFieldIndexFromOGRFieldIdx(i);
    2244    10362800 :             if (nFCIdx >= 0 &&
    2245     5110430 :                 aoFields[nFCIdx].GetXPath().find('@') != std::string::npos)
    2246             :             {
    2247             :                 // We process fixed as default. In theory, to be XSD compliant,
    2248             :                 // the user shouldn't have put a different value than the fixed
    2249             :                 // one, but just in case he did, then honour it instead of
    2250             :                 // overwriting it.
    2251             :                 CPLString osFixedDefaultValue =
    2252     3229330 :                     aoFields[nFCIdx].GetFixedValue();
    2253     1614660 :                 if (osFixedDefaultValue.empty())
    2254     1483380 :                     osFixedDefaultValue = aoFields[nFCIdx].GetDefaultValue();
    2255     1877230 :                 if (!osFixedDefaultValue.empty() &&
    2256      262564 :                     !m_oCurCtxt.m_poFeature->IsFieldSetAndNotNull(i))
    2257             :                 {
    2258        2072 :                     SetField(m_oCurCtxt.m_poFeature, m_oCurCtxt.m_poLayer, i,
    2259             :                              osFixedDefaultValue);
    2260             :                 }
    2261             :             }
    2262             :         }
    2263             :     }
    2264      153107 : }
    2265             : 
    2266             : /************************************************************************/
    2267             : /*                           ProcessXLinkHref()                         */
    2268             : /************************************************************************/
    2269             : 
    2270          55 : void GMLASReader::ProcessXLinkHref(int nAttrIdx, const CPLString &osAttrXPath,
    2271             :                                    const CPLString &osAttrValue)
    2272             : {
    2273             :     // If we are a xlink:href attribute, and that the link value is
    2274             :     // a internal link, then find if we have
    2275             :     // a field that does a relation to a targetElement
    2276          55 :     if (osAttrValue[0] == '#')
    2277             :     {
    2278          20 :         const int nAttrIdx2 = m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(
    2279          40 :             GMLASField::MakePKIDFieldXPathFromXLinkHrefXPath(osAttrXPath));
    2280          20 :         if (nAttrIdx2 >= 0)
    2281             :         {
    2282           2 :             SetField(m_oCurCtxt.m_poFeature, m_oCurCtxt.m_poLayer, nAttrIdx2,
    2283           4 :                      osAttrValue.substr(1));
    2284             :         }
    2285          18 :         else if (m_oXLinkResolver.GetConf().m_bResolveInternalXLinks)
    2286             :         {
    2287             :             const CPLString osReferringField(
    2288          18 :                 m_oCurCtxt.m_poLayer->GetLayerDefn()
    2289          18 :                     ->GetFieldDefn(nAttrIdx)
    2290          36 :                     ->GetNameRef());
    2291          36 :             CPLString osId(osAttrValue.substr(1));
    2292          18 :             if (m_bInitialPass)
    2293             :             {
    2294             :                 std::pair<OGRGMLASLayer *, CPLString> oReferringPair(
    2295          18 :                     m_oCurCtxt.m_poLayer, osReferringField);
    2296          18 :                 m_oMapFieldXPathToLinkValue[oReferringPair].push_back(
    2297           9 :                     std::move(osId));
    2298             :             }
    2299             :             else
    2300             :             {
    2301           9 :                 const auto oIter = m_oMapElementIdToLayer.find(osId);
    2302           9 :                 if (oIter != m_oMapElementIdToLayer.end())
    2303             :                 {
    2304           5 :                     OGRGMLASLayer *poTargetLayer = oIter->second;
    2305             :                     const CPLString osLinkFieldXPath =
    2306           5 :                         m_oCurCtxt.m_poLayer
    2307             :                             ->GetXPathOfFieldLinkForAttrToOtherLayer(
    2308             :                                 osReferringField,
    2309          10 :                                 poTargetLayer->GetFeatureClass().GetXPath());
    2310             :                     const int nLinkFieldOGRId =
    2311           5 :                         m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(
    2312             :                             osLinkFieldXPath);
    2313           5 :                     if (nLinkFieldOGRId >= 0)
    2314             :                     {
    2315           5 :                         const auto oIter2 = m_oMapElementIdToPKID.find(osId);
    2316           5 :                         if (oIter2 != m_oMapElementIdToPKID.end())
    2317             :                         {
    2318           3 :                             m_oCurCtxt.m_poFeature->SetField(nLinkFieldOGRId,
    2319           3 :                                                              oIter2->second);
    2320             :                         }
    2321             :                         else
    2322             :                         {
    2323           2 :                             m_oCurCtxt.m_poFeature->SetField(nLinkFieldOGRId,
    2324             :                                                              osId);
    2325             :                         }
    2326             :                     }
    2327             :                 }
    2328             :             }
    2329             :         }
    2330             :     }
    2331             :     else
    2332             :     {
    2333             :         const int nRuleIdx =
    2334          35 :             m_oXLinkResolver.GetMatchingResolutionRule(osAttrValue);
    2335          35 :         if (nRuleIdx >= 0)
    2336             :         {
    2337             :             const GMLASXLinkResolutionConf::URLSpecificResolution &oRule(
    2338          14 :                 m_oXLinkResolver.GetConf().m_aoURLSpecificRules[nRuleIdx]);
    2339          14 :             if (m_bInitialPass)
    2340             :             {
    2341           8 :                 m_oMapXLinkFields[m_oCurCtxt.m_poLayer][osAttrXPath].insert(
    2342           8 :                     nRuleIdx);
    2343             :             }
    2344           6 :             else if (oRule.m_eResolutionMode ==
    2345             :                      GMLASXLinkResolutionConf::RawContent)
    2346             :             {
    2347             :                 const int nAttrIdx2 =
    2348           1 :                     m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(
    2349             :                         GMLASField::
    2350           2 :                             MakeXLinkRawContentFieldXPathFromXLinkHrefXPath(
    2351             :                                 osAttrXPath));
    2352           1 :                 CPLAssert(nAttrIdx2 >= 0);
    2353             : 
    2354             :                 const CPLString osRawContent(
    2355           1 :                     m_oXLinkResolver.GetRawContentForRule(osAttrValue,
    2356           2 :                                                           nRuleIdx));
    2357           1 :                 if (!osRawContent.empty())
    2358             :                 {
    2359           1 :                     SetField(m_oCurCtxt.m_poFeature, m_oCurCtxt.m_poLayer,
    2360             :                              nAttrIdx2, osRawContent);
    2361             :                 }
    2362             :             }
    2363           5 :             else if (oRule.m_eResolutionMode ==
    2364             :                      GMLASXLinkResolutionConf::FieldsFromXPath)
    2365             :             {
    2366             :                 const CPLString osRawContent(
    2367           5 :                     m_oXLinkResolver.GetRawContentForRule(osAttrValue,
    2368          10 :                                                           nRuleIdx));
    2369           5 :                 if (!osRawContent.empty())
    2370             :                 {
    2371           5 :                     CPLXMLNode *psNode = CPLParseXMLString(osRawContent);
    2372           5 :                     if (psNode != nullptr)
    2373             :                     {
    2374          10 :                         std::vector<CPLString> aoXPaths;
    2375          10 :                         std::map<CPLString, size_t> oMapFieldXPathToIdx;
    2376          23 :                         for (size_t i = 0; i < oRule.m_aoFields.size(); ++i)
    2377             :                         {
    2378             :                             const CPLString &osXPathRule(
    2379          18 :                                 oRule.m_aoFields[i].m_osXPath);
    2380          18 :                             aoXPaths.push_back(osXPathRule);
    2381          18 :                             oMapFieldXPathToIdx[osXPathRule] = i;
    2382             :                         }
    2383          10 :                         GMLASXPathMatcher oMatcher;
    2384           5 :                         oMatcher.SetRefXPaths(std::map<CPLString, CPLString>(),
    2385             :                                               aoXPaths);
    2386           5 :                         oMatcher.SetDocumentMapURIToPrefix(
    2387          10 :                             std::map<CPLString, CPLString>());
    2388             : 
    2389           5 :                         CPLXMLNode *psIter = psNode;
    2390          15 :                         for (; psIter != nullptr; psIter = psIter->psNext)
    2391             :                         {
    2392          10 :                             if (psIter->eType == CXT_Element &&
    2393          10 :                                 psIter->pszValue[0] != '?')
    2394             :                             {
    2395           5 :                                 ExploreXMLDoc(osAttrXPath, oRule, psIter,
    2396          10 :                                               CPLString(), oMatcher,
    2397             :                                               oMapFieldXPathToIdx);
    2398             :                             }
    2399             :                         }
    2400             :                     }
    2401           5 :                     CPLDestroyXMLNode(psNode);
    2402             :                 }
    2403             :             }
    2404             :         }
    2405          21 :         else if (m_oXLinkResolver.IsRawContentResolutionEnabled())
    2406             :         {
    2407             :             const int nAttrIdx2 =
    2408          11 :                 m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(
    2409          22 :                     GMLASField::MakeXLinkRawContentFieldXPathFromXLinkHrefXPath(
    2410             :                         osAttrXPath));
    2411          11 :             CPLAssert(nAttrIdx2 >= 0);
    2412             : 
    2413             :             const CPLString osRawContent(
    2414          22 :                 m_oXLinkResolver.GetRawContent(osAttrValue));
    2415          11 :             if (!osRawContent.empty())
    2416             :             {
    2417           7 :                 SetField(m_oCurCtxt.m_poFeature, m_oCurCtxt.m_poLayer,
    2418             :                          nAttrIdx2, osRawContent);
    2419             :             }
    2420             :         }
    2421             :     }
    2422          55 : }
    2423             : 
    2424             : /************************************************************************/
    2425             : /*                            ExploreXMLDoc()                           */
    2426             : /************************************************************************/
    2427             : 
    2428          29 : void GMLASReader::ExploreXMLDoc(
    2429             :     const CPLString &osAttrXPath,
    2430             :     const GMLASXLinkResolutionConf::URLSpecificResolution &oRule,
    2431             :     CPLXMLNode *psNode, const CPLString &osParentXPath,
    2432             :     const GMLASXPathMatcher &oMatcher,
    2433             :     const std::map<CPLString, size_t> &oMapFieldXPathToIdx)
    2434             : {
    2435          58 :     CPLString osXPath;
    2436          29 :     if (osParentXPath.empty())
    2437           5 :         osXPath = psNode->pszValue;
    2438          24 :     else if (psNode->eType == CXT_Element)
    2439          22 :         osXPath = osParentXPath + "/" + psNode->pszValue;
    2440             :     else
    2441             :     {
    2442           2 :         CPLAssert(psNode->eType == CXT_Attribute);
    2443           2 :         osXPath = osParentXPath + "/@" + psNode->pszValue;
    2444             :     }
    2445             : 
    2446          58 :     CPLString osMatchedXPathRule;
    2447          29 :     if (oMatcher.MatchesRefXPath(osXPath, osMatchedXPathRule))
    2448             :     {
    2449          20 :         const auto oIter = oMapFieldXPathToIdx.find(osMatchedXPathRule);
    2450          20 :         CPLAssert(oIter != oMapFieldXPathToIdx.end());
    2451          20 :         const size_t nFieldRuleIdx = oIter->second;
    2452             :         const CPLString osDerivedFieldXPath(
    2453             :             GMLASField::MakeXLinkDerivedFieldXPathFromXLinkHrefXPath(
    2454          40 :                 osAttrXPath, oRule.m_aoFields[nFieldRuleIdx].m_osName));
    2455          20 :         const int nAttrIdx = m_oCurCtxt.m_poLayer->GetOGRFieldIndexFromXPath(
    2456             :             osDerivedFieldXPath);
    2457          20 :         CPLAssert(nAttrIdx >= 0);
    2458          40 :         CPLString osVal;
    2459          20 :         if (psNode->eType == CXT_Element && psNode->psChild != nullptr &&
    2460          18 :             psNode->psChild->eType == CXT_Text &&
    2461          18 :             psNode->psChild->psNext == nullptr)
    2462             :         {
    2463          16 :             osVal = psNode->psChild->pszValue;
    2464             :         }
    2465           4 :         else if (psNode->eType == CXT_Attribute)
    2466             :         {
    2467           2 :             osVal = psNode->psChild->pszValue;
    2468             :         }
    2469             :         else
    2470             :         {
    2471           2 :             char *pszContent = CPLSerializeXMLTree(psNode->psChild);
    2472           2 :             osVal = pszContent;
    2473           2 :             CPLFree(pszContent);
    2474             :         }
    2475          22 :         if (m_oCurCtxt.m_poFeature->IsFieldSetAndNotNull(nAttrIdx) &&
    2476           2 :             m_oCurCtxt.m_poFeature->GetFieldDefnRef(nAttrIdx)->GetType() ==
    2477             :                 OFTString)
    2478             :         {
    2479           2 :             osVal = m_oCurCtxt.m_poFeature->GetFieldAsString(nAttrIdx) +
    2480           4 :                     CPLString(" ") + osVal;
    2481             :         }
    2482          20 :         SetField(m_oCurCtxt.m_poFeature, m_oCurCtxt.m_poLayer, nAttrIdx, osVal);
    2483             :     }
    2484             : 
    2485          29 :     CPLXMLNode *psIter = psNode->psChild;
    2486          75 :     for (; psIter != nullptr; psIter = psIter->psNext)
    2487             :     {
    2488          46 :         if (psIter->eType == CXT_Element || psIter->eType == CXT_Attribute)
    2489             :         {
    2490          24 :             ExploreXMLDoc(osAttrXPath, oRule, psIter, osXPath, oMatcher,
    2491             :                           oMapFieldXPathToIdx);
    2492             :         }
    2493             :     }
    2494          29 : }
    2495             : 
    2496             : /************************************************************************/
    2497             : /*                              endElement()                            */
    2498             : /************************************************************************/
    2499             : 
    2500      162266 : void GMLASReader::endElement(const XMLCh *const uri,
    2501             :                              const XMLCh *const localname,
    2502             :                              const XMLCh *const
    2503             : #ifdef DEBUG_VERBOSE
    2504             :                                  qname
    2505             : #endif
    2506             : )
    2507             : {
    2508      162266 :     m_nEntityCounter = 0;
    2509             : 
    2510      162266 :     m_nLevel--;
    2511             : 
    2512             : #ifdef DEBUG_VERBOSE
    2513             :     CPLDebug("GMLAS", "m_nLevel = %d", m_nLevel);
    2514             : #endif
    2515             : 
    2516             : #ifdef DEBUG_VERBOSE
    2517             :     {
    2518             :         const CPLString &osLocalname(transcode(localname, m_osLocalname));
    2519             :         const CPLString &osNSPrefix(
    2520             :             m_osNSPrefix = m_oMapURIToPrefix[transcode(uri, m_osNSUri)]);
    2521             :         if (osNSPrefix.empty())
    2522             :             m_osXPath = osLocalname;
    2523             :         else
    2524             :         {
    2525             :             m_osXPath.reserve(osNSPrefix.size() + 1 + osLocalname.size());
    2526             :             m_osXPath = osNSPrefix;
    2527             :             m_osXPath += ":";
    2528             :             m_osXPath += osLocalname;
    2529             :         }
    2530             :     }
    2531             :     CPLDebug("GMLAS", "endElement(%s / %s)", transcode(qname).c_str(),
    2532             :              m_osXPath.c_str());
    2533             : #endif
    2534             : 
    2535      162266 :     if (m_nLevelSilentIgnoredXPath == m_nLevel)
    2536             :     {
    2537           0 :         m_nLevelSilentIgnoredXPath = -1;
    2538             :     }
    2539             : 
    2540             :     // Make sure to set field only if we are at the expected nesting level
    2541      162266 :     if (m_nCurFieldIdx >= 0 && m_nLevel == m_nCurFieldLevel - 1)
    2542             :     {
    2543             :         const OGRFieldType eType(
    2544      113078 :             m_oCurCtxt.m_poFeature->GetFieldDefnRef(m_nCurFieldIdx)->GetType());
    2545             : 
    2546             :         // Assign XML content to field value
    2547      113078 :         if (IsArrayType(eType))
    2548             :         {
    2549             :             const int nFCFieldIdx =
    2550       28886 :                 m_oCurCtxt.m_poLayer->GetFCFieldIndexFromOGRFieldIdx(
    2551             :                     m_nCurFieldIdx);
    2552       57772 :             if (nFCFieldIdx >= 0 && m_oCurCtxt.m_poLayer->GetFeatureClass()
    2553       28886 :                                         .GetFields()[nFCFieldIdx]
    2554       28886 :                                         .IsList())
    2555             :             {
    2556        5300 :                 SetField(m_oCurCtxt.m_poFeature, m_oCurCtxt.m_poLayer,
    2557        5300 :                          m_nCurFieldIdx, m_osTextContent);
    2558             :             }
    2559       23586 :             else if (m_nTextContentListEstimatedSize > m_nMaxContentSize)
    2560             :             {
    2561           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory,
    2562             :                          "Too much repeated data in a single element");
    2563           0 :                 m_bParsingError = true;
    2564             :             }
    2565             :             else
    2566             :             {
    2567             :                 // Transform boolean values to something that OGR understands
    2568       29806 :                 if (eType == OFTIntegerList &&
    2569        6220 :                     m_oCurCtxt.m_poFeature->GetFieldDefnRef(m_nCurFieldIdx)
    2570        6220 :                             ->GetSubType() == OFSTBoolean)
    2571             :                 {
    2572        2140 :                     if (m_osTextContent == "true")
    2573        1052 :                         m_osTextContent = "1";
    2574             :                     else
    2575        1088 :                         m_osTextContent = "0";
    2576             :                 }
    2577             : 
    2578       23586 :                 m_osTextContentList.AddString(m_osTextContent);
    2579             :                 // 16 is an arbitrary number for the cost of a new entry in the
    2580             :                 // string list
    2581       23586 :                 m_nTextContentListEstimatedSize += 16 + m_osTextContent.size();
    2582       23586 :                 m_oCurCtxt.m_poFeature->SetField(m_nCurFieldIdx,
    2583       23586 :                                                  m_osTextContentList.List());
    2584             :             }
    2585             :         }
    2586             :         else
    2587             :         {
    2588       84192 :             if (m_bIsXMLBlobIncludeUpper && FillTextContent())
    2589             :             {
    2590             :                 const CPLString &osLocalname(
    2591        1638 :                     transcode(localname, m_osLocalname));
    2592             :                 const CPLString &osNSPrefix(
    2593        1638 :                     m_oMapURIToPrefix[transcode(uri, m_osNSUri)]);
    2594             : 
    2595        1638 :                 m_osTextContent += "</";
    2596        1638 :                 if (!osNSPrefix.empty())
    2597             :                 {
    2598           2 :                     m_osTextContent += osNSPrefix;
    2599           2 :                     m_osTextContent += ":";
    2600             :                 }
    2601        1638 :                 m_osTextContent += osLocalname;
    2602        1638 :                 m_osTextContent += ">";
    2603             :             }
    2604             : 
    2605       84192 :             SetField(m_oCurCtxt.m_poFeature, m_oCurCtxt.m_poLayer,
    2606       84192 :                      m_nCurFieldIdx, m_osTextContent);
    2607             :         }
    2608             :     }
    2609             : 
    2610             :     // Make sure to set field only if we are at the expected nesting level
    2611      162266 :     if (m_nCurGeomFieldIdx >= 0 && m_nLevel == m_nCurFieldLevel - 1)
    2612             :     {
    2613         444 :         if (!m_apsXMLNodeStack.empty())
    2614             :         {
    2615         420 :             CPLAssert(m_apsXMLNodeStack.size() == 1);
    2616         420 :             CPLXMLNode *psRoot = m_apsXMLNodeStack[0].psNode;
    2617         420 :             ProcessGeometry(psRoot);
    2618         420 :             CPLDestroyXMLNode(psRoot);
    2619         420 :             m_apsXMLNodeStack.clear();
    2620             :         }
    2621             :     }
    2622             : 
    2623      162266 :     if ((m_nCurFieldIdx >= 0 || m_nCurGeomFieldIdx >= 0) &&
    2624      148093 :         m_nLevel == m_nCurFieldLevel - 1)
    2625             :     {
    2626      113446 :         m_bIsXMLBlob = false;
    2627      113446 :         m_bIsXMLBlobIncludeUpper = false;
    2628             :     }
    2629             : 
    2630      162266 :     if (m_bIsXMLBlob)
    2631             :     {
    2632        7832 :         if (m_nCurGeomFieldIdx >= 0)
    2633             :         {
    2634        1478 :             if (m_apsXMLNodeStack.size() > 1)
    2635             :             {
    2636             : #ifdef DEBUG_VERBOSE
    2637             :                 CPLDebug("GMLAS", "m_apsXMLNodeStack.pop_back()");
    2638             : #endif
    2639        1074 :                 m_apsXMLNodeStack.pop_back();
    2640             :             }
    2641             :         }
    2642             : 
    2643        7832 :         if (FillTextContent())
    2644             :         {
    2645        4716 :             const CPLString &osLocalname(transcode(localname, m_osLocalname));
    2646             :             const CPLString &osNSPrefix(
    2647        4716 :                 m_oMapURIToPrefix[transcode(uri, m_osNSUri)]);
    2648             : 
    2649        4716 :             m_osTextContent += "</";
    2650        4716 :             if (!osNSPrefix.empty())
    2651             :             {
    2652        1011 :                 m_osTextContent += osNSPrefix;
    2653        1011 :                 m_osTextContent += ":";
    2654             :             }
    2655        4716 :             m_osTextContent += osLocalname;
    2656        4716 :             m_osTextContent += ">";
    2657             : 
    2658        4716 :             if (m_osTextContent.size() > m_nMaxContentSize)
    2659             :             {
    2660           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory,
    2661             :                          "Too much data in a single element");
    2662           0 :                 m_bParsingError = true;
    2663             :             }
    2664             :         }
    2665             :     }
    2666             :     else
    2667             :     {
    2668      154434 :         m_osTextContent.clear();
    2669             :     }
    2670             : 
    2671      162266 :     if (m_nSWEDataArrayLevel >= 0)
    2672             :     {
    2673         204 :         if (m_nLevel > m_nSWEDataArrayLevel)
    2674             :         {
    2675         192 :             CPLAssert(m_apsXMLNodeStack.size() > 1);
    2676         192 :             m_apsXMLNodeStack.pop_back();
    2677             :         }
    2678             :         else
    2679             :         {
    2680          12 :             CPLAssert(m_apsXMLNodeStack.size() == 1);
    2681          12 :             CPLXMLNode *psRoot = m_apsXMLNodeStack[0].psNode;
    2682          12 :             ProcessSWEDataArray(psRoot);
    2683          12 :             m_nSWEDataArrayLevel = -1;
    2684          12 :             CPLDestroyXMLNode(psRoot);
    2685          12 :             m_apsXMLNodeStack.clear();
    2686             :         }
    2687             :     }
    2688             : 
    2689             :     // The while and not just if is needed when a group is at the end of an
    2690             :     // element
    2691      437372 :     while (!m_aoStackContext.empty() &&
    2692      218091 :            m_aoStackContext.back().m_nLevel >= m_nLevel)
    2693             :     {
    2694      114030 :         auto oMapCounter = m_aoStackContext.back().m_oMapCounter;
    2695       57015 :         if (!m_aoStackContext.back().m_osCurSubXPath.empty())
    2696             :         {
    2697             : #ifdef DEBUG_VERBOSE
    2698             :             CPLDebug("GMLAS", "Restoring m_osCurSubXPath from %s to %s",
    2699             :                      m_osCurSubXPath.c_str(),
    2700             :                      m_aoStackContext.back().m_osCurSubXPath.c_str());
    2701             : #endif
    2702       11613 :             m_osCurSubXPath = m_aoStackContext.back().m_osCurSubXPath;
    2703             :         }
    2704             : 
    2705       57015 :         if (m_oCurCtxt.m_poGroupLayer == m_oCurCtxt.m_poLayer)
    2706             :         {
    2707       12403 :             PopContext();
    2708       12403 :             CPLAssert(!m_aoStackContext.empty());
    2709       12403 :             m_oCurCtxt.m_poLayer = m_aoStackContext.back().m_poLayer;
    2710             :         }
    2711             :         else
    2712             :         {
    2713       44612 :             if (m_oCurCtxt.m_poGroupLayer)
    2714             :             {
    2715             :                 /* Case like
    2716             :                         <first_elt_of_group>...</first_elt_of_group>
    2717             :                     </end_of_enclosing_element>   <!-- we are here at
    2718             :                    endElement() -->
    2719             :                 */
    2720             : 
    2721             :                 // CPLDebug("GMLAS", "Feature ready");
    2722           0 :                 PushFeatureReady(
    2723           0 :                     std::unique_ptr<OGRFeature>(m_oCurCtxt.m_poFeature),
    2724             :                     m_oCurCtxt.m_poGroupLayer);
    2725             :                 // CPLDebug("GMLAS", "Feature ready");
    2726           0 :                 PushFeatureReady(std::unique_ptr<OGRFeature>(
    2727           0 :                                      m_aoStackContext.back().m_poFeature),
    2728           0 :                                  m_aoStackContext.back().m_poLayer);
    2729             :             }
    2730             :             else
    2731             :             {
    2732             :                 // CPLDebug("GMLAS", "Feature ready");
    2733       44612 :                 PushFeatureReady(
    2734       89224 :                     std::unique_ptr<OGRFeature>(m_oCurCtxt.m_poFeature),
    2735             :                     m_oCurCtxt.m_poLayer);
    2736             :             }
    2737       44612 :             PopContext();
    2738       44612 :             if (!m_aoStackContext.empty())
    2739             :             {
    2740       43608 :                 m_oCurCtxt = m_aoStackContext.back();
    2741       43608 :                 m_oCurCtxt.m_osCurSubXPath.clear();
    2742       43608 :                 if (m_oCurCtxt.m_nLevel < 0)
    2743             :                 {
    2744        3517 :                     PopContext();
    2745        3517 :                     CPLAssert(!m_aoStackContext.empty());
    2746        3517 :                     m_oCurCtxt.m_poLayer = m_aoStackContext.back().m_poLayer;
    2747             :                 }
    2748             :             }
    2749             :             else
    2750             :             {
    2751        1004 :                 m_oCurCtxt.m_poFeature = nullptr;
    2752        1004 :                 m_oCurCtxt.m_poLayer = nullptr;
    2753        1004 :                 m_oCurCtxt.m_poGroupLayer = nullptr;
    2754        1004 :                 m_oCurCtxt.m_nGroupLayerLevel = -1;
    2755        1004 :                 m_oCurCtxt.m_nLastFieldIdxGroupLayer = -1;
    2756             :             }
    2757       44612 :             m_nCurFieldIdx = -1;
    2758             :         }
    2759       57015 :         m_oCurCtxt.m_oMapCounter = std::move(oMapCounter);
    2760             : 
    2761             : #ifdef DEBUG_VERBOSE
    2762             :         CPLDebug("GMLAS", "m_oCurCtxt = ");
    2763             :         m_oCurCtxt.Dump();
    2764             : #endif
    2765             :     }
    2766             : 
    2767      162266 :     size_t nLastXPathLength = m_anStackXPathLength.back();
    2768      162266 :     m_anStackXPathLength.pop_back();
    2769      162266 :     if (m_anStackXPathLength.empty())
    2770         956 :         m_osCurXPath.clear();
    2771             :     else
    2772      161310 :         m_osCurXPath.resize(m_osCurXPath.size() - 1 - nLastXPathLength);
    2773             : 
    2774      162266 :     if (m_osCurSubXPath.size() >= 1 + nLastXPathLength)
    2775      161076 :         m_osCurSubXPath.resize(m_osCurSubXPath.size() - 1 - nLastXPathLength);
    2776        1190 :     else if (m_osCurSubXPath.size() == nLastXPathLength)
    2777        1004 :         m_osCurSubXPath.clear();
    2778             : 
    2779      162266 :     if (m_nSWEDataRecordLevel >= 0)
    2780             :     {
    2781          87 :         if (m_nLevel > m_nSWEDataRecordLevel)
    2782             :         {
    2783          82 :             CPLAssert(m_apsXMLNodeStack.size() > 1);
    2784          82 :             m_apsXMLNodeStack.pop_back();
    2785             :         }
    2786             :         else
    2787             :         {
    2788           5 :             CPLAssert(m_apsXMLNodeStack.size() == 1);
    2789           5 :             CPLXMLNode *psRoot = m_apsXMLNodeStack[0].psNode;
    2790           5 :             ProcessSWEDataRecord(psRoot);
    2791           5 :             m_nSWEDataRecordLevel = -1;
    2792           5 :             CPLDestroyXMLNode(psRoot);
    2793           5 :             m_apsXMLNodeStack.clear();
    2794             :         }
    2795             :     }
    2796      162266 : }
    2797             : 
    2798             : /************************************************************************/
    2799             : /*                             startEntity()                            */
    2800             : /************************************************************************/
    2801             : 
    2802        2002 : void GMLASReader::startEntity(const XMLCh *const /* name */)
    2803             : {
    2804        2002 :     m_nEntityCounter++;
    2805        2002 :     if (m_nEntityCounter > 1000 && !m_bParsingError)
    2806             :     {
    2807             :         throw SAXNotSupportedException(
    2808           2 :             "File probably corrupted (million laugh pattern)");
    2809             :     }
    2810        2000 : }
    2811             : 
    2812             : /************************************************************************/
    2813             : /*                             SetSWEValue()                            */
    2814             : /************************************************************************/
    2815             : 
    2816          63 : static void SetSWEValue(OGRFeature *poFeature, int iField, CPLString &osValue)
    2817             : {
    2818          63 :     if (!osValue.empty())
    2819             :     {
    2820          57 :         OGRFieldDefn *poFieldDefn = poFeature->GetFieldDefnRef(iField);
    2821          57 :         OGRFieldType eType(poFieldDefn->GetType());
    2822          57 :         OGRFieldSubType eSubType(poFieldDefn->GetSubType());
    2823          57 :         if (eType == OFTReal || eType == OFTInteger)
    2824             :         {
    2825          24 :             osValue.Trim();
    2826          24 :             if (eSubType == OFSTBoolean)
    2827             :             {
    2828             :                 osValue =
    2829           3 :                     EQUAL(osValue, "1") || EQUAL(osValue, "True") ? "1" : "0";
    2830             :             }
    2831             :         }
    2832          57 :         poFeature->SetField(iField, osValue.c_str());
    2833             :     }
    2834          63 : }
    2835             : 
    2836             : /************************************************************************/
    2837             : /*                              SkipSpace()                             */
    2838             : /************************************************************************/
    2839             : 
    2840         111 : static size_t SkipSpace(const char *pszValues, size_t i)
    2841             : {
    2842         111 :     while (isspace(static_cast<unsigned char>(pszValues[i])))
    2843          39 :         i++;
    2844          72 :     return i;
    2845             : }
    2846             : 
    2847             : /************************************************************************/
    2848             : /*                         ProcessSWEDataArray()                        */
    2849             : /************************************************************************/
    2850             : 
    2851          12 : void GMLASReader::ProcessSWEDataArray(CPLXMLNode *psRoot)
    2852             : {
    2853          12 :     if (m_oCurCtxt.m_poLayer == nullptr)
    2854           0 :         return;
    2855             : 
    2856          12 :     CPLStripXMLNamespace(psRoot, "swe", true);
    2857          12 :     CPLXMLNode *psElementType = CPLGetXMLNode(psRoot, "elementType");
    2858          12 :     if (psElementType == nullptr)
    2859           0 :         return;
    2860          12 :     CPLXMLNode *psDataRecord = CPLGetXMLNode(psElementType, "DataRecord");
    2861          12 :     if (psDataRecord == nullptr)
    2862           0 :         return;
    2863          12 :     const char *pszValues = CPLGetXMLValue(psRoot, "values", nullptr);
    2864          12 :     if (pszValues == nullptr)
    2865           0 :         return;
    2866          12 :     CPLXMLNode *psTextEncoding = CPLGetXMLNode(psRoot, "encoding.TextEncoding");
    2867          12 :     if (psTextEncoding == nullptr)
    2868           0 :         return;
    2869             :     // CPLString osDecimalSeparator =
    2870             :     //     CPLGetXMLValue(psTextEncoding, "decimalSeparator", ".");
    2871             :     CPLString osBlockSeparator =
    2872          12 :         CPLGetXMLValue(psTextEncoding, "blockSeparator", "");
    2873             :     CPLString osTokenSeparator =
    2874          12 :         CPLGetXMLValue(psTextEncoding, "tokenSeparator", "");
    2875          12 :     if (osBlockSeparator.empty() || osTokenSeparator.empty())
    2876           0 :         return;
    2877             : 
    2878          12 :     if (m_bInitialPass)
    2879             :     {
    2880           6 :         CPLString osLayerName;
    2881           3 :         osLayerName.Printf("DataArray_%d", m_nSWEDataArrayLayerIdx + 1);
    2882             :         const char *pszElementTypeName =
    2883           3 :             CPLGetXMLValue(psElementType, "name", nullptr);
    2884           3 :         if (pszElementTypeName != nullptr)
    2885             :         {
    2886           1 :             osLayerName += "_";
    2887           1 :             osLayerName += pszElementTypeName;
    2888             :         }
    2889           3 :         osLayerName = osLayerName.tolower();
    2890           6 :         auto poLayer = std::make_unique<OGRGMLASLayer>(osLayerName);
    2891             : 
    2892             :         // Register layer in _ogr_layers_metadata
    2893             :         {
    2894             :             OGRFeature oLayerDescFeature(
    2895           6 :                 m_poLayersMetadataLayer->GetLayerDefn());
    2896           3 :             oLayerDescFeature.SetField(szLAYER_NAME, osLayerName);
    2897           3 :             oLayerDescFeature.SetField(szLAYER_CATEGORY, szSWE_DATA_ARRAY);
    2898             : 
    2899           3 :             CPLString osFieldName(szPARENT_PREFIX);
    2900             :             osFieldName +=
    2901           3 :                 m_oCurCtxt.m_poLayer->GetLayerDefn()
    2902           3 :                     ->GetFieldDefn(m_oCurCtxt.m_poLayer->GetIDFieldIdx())
    2903           3 :                     ->GetNameRef();
    2904           3 :             oLayerDescFeature.SetField(szLAYER_PARENT_PKID_NAME,
    2905             :                                        osFieldName.c_str());
    2906           3 :             CPL_IGNORE_RET_VAL(
    2907           3 :                 m_poLayersMetadataLayer->CreateFeature(&oLayerDescFeature));
    2908             :         }
    2909             : 
    2910             :         // Register layer relationship in _ogr_layer_relationships
    2911             :         {
    2912             :             OGRFeature oRelationshipsFeature(
    2913           3 :                 m_poRelationshipsLayer->GetLayerDefn());
    2914           3 :             oRelationshipsFeature.SetField(szPARENT_LAYER,
    2915           3 :                                            m_oCurCtxt.m_poLayer->GetName());
    2916           3 :             oRelationshipsFeature.SetField(
    2917             :                 szPARENT_PKID,
    2918           3 :                 m_oCurCtxt.m_poLayer->GetLayerDefn()
    2919           3 :                     ->GetFieldDefn(m_oCurCtxt.m_poLayer->GetIDFieldIdx())
    2920             :                     ->GetNameRef());
    2921           3 :             if (!m_osSWEDataArrayParentField.empty())
    2922             :             {
    2923           3 :                 oRelationshipsFeature.SetField(szPARENT_ELEMENT_NAME,
    2924             :                                                m_osSWEDataArrayParentField);
    2925             :             }
    2926           3 :             oRelationshipsFeature.SetField(szCHILD_LAYER, osLayerName);
    2927           3 :             CPL_IGNORE_RET_VAL(
    2928           3 :                 m_poRelationshipsLayer->CreateFeature(&oRelationshipsFeature));
    2929             :         }
    2930             : 
    2931           3 :         poLayer->ProcessDataRecordOfDataArrayCreateFields(
    2932             :             m_oCurCtxt.m_poLayer, psDataRecord, m_poFieldsMetadataLayer);
    2933           3 :         m_apoSWEDataArrayLayersOwned.emplace_back(std::move(poLayer));
    2934             :     }
    2935             :     else
    2936             :     {
    2937           9 :         CPLAssert(m_nSWEDataArrayLayerIdx <
    2938             :                   static_cast<int>(m_apoSWEDataArrayLayersRef.size()));
    2939             :         OGRGMLASLayer *poLayer =
    2940           9 :             m_apoSWEDataArrayLayersRef[m_nSWEDataArrayLayerIdx];
    2941             :         // -1 because first field is parent id
    2942           9 :         const int nFieldCount = poLayer->GetLayerDefn()->GetFieldCount() - 1;
    2943           9 :         int nFID = 1;
    2944           9 :         int iField = 0;
    2945           9 :         const size_t nLen = strlen(pszValues);
    2946           9 :         std::unique_ptr<OGRFeature> poFeature;
    2947           9 :         const bool bSameSep = (osTokenSeparator == osBlockSeparator);
    2948           9 :         size_t nLastValid = SkipSpace(pszValues, 0);
    2949           9 :         size_t i = nLastValid;
    2950         645 :         while (i < nLen)
    2951             :         {
    2952         636 :             if (poFeature == nullptr)
    2953             :             {
    2954             :                 poFeature =
    2955          15 :                     std::make_unique<OGRFeature>(poLayer->GetLayerDefn());
    2956          15 :                 poFeature->SetFID(nFID);
    2957          30 :                 poFeature->SetField(0,
    2958          15 :                                     m_oCurCtxt.m_poFeature->GetFieldAsString(
    2959          15 :                                         m_oCurCtxt.m_poLayer->GetIDFieldIdx()));
    2960          15 :                 nFID++;
    2961          15 :                 iField = 0;
    2962             :             }
    2963         636 :             if (strncmp(pszValues + i, osTokenSeparator,
    2964         636 :                         osTokenSeparator.size()) == 0)
    2965             :             {
    2966          54 :                 if (bSameSep && iField == nFieldCount)
    2967             :                 {
    2968           3 :                     PushFeatureReady(std::move(poFeature), poLayer);
    2969             :                     poFeature =
    2970           3 :                         std::make_unique<OGRFeature>(poLayer->GetLayerDefn());
    2971           3 :                     poFeature->SetFID(nFID);
    2972           6 :                     poFeature->SetField(
    2973           3 :                         0, m_oCurCtxt.m_poFeature->GetFieldAsString(
    2974           3 :                                m_oCurCtxt.m_poLayer->GetIDFieldIdx()));
    2975           3 :                     nFID++;
    2976           3 :                     iField = 0;
    2977             :                 }
    2978             : 
    2979          54 :                 if (iField < nFieldCount)
    2980             :                 {
    2981          51 :                     CPLString osValue(pszValues + nLastValid, i - nLastValid);
    2982             :                     // +1 because first field is parent id
    2983          51 :                     SetSWEValue(poFeature.get(), iField + 1, osValue);
    2984          51 :                     iField++;
    2985             :                 }
    2986          54 :                 nLastValid = i + osTokenSeparator.size();
    2987          54 :                 nLastValid = SkipSpace(pszValues, nLastValid);
    2988          54 :                 i = nLastValid;
    2989             :             }
    2990         582 :             else if (strncmp(pszValues + i, osBlockSeparator,
    2991         582 :                              osBlockSeparator.size()) == 0)
    2992             :             {
    2993           9 :                 if (iField < nFieldCount)
    2994             :                 {
    2995           6 :                     CPLString osValue(pszValues + nLastValid, i - nLastValid);
    2996             :                     // +1 because first field is parent id
    2997           6 :                     SetSWEValue(poFeature.get(), iField + 1, osValue);
    2998           6 :                     iField++;
    2999             :                 }
    3000           9 :                 PushFeatureReady(std::move(poFeature), poLayer);
    3001           9 :                 poFeature.reset();
    3002           9 :                 nLastValid = i + osBlockSeparator.size();
    3003           9 :                 nLastValid = SkipSpace(pszValues, nLastValid);
    3004           9 :                 i = nLastValid;
    3005             :             }
    3006             :             else
    3007             :             {
    3008         573 :                 i++;
    3009             :             }
    3010             :         }
    3011             :         // cppcheck-suppress accessMoved
    3012           9 :         if (poFeature)
    3013             :         {
    3014           6 :             if (iField < nFieldCount)
    3015             :             {
    3016          12 :                 CPLString osValue(pszValues + nLastValid, nLen - nLastValid);
    3017             :                 // +1 because first field is parent id
    3018           6 :                 SetSWEValue(poFeature.get(), iField + 1, osValue);
    3019             :                 // iField ++;
    3020             :             }
    3021           6 :             PushFeatureReady(std::move(poFeature), poLayer);
    3022             :         }
    3023             :     }
    3024          12 :     m_nSWEDataArrayLayerIdx++;
    3025             : }
    3026             : 
    3027             : /************************************************************************/
    3028             : /*                        ProcessSWEDataRecord()                        */
    3029             : /************************************************************************/
    3030             : 
    3031           5 : void GMLASReader::ProcessSWEDataRecord(CPLXMLNode *psRoot)
    3032             : {
    3033           5 :     CPLStripXMLNamespace(psRoot, "swe", true);
    3034           5 :     if (m_bInitialPass)
    3035             :     {
    3036             :         // Collect existing live features of this layer, so that we can
    3037             :         // patch them
    3038           8 :         std::vector<OGRFeature *> apoFeatures;
    3039           4 :         apoFeatures.push_back(m_oCurCtxt.m_poFeature);
    3040           8 :         for (auto &feature : m_aoFeaturesReady)
    3041             :         {
    3042           4 :             if (feature.second == m_oCurCtxt.m_poLayer)
    3043           0 :                 apoFeatures.push_back(feature.first.get());
    3044             :         }
    3045           4 :         m_oCurCtxt.m_poLayer->ProcessDataRecordCreateFields(
    3046             :             psRoot, apoFeatures, m_poFieldsMetadataLayer);
    3047             :     }
    3048             :     else
    3049             :     {
    3050           1 :         m_oCurCtxt.m_poLayer->ProcessDataRecordFillFeature(
    3051             :             psRoot, m_oCurCtxt.m_poFeature);
    3052             :     }
    3053           5 : }
    3054             : 
    3055             : /************************************************************************/
    3056             : /*                            GMLASGetSRSName()                         */
    3057             : /************************************************************************/
    3058             : 
    3059         420 : static const char *GMLASGetSRSName(CPLXMLNode *psNode)
    3060             : {
    3061         420 :     const char *pszSRSName = CPLGetXMLValue(psNode, szSRS_NAME, nullptr);
    3062         420 :     if (pszSRSName == nullptr)
    3063             :     {
    3064             :         // Case of a gml:Point where the srsName is on the gml:pos
    3065         340 :         pszSRSName = CPLGetXMLValue(psNode, "gml:pos.srsName", nullptr);
    3066             :     }
    3067         420 :     return pszSRSName;
    3068             : }
    3069             : 
    3070             : /************************************************************************/
    3071             : /*                    AddMissingSRSDimension()                          */
    3072             : /************************************************************************/
    3073             : 
    3074           1 : static void AddMissingSRSDimension(CPLXMLNode *psRoot, int nDefaultSrsDimension)
    3075             : {
    3076           3 :     for (CPLXMLNode *psIter = psRoot->psChild; psIter; psIter = psIter->psNext)
    3077             :     {
    3078           2 :         if (psIter->eType == CXT_Element)
    3079             :         {
    3080           1 :             if (CPLGetXMLValue(psIter, "srsDimension", nullptr) == nullptr)
    3081             :             {
    3082           1 :                 if (strcmp(psIter->pszValue, "gml:posList") == 0)
    3083             :                 {
    3084           1 :                     CPLAddXMLAttributeAndValue(
    3085             :                         psIter, "srsDimension",
    3086             :                         CPLSPrintf("%d", nDefaultSrsDimension));
    3087             :                 }
    3088             :                 else
    3089             :                 {
    3090           0 :                     AddMissingSRSDimension(psIter, nDefaultSrsDimension);
    3091             :                 }
    3092             :             }
    3093             :         }
    3094             :     }
    3095           1 : }
    3096             : 
    3097             : /************************************************************************/
    3098             : /*                            ProcessGeometry()                         */
    3099             : /************************************************************************/
    3100             : 
    3101         420 : void GMLASReader::ProcessGeometry(CPLXMLNode *psRoot)
    3102             : {
    3103             :     OGRGeomFieldDefn *poGeomFieldDefn =
    3104         420 :         m_oCurCtxt.m_poFeature->GetGeomFieldDefnRef(m_nCurGeomFieldIdx);
    3105             : 
    3106         420 :     if (m_bInitialPass)
    3107             :     {
    3108         228 :         const char *pszSRSName = GMLASGetSRSName(psRoot);
    3109         228 :         if (pszSRSName != nullptr)
    3110             :         {
    3111             :             // If we are doing a first pass, store the SRS of the geometry
    3112             :             // column
    3113         100 :             if (!m_oSetGeomFieldsWithUnknownSRS.empty() &&
    3114          50 :                 m_oSetGeomFieldsWithUnknownSRS.find(poGeomFieldDefn) !=
    3115         100 :                     m_oSetGeomFieldsWithUnknownSRS.end())
    3116             :             {
    3117          42 :                 OGRSpatialReference *poSRS = new OGRSpatialReference();
    3118          42 :                 poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    3119             : 
    3120          42 :                 if (poSRS->SetFromUserInput(
    3121             :                         pszSRSName,
    3122             :                         OGRSpatialReference::
    3123          42 :                             SET_FROM_USER_INPUT_LIMITATIONS_get()) ==
    3124             :                     OGRERR_NONE)
    3125             :                 {
    3126          42 :                     m_oMapGeomFieldDefnToSRSName[poGeomFieldDefn] = pszSRSName;
    3127          42 :                     poGeomFieldDefn->SetSpatialRef(poSRS);
    3128             :                 }
    3129          42 :                 poSRS->Release();
    3130          42 :                 m_oSetGeomFieldsWithUnknownSRS.erase(poGeomFieldDefn);
    3131             :             }
    3132             :         }
    3133         228 :         return;
    3134             :     }
    3135             : 
    3136         193 :     if (m_nDefaultSrsDimension != 0 &&
    3137           1 :         CPLGetXMLValue(psRoot, "srsDimension", nullptr) == nullptr)
    3138             :     {
    3139           1 :         AddMissingSRSDimension(psRoot, m_nDefaultSrsDimension);
    3140             :     }
    3141             : 
    3142             : #ifdef DEBUG_VERBOSE
    3143             :     {
    3144             :         char *pszXML = CPLSerializeXMLTree(psRoot);
    3145             :         CPLDebug("GML", "geometry = %s", pszXML);
    3146             :         CPLFree(pszXML);
    3147             :     }
    3148             : #endif
    3149             : 
    3150             :     auto poGeom = std::unique_ptr<OGRGeometry>(
    3151         384 :         OGRGeometry::FromHandle(OGR_G_CreateFromGMLTree(psRoot)));
    3152         192 :     if (poGeom != nullptr)
    3153             :     {
    3154         192 :         const char *pszSRSName = GMLASGetSRSName(psRoot);
    3155             : 
    3156         192 :         bool bSwapXY = false;
    3157         192 :         if (pszSRSName != nullptr)
    3158             :         {
    3159             :             // Check if the srsName indicates unusual axis order,
    3160             :             // and if so swap x and y coordinates.
    3161          38 :             const auto oIter = m_oMapSRSNameToInvertedAxis.find(pszSRSName);
    3162          38 :             if (oIter == m_oMapSRSNameToInvertedAxis.end())
    3163             :             {
    3164          38 :                 OGRSpatialReference oSRS;
    3165          38 :                 oSRS.SetFromUserInput(
    3166             :                     pszSRSName,
    3167             :                     OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
    3168          66 :                 bSwapXY = !STARTS_WITH_CI(pszSRSName, "EPSG:") &&
    3169          28 :                           (CPL_TO_BOOL(oSRS.EPSGTreatsAsLatLong()) ||
    3170           2 :                            CPL_TO_BOOL(oSRS.EPSGTreatsAsNorthingEasting()));
    3171          38 :                 m_oMapSRSNameToInvertedAxis[pszSRSName] = bSwapXY;
    3172             :             }
    3173             :             else
    3174             :             {
    3175           0 :                 bSwapXY = oIter->second;
    3176             :             }
    3177             :         }
    3178         192 :         if ((bSwapXY && m_eSwapCoordinates == GMLAS_SWAP_AUTO) ||
    3179         172 :             m_eSwapCoordinates == GMLAS_SWAP_YES)
    3180             :         {
    3181          36 :             poGeom->swapXY();
    3182             :         }
    3183             : 
    3184             :         // Do we need to do reprojection ?
    3185         230 :         if (pszSRSName != nullptr &&
    3186         230 :             poGeomFieldDefn->GetSpatialRef() != nullptr &&
    3187          38 :             m_oMapGeomFieldDefnToSRSName[poGeomFieldDefn] != pszSRSName)
    3188             :         {
    3189           2 :             bool bReprojectionOK = false;
    3190           4 :             OGRSpatialReference oSRS;
    3191           2 :             if (oSRS.SetFromUserInput(
    3192             :                     pszSRSName,
    3193             :                     OGRSpatialReference::
    3194           2 :                         SET_FROM_USER_INPUT_LIMITATIONS_get()) == OGRERR_NONE)
    3195             :             {
    3196             :                 auto poCT = std::unique_ptr<OGRCoordinateTransformation>(
    3197             :                     OGRCreateCoordinateTransformation(
    3198           2 :                         &oSRS, poGeomFieldDefn->GetSpatialRef()));
    3199           1 :                 if (poCT != nullptr)
    3200             :                 {
    3201           1 :                     bReprojectionOK =
    3202           1 :                         (poGeom->transform(poCT.get()) == OGRERR_NONE);
    3203             :                 }
    3204             :             }
    3205           2 :             if (!bReprojectionOK)
    3206             :             {
    3207           1 :                 CPLError(CE_Warning, CPLE_AppDefined,
    3208             :                          "Reprojection from %s to %s failed", pszSRSName,
    3209           1 :                          m_oMapGeomFieldDefnToSRSName[poGeomFieldDefn].c_str());
    3210           1 :                 poGeom.reset();
    3211             :             }
    3212             : #ifdef DEBUG_VERBOSE
    3213             :             else
    3214             :             {
    3215             :                 CPLDebug("GMLAS", "Reprojected geometry from %s to %s",
    3216             :                          pszSRSName,
    3217             :                          m_oMapGeomFieldDefnToSRSName[poGeomFieldDefn].c_str());
    3218             :             }
    3219             : #endif
    3220             :         }
    3221             : 
    3222         192 :         if (poGeom != nullptr)
    3223             :         {
    3224             :             // Deal with possibly repeated geometries by building
    3225             :             // a geometry collection. We could also create a
    3226             :             // nested table, but that would probably be less
    3227             :             // convenient to use.
    3228             :             auto poPrevGeom = std::unique_ptr<OGRGeometry>(
    3229         382 :                 m_oCurCtxt.m_poFeature->StealGeometry(m_nCurGeomFieldIdx));
    3230         191 :             if (poPrevGeom != nullptr)
    3231             :             {
    3232          25 :                 if (poPrevGeom->getGeometryType() == wkbGeometryCollection)
    3233             :                 {
    3234          24 :                     poPrevGeom->toGeometryCollection()->addGeometryDirectly(
    3235          12 :                         poGeom.release());
    3236          12 :                     poGeom = std::move(poPrevGeom);
    3237             :                 }
    3238             :                 else
    3239             :                 {
    3240          26 :                     auto poGC = std::make_unique<OGRGeometryCollection>();
    3241          13 :                     poGC->addGeometryDirectly(poPrevGeom.release());
    3242          13 :                     poGC->addGeometryDirectly(poGeom.release());
    3243          13 :                     poGeom = std::move(poGC);
    3244             :                 }
    3245             :             }
    3246         191 :             poGeom->assignSpatialReference(poGeomFieldDefn->GetSpatialRef());
    3247         191 :             m_oCurCtxt.m_poFeature->SetGeomFieldDirectly(m_nCurGeomFieldIdx,
    3248             :                                                          poGeom.release());
    3249             :         }
    3250             :     }
    3251             :     else
    3252             :     {
    3253           0 :         char *pszXML = CPLSerializeXMLTree(psRoot);
    3254           0 :         CPLDebug("GMLAS", "Non-recognized geometry: %s", pszXML);
    3255           0 :         CPLFree(pszXML);
    3256             :     }
    3257             : }
    3258             : 
    3259             : /************************************************************************/
    3260             : /*                              characters()                            */
    3261             : /************************************************************************/
    3262             : 
    3263      290157 : void GMLASReader::characters(const XMLCh *const chars, const XMLSize_t length)
    3264             : {
    3265      290157 :     bool bTextMemberUpdated = false;
    3266        5693 :     if (((m_bIsXMLBlob && m_nCurGeomFieldIdx >= 0 && !m_bInitialPass) ||
    3267      296307 :          m_nSWEDataArrayLevel >= 0 || m_nSWEDataRecordLevel >= 0) &&
    3268             :         // Check the stack is not empty in case of space chars before the
    3269             :         // starting node
    3270         711 :         !m_apsXMLNodeStack.empty())
    3271             :     {
    3272         699 :         bTextMemberUpdated = true;
    3273             :         const CPLString &osText(
    3274         699 :             transcode(chars, m_osText, static_cast<int>(length)));
    3275             : 
    3276             :         // Merge content in current text node if it exists
    3277         699 :         NodeLastChild &sNodeLastChild = m_apsXMLNodeStack.back();
    3278         699 :         if (sNodeLastChild.psLastChild != nullptr &&
    3279         374 :             sNodeLastChild.psLastChild->eType == CXT_Text)
    3280             :         {
    3281          20 :             CPLXMLNode *psNode = sNodeLastChild.psLastChild;
    3282          20 :             const size_t nOldLength = strlen(psNode->pszValue);
    3283             :             char *pszNewValue = reinterpret_cast<char *>(
    3284          20 :                 VSIRealloc(psNode->pszValue, nOldLength + osText.size() + 1));
    3285          20 :             if (pszNewValue)
    3286             :             {
    3287          20 :                 psNode->pszValue = pszNewValue;
    3288          20 :                 memcpy(pszNewValue + nOldLength, osText.c_str(),
    3289          20 :                        osText.size() + 1);
    3290             :             }
    3291             :             else
    3292             :             {
    3293           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory");
    3294           0 :                 m_bParsingError = true;
    3295          20 :             }
    3296             :         }
    3297             :         // Otherwise create a new text node
    3298             :         else
    3299             :         {
    3300             :             CPLXMLNode *psNode =
    3301         679 :                 reinterpret_cast<CPLXMLNode *>(CPLMalloc(sizeof(CPLXMLNode)));
    3302         679 :             psNode->eType = CXT_Text;
    3303         679 :             psNode->pszValue =
    3304         679 :                 reinterpret_cast<char *>(CPLMalloc(osText.size() + 1));
    3305         679 :             memcpy(psNode->pszValue, osText.c_str(), osText.size() + 1);
    3306         679 :             psNode->psNext = nullptr;
    3307         679 :             psNode->psChild = nullptr;
    3308         679 :             AttachAsLastChild(psNode);
    3309             :         }
    3310             :     }
    3311             : 
    3312      290157 :     if (!FillTextContent())
    3313             :     {
    3314       90940 :         m_osTextContent = "1";  // dummy
    3315       90940 :         return;
    3316             :     }
    3317             : 
    3318      199217 :     if (m_bIsXMLBlob)
    3319             :     {
    3320        3894 :         if (m_nCurFieldIdx >= 0)
    3321             :         {
    3322             :             const CPLString &osText(
    3323             :                 bTextMemberUpdated
    3324        3894 :                     ? m_osText
    3325        3592 :                     : transcode(chars, m_osText, static_cast<int>(length)));
    3326             : 
    3327        3894 :             char *pszEscaped = CPLEscapeString(
    3328        3894 :                 osText.c_str(), static_cast<int>(osText.size()), CPLES_XML);
    3329             :             try
    3330             :             {
    3331        3894 :                 m_osTextContent += pszEscaped;
    3332             :             }
    3333           0 :             catch (const std::bad_alloc &)
    3334             :             {
    3335           0 :                 CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory");
    3336           0 :                 m_bParsingError = true;
    3337             :             }
    3338        3894 :             CPLFree(pszEscaped);
    3339             :         }
    3340             :     }
    3341             :     // Make sure to set content only if we are at the expected nesting level
    3342      195323 :     else if (m_nLevel == m_nCurFieldLevel)
    3343             :     {
    3344             :         const CPLString &osText(
    3345      100795 :             transcode(chars, m_osText, static_cast<int>(length)));
    3346             :         try
    3347             :         {
    3348      100795 :             m_osTextContent += osText;
    3349             :         }
    3350           0 :         catch (const std::bad_alloc &)
    3351             :         {
    3352           0 :             CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory");
    3353           0 :             m_bParsingError = true;
    3354             :         }
    3355             :     }
    3356             : 
    3357      199217 :     if (m_osTextContent.size() > m_nMaxContentSize)
    3358             :     {
    3359           0 :         CPLError(CE_Failure, CPLE_OutOfMemory,
    3360             :                  "Too much data in a single element");
    3361           0 :         m_bParsingError = true;
    3362             :     }
    3363             : }
    3364             : 
    3365             : /************************************************************************/
    3366             : /*                            GetNextFeature()                          */
    3367             : /************************************************************************/
    3368             : 
    3369        5417 : OGRFeature *GMLASReader::GetNextFeature(OGRGMLASLayer **ppoBelongingLayer,
    3370             :                                         GDALProgressFunc pfnProgress,
    3371             :                                         void *pProgressData)
    3372             : {
    3373        5417 :     while (!m_aoFeaturesReady.empty())
    3374             :     {
    3375           3 :         auto poFeatureReady = std::move(m_aoFeaturesReady.front().first);
    3376           3 :         OGRGMLASLayer *poFeatureReadyLayer = m_aoFeaturesReady.front().second;
    3377           3 :         m_aoFeaturesReady.erase(m_aoFeaturesReady.begin());
    3378             : 
    3379           3 :         if (m_poLayerOfInterest == nullptr ||
    3380           3 :             m_poLayerOfInterest == poFeatureReadyLayer)
    3381             :         {
    3382           3 :             if (ppoBelongingLayer)
    3383           0 :                 *ppoBelongingLayer = poFeatureReadyLayer;
    3384           3 :             return poFeatureReady.release();
    3385             :         }
    3386             :     }
    3387             : 
    3388        5414 :     if (m_bEOF)
    3389           0 :         return nullptr;
    3390             : 
    3391             :     try
    3392             :     {
    3393        5414 :         if (m_bFirstIteration)
    3394             :         {
    3395        1239 :             m_bFirstIteration = false;
    3396        2478 :             if (!m_poSAXReader->parseFirst(*(m_GMLInputSource.get()),
    3397        1239 :                                            m_oToFill))
    3398             :             {
    3399           0 :                 m_bParsingError = true;
    3400           0 :                 m_bEOF = true;
    3401           0 :                 return nullptr;
    3402             :             }
    3403             :         }
    3404             : 
    3405        5414 :         vsi_l_offset nLastOffset = m_fp->Tell();
    3406      617650 :         while (m_poSAXReader->parseNext(m_oToFill))
    3407             :         {
    3408      616717 :             if (pfnProgress && m_fp->Tell() - nLastOffset > 100 * 1024)
    3409             :             {
    3410           0 :                 nLastOffset = m_fp->Tell();
    3411           0 :                 double dfPct = -1;
    3412           0 :                 if (m_nFileSize)
    3413           0 :                     dfPct = 1.0 * nLastOffset / m_nFileSize;
    3414           0 :                 if (!pfnProgress(dfPct, "", pProgressData))
    3415             :                 {
    3416           0 :                     m_bInterrupted = true;
    3417           0 :                     break;
    3418             :                 }
    3419             :             }
    3420      616717 :             if (m_bParsingError)
    3421           0 :                 break;
    3422             : 
    3423      668800 :             while (!m_aoFeaturesReady.empty())
    3424             :             {
    3425             :                 auto poFeatureReady =
    3426       56564 :                     std::move(m_aoFeaturesReady.front().first);
    3427             :                 OGRGMLASLayer *poFeatureReadyLayer =
    3428       56564 :                     m_aoFeaturesReady.front().second;
    3429       56564 :                 m_aoFeaturesReady.erase(m_aoFeaturesReady.begin());
    3430             : 
    3431       56564 :                 if (m_poLayerOfInterest == nullptr ||
    3432       54126 :                     m_poLayerOfInterest == poFeatureReadyLayer)
    3433             :                 {
    3434        4481 :                     if (ppoBelongingLayer)
    3435        2438 :                         *ppoBelongingLayer = poFeatureReadyLayer;
    3436             : 
    3437        4481 :                     if (pfnProgress)
    3438             :                     {
    3439           0 :                         nLastOffset = m_fp->Tell();
    3440           0 :                         double dfPct = -1;
    3441           0 :                         if (m_nFileSize)
    3442           0 :                             dfPct = 1.0 * nLastOffset / m_nFileSize;
    3443           0 :                         if (!pfnProgress(dfPct, "", pProgressData))
    3444             :                         {
    3445           0 :                             m_bInterrupted = true;
    3446           0 :                             m_bEOF = true;
    3447           0 :                             return nullptr;
    3448             :                         }
    3449             :                     }
    3450             : 
    3451        4481 :                     return poFeatureReady.release();
    3452             :                 }
    3453             :             }
    3454             :         }
    3455             : 
    3456         931 :         m_bEOF = true;
    3457             :     }
    3458           0 :     catch (const XMLException &toCatch)
    3459             :     {
    3460           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
    3461           0 :                  transcode(toCatch.getMessage()).c_str());
    3462           0 :         m_bParsingError = true;
    3463           0 :         m_bEOF = true;
    3464             :     }
    3465           2 :     catch (const SAXException &toCatch)
    3466             :     {
    3467           2 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
    3468           4 :                  transcode(toCatch.getMessage()).c_str());
    3469           2 :         m_bParsingError = true;
    3470           2 :         m_bEOF = true;
    3471             :     }
    3472             : 
    3473         933 :     return nullptr;
    3474             : }
    3475             : 
    3476             : /************************************************************************/
    3477             : /*                              RunFirstPass()                          */
    3478             : /************************************************************************/
    3479             : 
    3480         112 : bool GMLASReader::RunFirstPass(
    3481             :     GDALProgressFunc pfnProgress, void *pProgressData, bool bRemoveUnusedLayers,
    3482             :     bool bRemoveUnusedFields, bool bProcessSWEDataArray,
    3483             :     OGRLayer *poFieldsMetadataLayer, OGRLayer *poLayersMetadataLayer,
    3484             :     OGRLayer *poRelationshipsLayer, std::set<CPLString> &aoSetRemovedLayerNames)
    3485             : {
    3486         112 :     m_bInitialPass = true;
    3487         112 :     m_bProcessSWEDataArray = bProcessSWEDataArray;
    3488         112 :     m_poFieldsMetadataLayer = poFieldsMetadataLayer;
    3489         112 :     m_poLayersMetadataLayer = poLayersMetadataLayer;
    3490         112 :     m_poRelationshipsLayer = poRelationshipsLayer;
    3491             : 
    3492             :     // Store in m_oSetGeomFieldsWithUnknownSRS the geometry fields
    3493         224 :     std::set<OGRGMLASLayer *> oSetUnreferencedLayers;
    3494         112 :     std::map<OGRGMLASLayer *, std::set<CPLString>> oMapUnusedFields;
    3495       14304 :     for (auto &poLayer : *m_apoLayers)
    3496             :     {
    3497       14192 :         OGRFeatureDefn *poFDefn = poLayer->GetLayerDefn();
    3498       14192 :         oSetUnreferencedLayers.insert(poLayer.get());
    3499       14416 :         for (int j = 0; j < poFDefn->GetGeomFieldCount(); j++)
    3500             :         {
    3501         224 :             m_oSetGeomFieldsWithUnknownSRS.insert(poFDefn->GetGeomFieldDefn(j));
    3502             :         }
    3503      214618 :         for (int j = 0; j < poFDefn->GetFieldCount(); j++)
    3504             :         {
    3505      200426 :             oMapUnusedFields[poLayer.get()].insert(
    3506      200426 :                 poFDefn->GetFieldDefn(j)->GetNameRef());
    3507             :         }
    3508             :     }
    3509             : 
    3510         112 :     CPLDebug("GMLAS", "Start of first pass");
    3511             : 
    3512             :     // Do we need to do a full scan of the file ?
    3513             :     const bool bHasURLSpecificRules =
    3514         112 :         !m_oXLinkResolver.GetConf().m_aoURLSpecificRules.empty();
    3515             :     const bool bDoFullPass =
    3516          62 :         (m_bValidate || bRemoveUnusedLayers || bRemoveUnusedFields ||
    3517         230 :          bHasURLSpecificRules || bProcessSWEDataArray ||
    3518         168 :          m_oXLinkResolver.GetConf().m_bResolveInternalXLinks);
    3519             : 
    3520             :     // Loop on features until we have determined the SRS of all geometry
    3521             :     // columns, or potentially on the whole file for the above reasons
    3522             :     OGRGMLASLayer *poLayerFeature;
    3523        1770 :     while (bDoFullPass || !m_oSetGeomFieldsWithUnknownSRS.empty())
    3524             :     {
    3525             :         auto poFeature = std::unique_ptr<OGRFeature>(
    3526        1770 :             GetNextFeature(&poLayerFeature, pfnProgress, pProgressData));
    3527        1770 :         if (!poFeature)
    3528         112 :             break;
    3529        1658 :         if (bRemoveUnusedLayers)
    3530           1 :             oSetUnreferencedLayers.erase(poLayerFeature);
    3531        1658 :         if (bRemoveUnusedFields)
    3532             :         {
    3533             :             std::set<CPLString> &oSetUnusedFields =
    3534           1 :                 oMapUnusedFields[poLayerFeature];
    3535           1 :             OGRFeatureDefn *poFDefn = poLayerFeature->GetLayerDefn();
    3536           1 :             int nFieldCount = poFDefn->GetFieldCount();
    3537           9 :             for (int j = 0; j < nFieldCount; j++)
    3538             :             {
    3539           8 :                 if (poFeature->IsFieldSetAndNotNull(j))
    3540           4 :                     oSetUnusedFields.erase(
    3541           4 :                         poFDefn->GetFieldDefn(j)->GetNameRef());
    3542             :             }
    3543             :         }
    3544             :     }
    3545             : 
    3546         112 :     CPLDebug("GMLAS", "End of first pass");
    3547             : 
    3548         112 :     ProcessInternalXLinkFirstPass(bRemoveUnusedFields, oMapUnusedFields);
    3549             : 
    3550         112 :     if (bRemoveUnusedLayers)
    3551             :     {
    3552           2 :         std::vector<std::unique_ptr<OGRGMLASLayer>> apoNewLayers;
    3553           5 :         for (auto &poLayer : *m_apoLayers)
    3554             :         {
    3555           4 :             if (oSetUnreferencedLayers.find(poLayer.get()) ==
    3556           8 :                 oSetUnreferencedLayers.end())
    3557             :             {
    3558           1 :                 apoNewLayers.emplace_back(std::move(poLayer));
    3559             :             }
    3560             :             else
    3561             :             {
    3562           3 :                 aoSetRemovedLayerNames.insert(poLayer->GetName());
    3563             :             }
    3564             :         }
    3565           1 :         *m_apoLayers = std::move(apoNewLayers);
    3566             :     }
    3567         112 :     if (bRemoveUnusedFields)
    3568             :     {
    3569           2 :         for (auto &poLayer : *m_apoLayers)
    3570             :         {
    3571           5 :             for (const auto &oIter : oMapUnusedFields[poLayer.get()])
    3572             :             {
    3573           8 :                 poLayer->RemoveField(
    3574           4 :                     poLayer->GetLayerDefn()->GetFieldIndex(oIter));
    3575             :             }
    3576             : 
    3577             :             // We need to run this again since we may have delete the
    3578             :             // element that holds attributes, like in
    3579             :             // <foo xsi:nil="true" nilReason="unknown"/> where foo will be
    3580             :             // eliminated, but foo_nilReason kept.
    3581           1 :             poLayer->CreateCompoundFoldedMappings();
    3582             :         }
    3583             :     }
    3584             : 
    3585             :     // Add fields coming from matching URL specific rules
    3586         112 :     if (bHasURLSpecificRules)
    3587             :     {
    3588           3 :         CreateFieldsForURLSpecificRules();
    3589             :     }
    3590             : 
    3591             :     // Clear the set even if we didn't manage to determine all the SRS
    3592         112 :     m_oSetGeomFieldsWithUnknownSRS.clear();
    3593             : 
    3594         224 :     return !m_bInterrupted;
    3595             : }
    3596             : 
    3597             : /************************************************************************/
    3598             : /*                    ProcessInternalXLinkFirstPass()                   */
    3599             : /************************************************************************/
    3600             : 
    3601         112 : void GMLASReader::ProcessInternalXLinkFirstPass(
    3602             :     bool bRemoveUnusedFields,
    3603             :     std::map<OGRGMLASLayer *, std::set<CPLString>> &oMapUnusedFields)
    3604             : {
    3605         118 :     for (const auto &oIter : m_oMapFieldXPathToLinkValue)
    3606             :     {
    3607           6 :         OGRGMLASLayer *poReferringLayer = oIter.first.first;
    3608           6 :         const CPLString &osReferringField = oIter.first.second;
    3609           6 :         const std::vector<CPLString> &aosLinks = oIter.second;
    3610          12 :         std::set<OGRGMLASLayer *> oSetTargetLayers;
    3611          15 :         for (size_t i = 0; i < aosLinks.size(); i++)
    3612             :         {
    3613           9 :             const auto oIter2 = m_oMapElementIdToLayer.find(aosLinks[i]);
    3614           9 :             if (oIter2 == m_oMapElementIdToLayer.end())
    3615             :             {
    3616           4 :                 CPLError(CE_Warning, CPLE_AppDefined,
    3617             :                          "%s:%s = '#%s' has no corresponding target "
    3618             :                          "element in this document",
    3619             :                          poReferringLayer->GetName(), osReferringField.c_str(),
    3620           4 :                          aosLinks[i].c_str());
    3621             :             }
    3622           5 :             else if (oSetTargetLayers.find(oIter2->second) ==
    3623          10 :                      oSetTargetLayers.end())
    3624             :             {
    3625           4 :                 OGRGMLASLayer *poTargetLayer = oIter2->second;
    3626           4 :                 oSetTargetLayers.insert(poTargetLayer);
    3627             :                 CPLString osLinkFieldName =
    3628             :                     poReferringLayer->CreateLinkForAttrToOtherLayer(
    3629             :                         osReferringField,
    3630           8 :                         poTargetLayer->GetFeatureClass().GetXPath());
    3631           4 :                 if (bRemoveUnusedFields)
    3632             :                 {
    3633           0 :                     oMapUnusedFields[poReferringLayer].erase(osLinkFieldName);
    3634             :                 }
    3635             :             }
    3636             :         }
    3637             :     }
    3638         112 : }
    3639             : 
    3640             : /************************************************************************/
    3641             : /*                    CreateFieldsForURLSpecificRules()                 */
    3642             : /************************************************************************/
    3643             : 
    3644           3 : void GMLASReader::CreateFieldsForURLSpecificRules()
    3645             : {
    3646           6 :     for (const auto &oIter : m_oMapXLinkFields)
    3647             :     {
    3648           3 :         OGRGMLASLayer *poLayer = oIter.first;
    3649           3 :         const auto &oMap2 = oIter.second;
    3650           8 :         for (const auto &oIter2 : oMap2)
    3651             :         {
    3652           5 :             const CPLString &osFieldXPath(oIter2.first);
    3653             :             // Note that CreateFieldsForURLSpecificRule() running on a previous
    3654             :             // iteration will have inserted new OGR fields, so we really need
    3655             :             // to compute that index now.
    3656             :             const int nFieldIdx =
    3657           5 :                 poLayer->GetOGRFieldIndexFromXPath(osFieldXPath);
    3658           5 :             CPLAssert(nFieldIdx >= 0);
    3659           5 :             int nInsertFieldIdx = nFieldIdx + 1;
    3660           5 :             const auto &oSetRuleIndex = oIter2.second;
    3661          12 :             for (const auto &nRuleIdx : oSetRuleIndex)
    3662             :             {
    3663             :                 const GMLASXLinkResolutionConf::URLSpecificResolution &oRule =
    3664           7 :                     m_oXLinkResolver.GetConf().m_aoURLSpecificRules[nRuleIdx];
    3665           7 :                 CreateFieldsForURLSpecificRule(poLayer, nFieldIdx, osFieldXPath,
    3666             :                                                nInsertFieldIdx, oRule);
    3667             :             }
    3668             :         }
    3669             :     }
    3670           3 : }
    3671             : 
    3672             : /************************************************************************/
    3673             : /*                    CreateFieldsForURLSpecificRule()                  */
    3674             : /************************************************************************/
    3675             : 
    3676           7 : void GMLASReader::CreateFieldsForURLSpecificRule(
    3677             :     OGRGMLASLayer *poLayer, int nFieldIdx, const CPLString &osFieldXPath,
    3678             :     int &nInsertFieldIdx,
    3679             :     const GMLASXLinkResolutionConf::URLSpecificResolution &oRule)
    3680             : {
    3681           7 :     if (oRule.m_eResolutionMode == GMLASXLinkResolutionConf::RawContent)
    3682             :     {
    3683             :         const CPLString osRawContentXPath(
    3684             :             GMLASField::MakeXLinkRawContentFieldXPathFromXLinkHrefXPath(
    3685           2 :                 osFieldXPath));
    3686           1 :         if (poLayer->GetOGRFieldIndexFromXPath(osRawContentXPath) < 0)
    3687             :         {
    3688             :             CPLString osRawContentFieldname(
    3689           2 :                 poLayer->GetLayerDefn()->GetFieldDefn(nFieldIdx)->GetNameRef());
    3690           1 :             size_t nPos = osRawContentFieldname.find("_href");
    3691           1 :             if (nPos != std::string::npos)
    3692           1 :                 osRawContentFieldname.resize(nPos);
    3693           1 :             osRawContentFieldname += "_rawcontent";
    3694           1 :             OGRFieldDefn oFieldDefnRaw(osRawContentFieldname, OFTString);
    3695           1 :             poLayer->InsertNewField(nInsertFieldIdx, oFieldDefnRaw,
    3696             :                                     osRawContentXPath);
    3697           1 :             nInsertFieldIdx++;
    3698             :         }
    3699             :     }
    3700           6 :     else if (oRule.m_eResolutionMode ==
    3701             :              GMLASXLinkResolutionConf::FieldsFromXPath)
    3702             :     {
    3703          26 :         for (size_t i = 0; i < oRule.m_aoFields.size(); ++i)
    3704             :         {
    3705             :             const CPLString osDerivedFieldXPath(
    3706             :                 GMLASField::MakeXLinkDerivedFieldXPathFromXLinkHrefXPath(
    3707          40 :                     osFieldXPath, oRule.m_aoFields[i].m_osName));
    3708          20 :             if (poLayer->GetOGRFieldIndexFromXPath(osDerivedFieldXPath) < 0)
    3709             :             {
    3710          18 :                 CPLString osNewFieldname(poLayer->GetLayerDefn()
    3711          18 :                                              ->GetFieldDefn(nFieldIdx)
    3712          36 :                                              ->GetNameRef());
    3713          18 :                 size_t nPos = osNewFieldname.find("_href");
    3714          18 :                 if (nPos != std::string::npos)
    3715          18 :                     osNewFieldname.resize(nPos);
    3716          18 :                 osNewFieldname += "_" + oRule.m_aoFields[i].m_osName;
    3717             : 
    3718          18 :                 OGRFieldType eType = OFTString;
    3719          18 :                 const CPLString &osType(oRule.m_aoFields[i].m_osType);
    3720          18 :                 if (osType == "integer")
    3721           6 :                     eType = OFTInteger;
    3722          12 :                 else if (osType == "long")
    3723           2 :                     eType = OFTInteger64;
    3724          10 :                 else if (osType == "double")
    3725           2 :                     eType = OFTReal;
    3726           8 :                 else if (osType == "dateTime")
    3727           2 :                     eType = OFTDateTime;
    3728             : 
    3729          18 :                 OGRFieldDefn oFieldDefnRaw(osNewFieldname, eType);
    3730          18 :                 poLayer->InsertNewField(nInsertFieldIdx, oFieldDefnRaw,
    3731             :                                         osDerivedFieldXPath);
    3732          18 :                 nInsertFieldIdx++;
    3733             :             }
    3734             :         }
    3735             :     }
    3736           7 : }

Generated by: LCOV version 1.14