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

Generated by: LCOV version 1.14