LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gmlas - ogrgmlasreader.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1439 1560 92.2 %
Date: 2024-05-06 16:27:07 Functions: 54 57 94.7 %

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

Generated by: LCOV version 1.14