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

Generated by: LCOV version 1.14