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

Generated by: LCOV version 1.14