LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gmlas - ogrgmlasdatasource.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 643 671 95.8 %
Date: 2025-03-29 15:49:56 Functions: 27 27 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  * Project:  OGR
       3             :  * Purpose:  OGRGMLASDriver implementation
       4             :  * Author:   Even Rouault, <even dot rouault at spatialys dot com>
       5             :  *
       6             :  * Initial development funded by the European Earth observation programme
       7             :  * Copernicus
       8             :  *
       9             :  ******************************************************************************
      10             :  * Copyright (c) 2016, Even Rouault, <even dot rouault at spatialys dot com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #include "ogr_gmlas.h"
      16             : 
      17             : #include "ogr_mem.h"
      18             : #include "cpl_sha256.h"
      19             : 
      20             : #include <algorithm>
      21             : 
      22             : /************************************************************************/
      23             : /*                 XercesInitializer::XercesInitializer()               */
      24             : /************************************************************************/
      25             : 
      26         194 : OGRGMLASDataSource::XercesInitializer::XercesInitializer()
      27             : {
      28         194 :     OGRInitializeXerces();
      29         194 : }
      30             : 
      31             : /************************************************************************/
      32             : /*                 XercesInitializer::~XercesInitializer()              */
      33             : /************************************************************************/
      34             : 
      35         194 : OGRGMLASDataSource::XercesInitializer::~XercesInitializer()
      36             : {
      37         194 :     OGRDeinitializeXerces();
      38         194 : }
      39             : 
      40             : /************************************************************************/
      41             : /*                          OGRGMLASDataSource()                        */
      42             : /************************************************************************/
      43             : 
      44         194 : OGRGMLASDataSource::OGRGMLASDataSource()
      45         194 :     : m_poFieldsMetadataLayer(std::make_unique<OGRMemLayer>(
      46           0 :           szOGR_FIELDS_METADATA, nullptr, wkbNone)),
      47         194 :       m_poLayersMetadataLayer(std::make_unique<OGRMemLayer>(
      48           0 :           szOGR_LAYERS_METADATA, nullptr, wkbNone)),
      49         194 :       m_poRelationshipsLayer(std::make_unique<OGRMemLayer>(
      50           0 :           szOGR_LAYER_RELATIONSHIPS, nullptr, wkbNone)),
      51             :       m_poOtherMetadataLayer(
      52         776 :           std::make_unique<OGRMemLayer>(szOGR_OTHER_METADATA, nullptr, wkbNone))
      53             : {
      54             :     // Initialize m_poFieldsMetadataLayer
      55             :     {
      56         388 :         OGRFieldDefn oFieldDefn(szLAYER_NAME, OFTString);
      57         194 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      58             :     }
      59             :     {
      60         388 :         OGRFieldDefn oFieldDefn(szFIELD_INDEX, OFTInteger);
      61         194 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      62             :     }
      63             :     {
      64         388 :         OGRFieldDefn oFieldDefn(szFIELD_NAME, OFTString);
      65         194 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      66             :     }
      67             :     {
      68         388 :         OGRFieldDefn oFieldDefn(szFIELD_XPATH, OFTString);
      69         194 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      70             :     }
      71             :     {
      72         388 :         OGRFieldDefn oFieldDefn(szFIELD_TYPE, OFTString);
      73         194 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      74             :     }
      75             :     {
      76         388 :         OGRFieldDefn oFieldDefn(szFIELD_IS_LIST, OFTInteger);
      77         194 :         oFieldDefn.SetSubType(OFSTBoolean);
      78         194 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      79             :     }
      80             :     {
      81         388 :         OGRFieldDefn oFieldDefn(szFIELD_MIN_OCCURS, OFTInteger);
      82         194 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      83             :     }
      84             :     {
      85         388 :         OGRFieldDefn oFieldDefn(szFIELD_MAX_OCCURS, OFTInteger);
      86         194 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      87             :     }
      88             :     {
      89         388 :         OGRFieldDefn oFieldDefn(szFIELD_REPETITION_ON_SEQUENCE, OFTInteger);
      90         194 :         oFieldDefn.SetSubType(OFSTBoolean);
      91         194 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      92             :     }
      93             :     {
      94         388 :         OGRFieldDefn oFieldDefn(szFIELD_DEFAULT_VALUE, OFTString);
      95         194 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      96             :     }
      97             :     {
      98         388 :         OGRFieldDefn oFieldDefn(szFIELD_FIXED_VALUE, OFTString);
      99         194 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
     100             :     }
     101             :     {
     102         388 :         OGRFieldDefn oFieldDefn(szFIELD_CATEGORY, OFTString);
     103         194 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
     104             :     }
     105             :     {
     106         388 :         OGRFieldDefn oFieldDefn(szFIELD_RELATED_LAYER, OFTString);
     107         194 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
     108             :     }
     109             :     {
     110         388 :         OGRFieldDefn oFieldDefn(szFIELD_JUNCTION_LAYER, OFTString);
     111         194 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
     112             :     }
     113             :     {
     114         388 :         OGRFieldDefn oFieldDefn(szFIELD_DOCUMENTATION, OFTString);
     115         194 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
     116             :     }
     117             : 
     118             :     // Initialize m_poLayersMetadataLayer
     119             :     {
     120         388 :         OGRFieldDefn oFieldDefn(szLAYER_NAME, OFTString);
     121         194 :         m_poLayersMetadataLayer->CreateField(&oFieldDefn);
     122             :     }
     123             :     {
     124         388 :         OGRFieldDefn oFieldDefn(szLAYER_XPATH, OFTString);
     125         194 :         m_poLayersMetadataLayer->CreateField(&oFieldDefn);
     126             :     }
     127             :     {
     128         388 :         OGRFieldDefn oFieldDefn(szLAYER_CATEGORY, OFTString);
     129         194 :         m_poLayersMetadataLayer->CreateField(&oFieldDefn);
     130             :     }
     131             :     {
     132         388 :         OGRFieldDefn oFieldDefn(szLAYER_PKID_NAME, OFTString);
     133         194 :         m_poLayersMetadataLayer->CreateField(&oFieldDefn);
     134             :     }
     135             :     {
     136         388 :         OGRFieldDefn oFieldDefn(szLAYER_PARENT_PKID_NAME, OFTString);
     137         194 :         m_poLayersMetadataLayer->CreateField(&oFieldDefn);
     138             :     }
     139             :     {
     140         388 :         OGRFieldDefn oFieldDefn(szLAYER_DOCUMENTATION, OFTString);
     141         194 :         m_poLayersMetadataLayer->CreateField(&oFieldDefn);
     142             :     }
     143             : 
     144             :     // Initialize m_poRelationshipsLayer
     145             :     {
     146         388 :         OGRFieldDefn oFieldDefn(szPARENT_LAYER, OFTString);
     147         194 :         m_poRelationshipsLayer->CreateField(&oFieldDefn);
     148             :     }
     149             :     {
     150         388 :         OGRFieldDefn oFieldDefn(szPARENT_PKID, OFTString);
     151         194 :         m_poRelationshipsLayer->CreateField(&oFieldDefn);
     152             :     }
     153             :     {
     154         388 :         OGRFieldDefn oFieldDefn(szPARENT_ELEMENT_NAME, OFTString);
     155         194 :         m_poRelationshipsLayer->CreateField(&oFieldDefn);
     156             :     }
     157             :     {
     158         388 :         OGRFieldDefn oFieldDefn(szCHILD_LAYER, OFTString);
     159         194 :         m_poRelationshipsLayer->CreateField(&oFieldDefn);
     160             :     }
     161             :     {
     162         388 :         OGRFieldDefn oFieldDefn(szCHILD_PKID, OFTString);
     163         194 :         m_poRelationshipsLayer->CreateField(&oFieldDefn);
     164             :     }
     165             : 
     166             :     // Initialize m_poOtherMetadataLayer
     167             :     {
     168         388 :         OGRFieldDefn oFieldDefn(szKEY, OFTString);
     169         194 :         m_poOtherMetadataLayer->CreateField(&oFieldDefn);
     170             :     }
     171             :     {
     172         388 :         OGRFieldDefn oFieldDefn(szVALUE, OFTString);
     173         194 :         m_poOtherMetadataLayer->CreateField(&oFieldDefn);
     174             :     }
     175         194 : }
     176             : 
     177             : /************************************************************************/
     178             : /*                        ~OGRGMLASDataSource()                         */
     179             : /************************************************************************/
     180             : 
     181         388 : OGRGMLASDataSource::~OGRGMLASDataSource()
     182             : {
     183         194 :     if (m_bUnlinkConfigFileAfterUse)
     184             :     {
     185           0 :         VSIUnlink(m_osConfigFile.c_str());
     186             :     }
     187         388 : }
     188             : 
     189             : /************************************************************************/
     190             : /*                            GetLayerCount()                           */
     191             : /************************************************************************/
     192             : 
     193        8513 : int OGRGMLASDataSource::GetLayerCount()
     194             : {
     195        8513 :     return static_cast<int>(m_apoLayers.size() +
     196        8513 :                             m_apoRequestedMetadataLayers.size());
     197             : }
     198             : 
     199             : /************************************************************************/
     200             : /*                                GetLayer()                            */
     201             : /************************************************************************/
     202             : 
     203        8906 : OGRLayer *OGRGMLASDataSource::GetLayer(int i)
     204             : {
     205        8906 :     const int nBaseLayers = static_cast<int>(m_apoLayers.size());
     206        8906 :     if (i >= nBaseLayers)
     207             :     {
     208         153 :         RunFirstPassIfNeeded(nullptr, nullptr, nullptr);
     209         153 :         if (i - nBaseLayers <
     210         153 :             static_cast<int>(m_apoRequestedMetadataLayers.size()))
     211         152 :             return m_apoRequestedMetadataLayers[i - nBaseLayers];
     212             :     }
     213             : 
     214        8754 :     if (i < 0 || i >= nBaseLayers)
     215           2 :         return nullptr;
     216        8752 :     return m_apoLayers[i].get();
     217             : }
     218             : 
     219             : /************************************************************************/
     220             : /*                             GetLayerByName()                         */
     221             : /************************************************************************/
     222             : 
     223         600 : OGRLayer *OGRGMLASDataSource::GetLayerByName(const char *pszName)
     224             : {
     225         600 :     if (OGRLayer *poLayer = GDALDataset::GetLayerByName(pszName))
     226         576 :         return poLayer;
     227             : 
     228             :     OGRLayer *apoLayers[] = {
     229          24 :         m_poFieldsMetadataLayer.get(), m_poLayersMetadataLayer.get(),
     230          24 :         m_poRelationshipsLayer.get(), m_poOtherMetadataLayer.get()};
     231          52 :     for (auto *poLayer : apoLayers)
     232             :     {
     233          51 :         if (EQUAL(pszName, poLayer->GetName()))
     234             :         {
     235          23 :             if (std::find(m_apoRequestedMetadataLayers.begin(),
     236             :                           m_apoRequestedMetadataLayers.end(),
     237          23 :                           poLayer) == m_apoRequestedMetadataLayers.end())
     238             :             {
     239          23 :                 m_apoRequestedMetadataLayers.push_back(poLayer);
     240             :             }
     241          23 :             RunFirstPassIfNeeded(nullptr, nullptr, nullptr);
     242          23 :             return poLayer;
     243             :         }
     244             :     }
     245             : 
     246           1 :     return nullptr;
     247             : }
     248             : 
     249             : /************************************************************************/
     250             : /*                           TranslateClasses()                         */
     251             : /************************************************************************/
     252             : 
     253       15421 : void OGRGMLASDataSource::TranslateClasses(OGRGMLASLayer *poParentLayer,
     254             :                                           const GMLASFeatureClass &oFC)
     255             : {
     256       15421 :     const std::vector<GMLASFeatureClass> &aoClasses = oFC.GetNestedClasses();
     257             : 
     258             :     // CPLDebug("GMLAS", "TranslateClasses(%s,%s)",
     259             :     //          oFC.GetName().c_str(), oFC.GetXPath().c_str());
     260             : 
     261       15421 :     m_apoLayers.emplace_back(std::make_unique<OGRGMLASLayer>(
     262       15421 :         this, oFC, poParentLayer, m_oConf.m_bAlwaysGenerateOGRId));
     263       15421 :     auto poLayer = m_apoLayers.back().get();
     264             : 
     265       18761 :     for (size_t i = 0; i < aoClasses.size(); ++i)
     266             :     {
     267        3340 :         TranslateClasses(poLayer, aoClasses[i]);
     268             :     }
     269       15421 : }
     270             : 
     271             : /************************************************************************/
     272             : /*                         GMLASTopElementParser                        */
     273             : /************************************************************************/
     274             : 
     275             : class GMLASTopElementParser : public DefaultHandler
     276             : {
     277             :     std::vector<PairURIFilename> m_aoFilenames{};
     278             :     int m_nStartElementCounter = 0;
     279             :     bool m_bFinish = false;
     280             :     bool m_bFoundSWE = false;
     281             :     std::map<CPLString, CPLString> m_oMapDocNSURIToPrefix{};
     282             : 
     283             :   public:
     284         189 :     GMLASTopElementParser() = default;
     285             : 
     286             :     void Parse(const CPLString &osFilename,
     287             :                const std::shared_ptr<VSIVirtualHandle> &fp);
     288             : 
     289         154 :     const std::vector<PairURIFilename> &GetXSDs() const
     290             :     {
     291         154 :         return m_aoFilenames;
     292             :     }
     293             : 
     294         157 :     bool GetSWE() const
     295             :     {
     296         157 :         return m_bFoundSWE;
     297             :     }
     298             : 
     299         157 :     const std::map<CPLString, CPLString> &GetMapDocNSURIToPrefix() const
     300             :     {
     301         157 :         return m_oMapDocNSURIToPrefix;
     302             :     }
     303             : 
     304             :     virtual void startElement(const XMLCh *const uri,
     305             :                               const XMLCh *const localname,
     306             :                               const XMLCh *const qname,
     307             :                               const Attributes &attrs) override;
     308             : };
     309             : 
     310             : /************************************************************************/
     311             : /*                               Parse()                                */
     312             : /************************************************************************/
     313             : 
     314         157 : void GMLASTopElementParser::Parse(const CPLString &osFilename,
     315             :                                   const std::shared_ptr<VSIVirtualHandle> &fp)
     316             : {
     317             :     auto poSAXReader =
     318         314 :         std::unique_ptr<SAX2XMLReader>(XMLReaderFactory::createXMLReader());
     319             : 
     320         157 :     poSAXReader->setFeature(XMLUni::fgSAX2CoreNameSpaces, true);
     321         157 :     poSAXReader->setFeature(XMLUni::fgSAX2CoreNameSpacePrefixes, true);
     322             : 
     323         157 :     poSAXReader->setContentHandler(this);
     324         157 :     poSAXReader->setLexicalHandler(this);
     325         157 :     poSAXReader->setDTDHandler(this);
     326             : 
     327         157 :     poSAXReader->setFeature(XMLUni::fgXercesLoadSchema, false);
     328             : 
     329         314 :     GMLASErrorHandler oErrorHandler;
     330         157 :     poSAXReader->setErrorHandler(&oErrorHandler);
     331             : 
     332         314 :     GMLASInputSource oIS(osFilename, fp);
     333             : 
     334             :     try
     335             :     {
     336         314 :         XMLPScanToken oToFill;
     337         157 :         if (poSAXReader->parseFirst(oIS, oToFill))
     338             :         {
     339         314 :             while (!m_bFinish && poSAXReader->parseNext(oToFill))
     340             :             {
     341             :                 // do nothing
     342             :             }
     343             :         }
     344             :     }
     345           0 :     catch (const XMLException &toCatch)
     346             :     {
     347           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     348           0 :                  transcode(toCatch.getMessage()).c_str());
     349             :     }
     350           0 :     catch (const SAXException &toCatch)
     351             :     {
     352           0 :         CPLError(CE_Failure, CPLE_AppDefined, "%s",
     353           0 :                  transcode(toCatch.getMessage()).c_str());
     354             :     }
     355         157 : }
     356             : 
     357             : /************************************************************************/
     358             : /*                             startElement()                           */
     359             : /************************************************************************/
     360             : 
     361         157 : void GMLASTopElementParser::startElement(const XMLCh *const /*uri*/,
     362             :                                          const XMLCh *const /*localname*/,
     363             :                                          const XMLCh *const /*qname*/,
     364             :                                          const Attributes &attrs)
     365             : {
     366         157 :     m_nStartElementCounter++;
     367             : 
     368         965 :     for (unsigned int i = 0; i < attrs.getLength(); i++)
     369             :     {
     370        1616 :         const std::string osAttrURIPrefix(transcode(attrs.getURI(i)));
     371        1616 :         const std::string osAttrLocalname(transcode(attrs.getLocalName(i)));
     372        1616 :         const std::string osAttrValue(transcode(attrs.getValue(i)));
     373             : 
     374         964 :         if (osAttrURIPrefix == szXSI_URI &&
     375         156 :             osAttrLocalname == szSCHEMA_LOCATION)
     376             :         {
     377         130 :             CPLDebug("GMLAS", "%s=%s", szSCHEMA_LOCATION, osAttrValue.c_str());
     378             : 
     379             :             const CPLStringList aosTokens(
     380         260 :                 CSLTokenizeString2(osAttrValue.c_str(), " ", 0));
     381         130 :             const int nTokens = aosTokens.size();
     382         130 :             if ((nTokens % 2) == 0)
     383             :             {
     384         343 :                 for (int j = 0; j < nTokens; j += 2)
     385             :                 {
     386         422 :                     if (!STARTS_WITH(aosTokens[j], szWFS_URI) &&
     387         209 :                         !(EQUAL(aosTokens[j], szGML_URI) ||
     388         422 :                           STARTS_WITH(aosTokens[j],
     389             :                                       (CPLString(szGML_URI) + "/").c_str())))
     390             :                     {
     391         209 :                         CPLDebug("GMLAS", "Schema to analyze: %s -> %s",
     392             :                                  aosTokens[j], aosTokens[j + 1]);
     393         209 :                         m_aoFilenames.push_back(
     394         418 :                             PairURIFilename(aosTokens[j], aosTokens[j + 1]));
     395             :                     }
     396             :                 }
     397             :             }
     398             :         }
     399         704 :         else if (osAttrURIPrefix == szXSI_URI &&
     400          26 :                  osAttrLocalname == szNO_NAMESPACE_SCHEMA_LOCATION)
     401             :         {
     402          26 :             CPLDebug("GMLAS", "%s=%s", szNO_NAMESPACE_SCHEMA_LOCATION,
     403             :                      osAttrValue.c_str());
     404          26 :             m_aoFilenames.push_back(PairURIFilename("", osAttrValue));
     405             :         }
     406         652 :         else if (osAttrURIPrefix == szXMLNS_URI && osAttrValue == szSWE_URI)
     407             :         {
     408           3 :             CPLDebug("GMLAS", "SWE namespace found");
     409           3 :             m_bFoundSWE = true;
     410             :         }
     411        1091 :         else if (osAttrURIPrefix == szXMLNS_URI && !osAttrValue.empty() &&
     412         442 :                  !osAttrLocalname.empty())
     413             :         {
     414             : #ifdef DEBUG_VERBOSE
     415             :             CPLDebug("GMLAS", "Namespace %s = %s", osAttrLocalname.c_str(),
     416             :                      osAttrValue.c_str());
     417             : #endif
     418         442 :             m_oMapDocNSURIToPrefix[osAttrValue] = osAttrLocalname;
     419             :         }
     420             :     }
     421             : 
     422         157 :     if (m_nStartElementCounter == 1)
     423         157 :         m_bFinish = true;
     424         157 : }
     425             : 
     426             : /************************************************************************/
     427             : /*                         FillOtherMetadataLayer()                     */
     428             : /************************************************************************/
     429             : 
     430         177 : void OGRGMLASDataSource::FillOtherMetadataLayer(
     431             :     GDALOpenInfo *poOpenInfo, const CPLString &osConfigFile,
     432             :     const std::vector<PairURIFilename> &aoXSDs,
     433             :     const std::set<CPLString> &oSetSchemaURLs)
     434             : {
     435             :     // 2 "secret" options just used for tests
     436         177 :     const bool bKeepRelativePathsForMetadata = CPLTestBool(
     437         177 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     438             :                              szKEEP_RELATIVE_PATHS_FOR_METADATA_OPTION, "NO"));
     439             : 
     440         177 :     const bool bExposeConfiguration = CPLTestBool(
     441         177 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     442             :                              szEXPOSE_CONFIGURATION_IN_METADATA_OPTION, "YES"));
     443             : 
     444         177 :     const bool bExposeSchemaNames = CPLTestBool(
     445         177 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     446             :                              szEXPOSE_SCHEMAS_NAME_IN_METADATA_OPTION, "YES"));
     447             : 
     448         177 :     OGRFeatureDefn *poFDefn = m_poOtherMetadataLayer->GetLayerDefn();
     449             : 
     450         177 :     if (!osConfigFile.empty() && bExposeConfiguration)
     451             :     {
     452         175 :         if (STARTS_WITH(osConfigFile, "<Configuration"))
     453             :         {
     454          85 :             OGRFeature oFeature(poFDefn);
     455          85 :             oFeature.SetField(szKEY, szCONFIGURATION_INLINED);
     456          85 :             oFeature.SetField(szVALUE, osConfigFile.c_str());
     457          85 :             CPL_IGNORE_RET_VAL(
     458          85 :                 m_poOtherMetadataLayer->CreateFeature(&oFeature));
     459             :         }
     460             :         else
     461             :         {
     462             :             {
     463          90 :                 OGRFeature oFeature(poFDefn);
     464          90 :                 oFeature.SetField(szKEY, szCONFIGURATION_FILENAME);
     465          90 :                 char *pszCurDir = CPLGetCurrentDir();
     466         270 :                 if (!bKeepRelativePathsForMetadata &&
     467          90 :                     CPLIsFilenameRelative(osConfigFile) && pszCurDir != nullptr)
     468             :                 {
     469           0 :                     oFeature.SetField(
     470             :                         szVALUE,
     471           0 :                         CPLFormFilenameSafe(pszCurDir, osConfigFile, nullptr)
     472             :                             .c_str());
     473             :                 }
     474             :                 else
     475             :                 {
     476          90 :                     oFeature.SetField(szVALUE, osConfigFile.c_str());
     477             :                 }
     478          90 :                 CPLFree(pszCurDir);
     479          90 :                 CPL_IGNORE_RET_VAL(
     480          90 :                     m_poOtherMetadataLayer->CreateFeature(&oFeature));
     481             :             }
     482             : 
     483          90 :             GByte *pabyRet = nullptr;
     484          90 :             if (VSIIngestFile(nullptr, osConfigFile, &pabyRet, nullptr, -1))
     485             :             {
     486          90 :                 OGRFeature oFeature(poFDefn);
     487          90 :                 oFeature.SetField(szKEY, szCONFIGURATION_INLINED);
     488          90 :                 oFeature.SetField(szVALUE, reinterpret_cast<char *>(pabyRet));
     489          90 :                 CPL_IGNORE_RET_VAL(
     490          90 :                     m_poOtherMetadataLayer->CreateFeature(&oFeature));
     491             :             }
     492          90 :             VSIFree(pabyRet);
     493             :         }
     494             :     }
     495             : 
     496         177 :     const char *const apszMeaningfulOptionsToStoreInMD[] = {
     497             :         szSWAP_COORDINATES_OPTION, szREMOVE_UNUSED_LAYERS_OPTION,
     498             :         szREMOVE_UNUSED_FIELDS_OPTION};
     499         708 :     for (size_t i = 0; i < CPL_ARRAYSIZE(apszMeaningfulOptionsToStoreInMD); ++i)
     500             :     {
     501         531 :         const char *pszKey = apszMeaningfulOptionsToStoreInMD[i];
     502             :         const char *pszVal =
     503         531 :             CSLFetchNameValue(poOpenInfo->papszOpenOptions, pszKey);
     504         531 :         if (pszVal)
     505             :         {
     506           4 :             OGRFeature oFeature(poFDefn);
     507           4 :             oFeature.SetField(szKEY, pszKey);
     508           4 :             oFeature.SetField(szVALUE, pszVal);
     509           4 :             CPL_IGNORE_RET_VAL(
     510           4 :                 m_poOtherMetadataLayer->CreateFeature(&oFeature));
     511             :         }
     512             :     }
     513             : 
     514         354 :     CPLString osAbsoluteGMLFilename;
     515         177 :     if (!m_osGMLFilename.empty())
     516             :     {
     517         149 :         OGRFeature oFeature(poFDefn);
     518         149 :         oFeature.SetField(szKEY, szDOCUMENT_FILENAME);
     519         149 :         char *pszCurDir = CPLGetCurrentDir();
     520         445 :         if (!bKeepRelativePathsForMetadata &&
     521         149 :             CPLIsFilenameRelative(m_osGMLFilename) && pszCurDir != nullptr)
     522             :         {
     523             :             osAbsoluteGMLFilename =
     524          78 :                 CPLFormFilenameSafe(pszCurDir, m_osGMLFilename, nullptr);
     525             :         }
     526             :         else
     527          71 :             osAbsoluteGMLFilename = m_osGMLFilename;
     528         149 :         oFeature.SetField(szVALUE, osAbsoluteGMLFilename.c_str());
     529         149 :         CPLFree(pszCurDir);
     530         149 :         CPL_IGNORE_RET_VAL(m_poOtherMetadataLayer->CreateFeature(&oFeature));
     531             :     }
     532             : 
     533         177 :     int nNSIdx = 1;
     534         354 :     std::set<CPLString> oSetVisitedURI;
     535         433 :     for (int i = 0; i < static_cast<int>(aoXSDs.size()); i++)
     536             :     {
     537         256 :         const CPLString osURI(aoXSDs[i].first);
     538         256 :         const std::string osXSDFilename(aoXSDs[i].second);
     539             : 
     540         256 :         oSetVisitedURI.insert(osURI);
     541             : 
     542         256 :         if (osURI == szOGRGMLAS_URI)
     543           9 :             continue;
     544             : 
     545             :         {
     546         247 :             OGRFeature oFeature(poFDefn);
     547         247 :             oFeature.SetField(szKEY, CPLSPrintf(szNAMESPACE_URI_FMT, nNSIdx));
     548         247 :             oFeature.SetField(szVALUE, osURI.c_str());
     549         247 :             CPL_IGNORE_RET_VAL(
     550         247 :                 m_poOtherMetadataLayer->CreateFeature(&oFeature));
     551             :         }
     552             : 
     553             :         {
     554         494 :             OGRFeature oFeature(poFDefn);
     555         247 :             oFeature.SetField(szKEY,
     556             :                               CPLSPrintf(szNAMESPACE_LOCATION_FMT, nNSIdx));
     557             : 
     558             :             const CPLString osAbsoluteXSDFilename(
     559         235 :                 (osXSDFilename.find("http://") != 0 &&
     560         235 :                  osXSDFilename.find("https://") != 0 &&
     561         132 :                  CPLIsFilenameRelative(osXSDFilename.c_str()))
     562         572 :                     ? CPLFormFilenameSafe(
     563         337 :                           CPLGetDirnameSafe(osAbsoluteGMLFilename).c_str(),
     564             :                           osXSDFilename.c_str(), nullptr)
     565         247 :                     : osXSDFilename);
     566         247 :             oFeature.SetField(szVALUE, osAbsoluteXSDFilename.c_str());
     567         247 :             CPL_IGNORE_RET_VAL(
     568         247 :                 m_poOtherMetadataLayer->CreateFeature(&oFeature));
     569             :         }
     570             : 
     571         247 :         if (m_oMapURIToPrefix.find(osURI) != m_oMapURIToPrefix.end())
     572             :         {
     573         219 :             OGRFeature oFeature(poFDefn);
     574         219 :             oFeature.SetField(szKEY,
     575             :                               CPLSPrintf(szNAMESPACE_PREFIX_FMT, nNSIdx));
     576         219 :             oFeature.SetField(szVALUE, m_oMapURIToPrefix[osURI].c_str());
     577         219 :             CPL_IGNORE_RET_VAL(
     578         219 :                 m_poOtherMetadataLayer->CreateFeature(&oFeature));
     579             :         }
     580             : 
     581         247 :         nNSIdx++;
     582             :     }
     583             : 
     584        1088 :     for (const auto &oIter : m_oMapURIToPrefix)
     585             :     {
     586         911 :         const CPLString &osURI(oIter.first);
     587         911 :         const CPLString &osPrefix(oIter.second);
     588             : 
     589        1594 :         if (oSetVisitedURI.find(osURI) == oSetVisitedURI.end() &&
     590        1696 :             osURI != szXML_URI && osURI != szXS_URI && osURI != szXSI_URI &&
     591        1924 :             osURI != szXMLNS_URI && osURI != szOGRGMLAS_URI)
     592             :         {
     593             :             {
     594         153 :                 OGRFeature oFeature(poFDefn);
     595         153 :                 oFeature.SetField(szKEY,
     596             :                                   CPLSPrintf(szNAMESPACE_URI_FMT, nNSIdx));
     597         153 :                 oFeature.SetField(szVALUE, osURI.c_str());
     598         153 :                 CPL_IGNORE_RET_VAL(
     599         153 :                     m_poOtherMetadataLayer->CreateFeature(&oFeature));
     600             :             }
     601             : 
     602             :             {
     603         153 :                 OGRFeature oFeature(poFDefn);
     604         153 :                 oFeature.SetField(szKEY,
     605             :                                   CPLSPrintf(szNAMESPACE_PREFIX_FMT, nNSIdx));
     606         153 :                 oFeature.SetField(szVALUE, osPrefix.c_str());
     607         153 :                 CPL_IGNORE_RET_VAL(
     608         153 :                     m_poOtherMetadataLayer->CreateFeature(&oFeature));
     609             :             }
     610             : 
     611         153 :             nNSIdx++;
     612             :         }
     613             :     }
     614             : 
     615         177 :     if (!m_osGMLVersionFound.empty())
     616             :     {
     617           4 :         OGRFeature oFeature(poFDefn);
     618           4 :         oFeature.SetField(szKEY, szGML_VERSION);
     619           4 :         oFeature.SetField(szVALUE, m_osGMLVersionFound);
     620           4 :         CPL_IGNORE_RET_VAL(m_poOtherMetadataLayer->CreateFeature(&oFeature));
     621             :     }
     622             : 
     623         177 :     int nSchemaIdx = 1;
     624         177 :     if (bExposeSchemaNames)
     625             :     {
     626         506 :         for (const auto &osSchemaURL : oSetSchemaURLs)
     627             :         {
     628         331 :             OGRFeature oFeature(poFDefn);
     629         331 :             oFeature.SetField(szKEY, CPLSPrintf(szSCHEMA_NAME_FMT, nSchemaIdx));
     630         331 :             oFeature.SetField(szVALUE, osSchemaURL.c_str());
     631         331 :             CPL_IGNORE_RET_VAL(
     632         331 :                 m_poOtherMetadataLayer->CreateFeature(&oFeature));
     633             : 
     634         331 :             nSchemaIdx++;
     635             :         }
     636             :     }
     637         177 : }
     638             : 
     639             : /************************************************************************/
     640             : /*                         BuildXSDVector()                             */
     641             : /************************************************************************/
     642             : 
     643             : std::vector<PairURIFilename>
     644          35 : OGRGMLASDataSource::BuildXSDVector(const CPLString &osXSDFilenames)
     645             : {
     646          35 :     std::vector<PairURIFilename> aoXSDs;
     647          35 :     char **papszTokens = CSLTokenizeString2(osXSDFilenames, ",", 0);
     648          35 :     char *pszCurDir = CPLGetCurrentDir();
     649          70 :     for (int i = 0; papszTokens != nullptr && papszTokens[i] != nullptr; i++)
     650             :     {
     651         104 :         if (!STARTS_WITH(papszTokens[i], "http://") &&
     652          34 :             !STARTS_WITH(papszTokens[i], "https://") &&
     653          69 :             CPLIsFilenameRelative(papszTokens[i]) && pszCurDir != nullptr)
     654             :         {
     655          29 :             aoXSDs.push_back(PairURIFilename(
     656          58 :                 "", CPLFormFilenameSafe(pszCurDir, papszTokens[i], nullptr)
     657          58 :                         .c_str()));
     658             :         }
     659             :         else
     660             :         {
     661           6 :             aoXSDs.push_back(PairURIFilename("", papszTokens[i]));
     662             :         }
     663             :     }
     664          35 :     CPLFree(pszCurDir);
     665          35 :     CSLDestroy(papszTokens);
     666          35 :     return aoXSDs;
     667             : }
     668             : 
     669             : /************************************************************************/
     670             : /*                                Open()                                */
     671             : /************************************************************************/
     672             : 
     673         194 : bool OGRGMLASDataSource::Open(GDALOpenInfo *poOpenInfo)
     674             : {
     675         194 :     m_osConfigFile = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     676         194 :                                           szCONFIG_FILE_OPTION, "");
     677         194 :     if (m_osConfigFile.empty())
     678             :     {
     679             :         m_osConfigFile =
     680         103 :             GMLASConfiguration::GetDefaultConfFile(m_bUnlinkConfigFileAfterUse);
     681             :     }
     682         194 :     if (m_osConfigFile.empty())
     683             :     {
     684           0 :         CPLError(CE_Warning, CPLE_AppDefined,
     685             :                  "No configuration file found. Using hard-coded defaults");
     686           0 :         m_oConf.Finalize();
     687             :     }
     688             :     else
     689             :     {
     690         194 :         if (!m_oConf.Load(m_osConfigFile.c_str()))
     691             :         {
     692           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     693             :                      "Loading of configuration failed");
     694           2 :             return false;
     695             :         }
     696             :     }
     697             : 
     698         192 :     m_oCache.SetCacheDirectory(m_oConf.m_osXSDCacheDirectory);
     699         192 :     const bool bRefreshCache(CPLTestBool(CSLFetchNameValueDef(
     700         192 :         poOpenInfo->papszOpenOptions, szREFRESH_CACHE_OPTION, "NO")));
     701         192 :     m_oCache.SetRefreshMode(bRefreshCache);
     702         192 :     m_oCache.SetAllowDownload(m_oConf.m_bAllowRemoteSchemaDownload);
     703             : 
     704         192 :     m_oIgnoredXPathMatcher.SetRefXPaths(m_oConf.m_oMapPrefixToURIIgnoredXPaths,
     705         192 :                                         m_oConf.m_aosIgnoredXPaths);
     706             : 
     707             :     {
     708         384 :         std::vector<CPLString> oVector;
     709         298 :         for (const auto &oIter : m_oConf.m_oMapChildrenElementsConstraints)
     710         106 :             oVector.push_back(oIter.first);
     711         192 :         m_oChildrenElementsConstraintsXPathMatcher.SetRefXPaths(
     712         192 :             m_oConf.m_oMapPrefixToURITypeConstraints, oVector);
     713             :     }
     714             : 
     715         192 :     m_oForcedFlattenedXPathMatcher.SetRefXPaths(
     716         192 :         m_oConf.m_oMapPrefixToURIFlatteningRules,
     717         192 :         m_oConf.m_osForcedFlattenedXPath);
     718             : 
     719         192 :     m_oDisabledFlattenedXPathMatcher.SetRefXPaths(
     720         192 :         m_oConf.m_oMapPrefixToURIFlatteningRules,
     721         192 :         m_oConf.m_osDisabledFlattenedXPath);
     722             : 
     723             :     GMLASSchemaAnalyzer oAnalyzer(
     724         192 :         m_oIgnoredXPathMatcher, m_oChildrenElementsConstraintsXPathMatcher,
     725         192 :         m_oConf.m_oMapChildrenElementsConstraints,
     726         384 :         m_oForcedFlattenedXPathMatcher, m_oDisabledFlattenedXPathMatcher);
     727         192 :     oAnalyzer.SetUseArrays(m_oConf.m_bUseArrays);
     728         192 :     oAnalyzer.SetUseNullState(m_oConf.m_bUseNullState);
     729         192 :     oAnalyzer.SetInstantiateGMLFeaturesOnly(
     730         192 :         m_oConf.m_bInstantiateGMLFeaturesOnly);
     731         192 :     oAnalyzer.SetIdentifierMaxLength(m_oConf.m_nIdentifierMaxLength);
     732         192 :     oAnalyzer.SetCaseInsensitiveIdentifier(
     733         192 :         m_oConf.m_bCaseInsensitiveIdentifier);
     734         192 :     oAnalyzer.SetPGIdentifierLaundering(m_oConf.m_bPGIdentifierLaundering);
     735         192 :     oAnalyzer.SetMaximumFieldsForFlattening(
     736             :         m_oConf.m_nMaximumFieldsForFlattening);
     737         192 :     oAnalyzer.SetAlwaysGenerateOGRId(m_oConf.m_bAlwaysGenerateOGRId);
     738             : 
     739         192 :     m_osGMLFilename = STARTS_WITH_CI(poOpenInfo->pszFilename, szGMLAS_PREFIX)
     740         384 :                           ? CPLExpandTildeSafe(poOpenInfo->pszFilename +
     741             :                                                strlen(szGMLAS_PREFIX))
     742         192 :                           : poOpenInfo->pszFilename;
     743             : 
     744             :     CPLString osXSDFilenames =
     745         384 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, szXSD_OPTION, "");
     746             : 
     747         192 :     std::shared_ptr<VSIVirtualHandle> fpGML;
     748         192 :     if (!m_osGMLFilename.empty())
     749             :     {
     750         159 :         fpGML.reset(VSIFOpenL(m_osGMLFilename, "rb"), VSIVirtualHandleCloser{});
     751         159 :         if (fpGML == nullptr)
     752             :         {
     753           2 :             CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
     754             :                      m_osGMLFilename.c_str());
     755           2 :             return false;
     756             :         }
     757             :     }
     758          33 :     else if (osXSDFilenames.empty())
     759             :     {
     760           1 :         CPLError(CE_Failure, CPLE_AppDefined,
     761             :                  "%s open option must be provided when no "
     762             :                  "XML data file is passed",
     763             :                  szXSD_OPTION);
     764           1 :         return false;
     765             :     }
     766             : 
     767         378 :     GMLASTopElementParser topElementParser;
     768         189 :     if (!m_osGMLFilename.empty())
     769             :     {
     770         157 :         topElementParser.Parse(m_osGMLFilename, fpGML);
     771         157 :         if (m_oConf.m_eSWEActivationMode ==
     772             :             GMLASConfiguration::SWE_ACTIVATE_IF_NAMESPACE_FOUND)
     773             :         {
     774         157 :             m_bFoundSWE = topElementParser.GetSWE();
     775             :         }
     776           0 :         else if (m_oConf.m_eSWEActivationMode ==
     777             :                  GMLASConfiguration::SWE_ACTIVATE_TRUE)
     778             :         {
     779           0 :             m_bFoundSWE = true;
     780             :         }
     781         157 :         oAnalyzer.SetMapDocNSURIToPrefix(
     782             :             topElementParser.GetMapDocNSURIToPrefix());
     783             :     }
     784         378 :     std::vector<PairURIFilename> aoXSDs;
     785         189 :     if (osXSDFilenames.empty())
     786             :     {
     787         154 :         aoXSDs = topElementParser.GetXSDs();
     788             :     }
     789             :     else
     790             :     {
     791          35 :         aoXSDs = BuildXSDVector(osXSDFilenames);
     792             :     }
     793         189 :     if (fpGML)
     794             :     {
     795             :         m_osHash =
     796         157 :             CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "HASH", "");
     797         157 :         if (m_osHash.empty())
     798             :         {
     799         151 :             fpGML->Seek(0, SEEK_SET);
     800         302 :             std::string osBuffer;
     801         151 :             osBuffer.resize(8192);
     802         151 :             size_t nRead = fpGML->Read(&osBuffer[0], 1, 8192);
     803         151 :             osBuffer.resize(nRead);
     804         151 :             size_t nPos = osBuffer.find("timeStamp=\"");
     805         151 :             if (nPos != std::string::npos)
     806             :             {
     807             :                 size_t nPos2 =
     808           6 :                     osBuffer.find('"', nPos + strlen("timeStamp=\""));
     809           6 :                 if (nPos2 != std::string::npos)
     810           6 :                     osBuffer.replace(nPos, nPos2 - nPos + 1, nPos2 - nPos + 1,
     811           6 :                                      ' ');
     812             :             }
     813             :             CPL_SHA256Context ctxt;
     814         151 :             CPL_SHA256Init(&ctxt);
     815         151 :             CPL_SHA256Update(&ctxt, osBuffer.data(), osBuffer.size());
     816             : 
     817             :             VSIStatBufL sStat;
     818         151 :             if (VSIStatL(m_osGMLFilename, &sStat) == 0)
     819             :             {
     820         151 :                 m_nFileSize = sStat.st_size;
     821         151 :                 GUInt64 nFileSizeLittleEndian =
     822         151 :                     static_cast<GUInt64>(sStat.st_size);
     823         151 :                 CPL_LSBPTR64(&nFileSizeLittleEndian);
     824         151 :                 CPL_SHA256Update(&ctxt, &nFileSizeLittleEndian,
     825             :                                  sizeof(nFileSizeLittleEndian));
     826             :             }
     827             : 
     828             :             GByte abyHash[CPL_SHA256_HASH_SIZE];
     829         151 :             CPL_SHA256Final(&ctxt, abyHash);
     830             :             // Half of the hash should be enough for our purpose
     831         151 :             char *pszHash = CPLBinaryToHex(CPL_SHA256_HASH_SIZE / 2, abyHash);
     832         151 :             m_osHash = pszHash;
     833         151 :             CPLFree(pszHash);
     834             :         }
     835             : 
     836         157 :         fpGML->Seek(0, SEEK_SET);
     837         157 :         PushUnusedGMLFilePointer(fpGML);
     838             :     }
     839             : 
     840         189 :     if (aoXSDs.empty())
     841             :     {
     842           1 :         if (osXSDFilenames.empty())
     843             :         {
     844           1 :             CPLError(CE_Failure, CPLE_AppDefined,
     845             :                      "No schema locations found when analyzing data file: "
     846             :                      "%s open option must be provided",
     847             :                      szXSD_OPTION);
     848             :         }
     849             :         else
     850             :         {
     851           0 :             CPLError(CE_Failure, CPLE_AppDefined, "No schema locations found");
     852             :         }
     853           1 :         return false;
     854             :     }
     855             : 
     856         376 :     m_bSchemaFullChecking = CPLFetchBool(poOpenInfo->papszOpenOptions,
     857             :                                          szSCHEMA_FULL_CHECKING_OPTION,
     858         188 :                                          m_oConf.m_bSchemaFullChecking);
     859             : 
     860         376 :     m_bHandleMultipleImports = CPLFetchBool(poOpenInfo->papszOpenOptions,
     861             :                                             szHANDLE_MULTIPLE_IMPORTS_OPTION,
     862         188 :                                             m_oConf.m_bHandleMultipleImports);
     863             : 
     864         376 :     bool bRet = oAnalyzer.Analyze(
     865         564 :         m_oCache, CPLGetDirnameSafe(m_osGMLFilename).c_str(), aoXSDs,
     866         188 :         m_bSchemaFullChecking, m_bHandleMultipleImports);
     867         188 :     if (!bRet)
     868             :     {
     869          11 :         return false;
     870             :     }
     871             : 
     872         177 :     if (!osXSDFilenames.empty())
     873          31 :         m_aoXSDsManuallyPassed = aoXSDs;
     874             : 
     875         177 :     m_oMapURIToPrefix = oAnalyzer.GetMapURIToPrefix();
     876             : 
     877         177 :     m_osGMLVersionFound = oAnalyzer.GetGMLVersionFound();
     878             : 
     879         177 :     const std::set<CPLString> &oSetSchemaURLs = oAnalyzer.GetSchemaURLS();
     880             : 
     881         177 :     FillOtherMetadataLayer(poOpenInfo, m_osConfigFile, aoXSDs, oSetSchemaURLs);
     882             : 
     883         177 :     if (CPLFetchBool(poOpenInfo->papszOpenOptions,
     884             :                      szEXPOSE_METADATA_LAYERS_OPTION,
     885         177 :                      m_oConf.m_bExposeMetadataLayers))
     886             :     {
     887          15 :         m_apoRequestedMetadataLayers.push_back(m_poFieldsMetadataLayer.get());
     888          15 :         m_apoRequestedMetadataLayers.push_back(m_poLayersMetadataLayer.get());
     889          15 :         m_apoRequestedMetadataLayers.push_back(m_poRelationshipsLayer.get());
     890          15 :         m_apoRequestedMetadataLayers.push_back(m_poOtherMetadataLayer.get());
     891             :     }
     892             : 
     893         354 :     const char *pszSwapCoordinates = CSLFetchNameValueDef(
     894         177 :         poOpenInfo->papszOpenOptions, szSWAP_COORDINATES_OPTION, "AUTO");
     895         177 :     if (EQUAL(pszSwapCoordinates, "AUTO"))
     896             :     {
     897         175 :         m_eSwapCoordinates = GMLAS_SWAP_AUTO;
     898             :     }
     899           2 :     else if (CPLTestBool(pszSwapCoordinates))
     900             :     {
     901           1 :         m_eSwapCoordinates = GMLAS_SWAP_YES;
     902             :     }
     903             :     else
     904             :     {
     905           1 :         m_eSwapCoordinates = GMLAS_SWAP_NO;
     906             :     }
     907             : 
     908         177 :     const std::vector<GMLASFeatureClass> &aoClasses = oAnalyzer.GetClasses();
     909             : 
     910             :     // First "standard" tables
     911       12258 :     for (auto &oClass : aoClasses)
     912             :     {
     913       12081 :         if (oClass.GetParentXPath().empty())
     914        3142 :             TranslateClasses(nullptr, oClass);
     915             :     }
     916             :     // Then junction tables
     917       12258 :     for (auto &oClass : aoClasses)
     918             :     {
     919       12081 :         if (!oClass.GetParentXPath().empty())
     920        8939 :             TranslateClasses(nullptr, oClass);
     921             :     }
     922             : 
     923             :     // And now do initialization since we need to have instantiated everything
     924             :     // to be able to do cross-layer links
     925       15598 :     for (auto &poLayer : m_apoLayers)
     926             :     {
     927       15421 :         poLayer->PostInit(m_oConf.m_bIncludeGeometryXML);
     928             :     }
     929         177 :     m_bLayerInitFinished = true;
     930             : 
     931             :     // Do optional validation
     932         354 :     m_bValidate = CPLFetchBool(poOpenInfo->papszOpenOptions, szVALIDATE_OPTION,
     933         177 :                                m_oConf.m_bValidate);
     934             : 
     935         354 :     m_bRemoveUnusedLayers = CPLFetchBool(poOpenInfo->papszOpenOptions,
     936             :                                          szREMOVE_UNUSED_LAYERS_OPTION,
     937         177 :                                          m_oConf.m_bRemoveUnusedLayers);
     938             : 
     939         354 :     m_bRemoveUnusedFields = CPLFetchBool(poOpenInfo->papszOpenOptions,
     940             :                                          szREMOVE_UNUSED_FIELDS_OPTION,
     941         177 :                                          m_oConf.m_bRemoveUnusedFields);
     942             : 
     943         177 :     m_oXLinkResolver.SetConf(m_oConf.m_oXLinkResolution);
     944         177 :     m_oXLinkResolver.SetRefreshMode(bRefreshCache);
     945             : 
     946         177 :     if (m_bValidate || m_bRemoveUnusedLayers ||
     947         125 :         (m_bFoundSWE &&
     948           2 :          (m_oConf.m_bSWEProcessDataRecord || m_oConf.m_bSWEProcessDataArray)))
     949             :     {
     950          54 :         CPLErrorReset();
     951          54 :         RunFirstPassIfNeeded(nullptr, nullptr, nullptr);
     952         162 :         if (CPLFetchBool(poOpenInfo->papszOpenOptions,
     953             :                          szFAIL_IF_VALIDATION_ERROR_OPTION,
     954          96 :                          m_oConf.m_bFailIfValidationError) &&
     955          42 :             CPLGetLastErrorType() != CE_None)
     956             :         {
     957           2 :             CPLError(CE_Failure, CPLE_AppDefined,
     958             :                      "Validation errors encountered");
     959           2 :             return false;
     960             :         }
     961             :     }
     962         175 :     if (CPLGetLastErrorType() == CE_Failure)
     963           1 :         CPLErrorReset();
     964             : 
     965         175 :     return true;
     966             : }
     967             : 
     968             : /************************************************************************/
     969             : /*                           TestCapability()                           */
     970             : /************************************************************************/
     971             : 
     972          96 : int OGRGMLASDataSource::TestCapability(const char *pszCap)
     973             : {
     974          96 :     return EQUAL(pszCap, ODsCRandomLayerRead);
     975             : }
     976             : 
     977             : /************************************************************************/
     978             : /*                           CreateReader()                             */
     979             : /************************************************************************/
     980             : 
     981             : GMLASReader *
     982        1282 : OGRGMLASDataSource::CreateReader(std::shared_ptr<VSIVirtualHandle> &fpGML,
     983             :                                  GDALProgressFunc pfnProgress,
     984             :                                  void *pProgressData)
     985             : {
     986        1282 :     if (fpGML == nullptr)
     987             :     {
     988             :         // Try recycling an already opened and unused file pointer
     989        1017 :         fpGML = PopUnusedGMLFilePointer();
     990        1017 :         if (fpGML == nullptr)
     991           7 :             fpGML.reset(VSIFOpenL(GetGMLFilename(), "rb"),
     992             :                         VSIVirtualHandleCloser{});
     993        1017 :         if (fpGML == nullptr)
     994           2 :             return nullptr;
     995             :     }
     996             : 
     997             :     auto poReader = std::make_unique<GMLASReader>(
     998        2560 :         GetCache(), GetIgnoredXPathMatcher(), m_oXLinkResolver);
     999        2560 :     poReader->Init(GetGMLFilename(), fpGML, GetMapURIToPrefix(), GetLayers(),
    1000        1280 :                    false, std::vector<PairURIFilename>(), m_bSchemaFullChecking,
    1001        1280 :                    m_bHandleMultipleImports);
    1002             : 
    1003        1280 :     poReader->SetSwapCoordinates(GetSwapCoordinates());
    1004             : 
    1005        1280 :     poReader->SetFileSize(m_nFileSize);
    1006             : 
    1007        1280 :     if (!RunFirstPassIfNeeded(poReader.get(), pfnProgress, pProgressData))
    1008             :     {
    1009           0 :         return nullptr;
    1010             :     }
    1011             : 
    1012        1280 :     poReader->SetMapIgnoredXPathToWarn(GetMapIgnoredXPathToWarn());
    1013             : 
    1014        1280 :     poReader->SetHash(m_osHash);
    1015             : 
    1016        1280 :     return poReader.release();
    1017             : }
    1018             : 
    1019             : /************************************************************************/
    1020             : /*                            ResetReading()                            */
    1021             : /************************************************************************/
    1022             : 
    1023           2 : void OGRGMLASDataSource::ResetReading()
    1024             : {
    1025           2 :     m_poReader.reset();
    1026           6 :     for (auto *poLayer : m_apoRequestedMetadataLayers)
    1027           4 :         poLayer->ResetReading();
    1028           2 :     m_bEndOfReaderLayers = false;
    1029           2 :     m_nCurMetadataLayerIdx = -1;
    1030           2 : }
    1031             : 
    1032             : /************************************************************************/
    1033             : /*                           GetNextFeature()                           */
    1034             : /************************************************************************/
    1035             : 
    1036        2774 : OGRFeature *OGRGMLASDataSource::GetNextFeature(OGRLayer **ppoBelongingLayer,
    1037             :                                                double *pdfProgressPct,
    1038             :                                                GDALProgressFunc pfnProgress,
    1039             :                                                void *pProgressData)
    1040             : {
    1041        2774 :     if (m_bEndOfReaderLayers)
    1042             :     {
    1043        3934 :         if (m_nCurMetadataLayerIdx >= 0 &&
    1044        1963 :             m_nCurMetadataLayerIdx <
    1045        1963 :                 static_cast<int>(m_apoRequestedMetadataLayers.size()))
    1046             :         {
    1047             :             while (true)
    1048             :             {
    1049             :                 OGRLayer *poLayer =
    1050        2002 :                     m_apoRequestedMetadataLayers[m_nCurMetadataLayerIdx];
    1051        2002 :                 OGRFeature *poFeature = poLayer->GetNextFeature();
    1052        2002 :                 if (poFeature != nullptr)
    1053             :                 {
    1054        1945 :                     if (pdfProgressPct != nullptr)
    1055           0 :                         *pdfProgressPct = 1.0;
    1056        1945 :                     if (ppoBelongingLayer != nullptr)
    1057        1945 :                         *ppoBelongingLayer = poLayer;
    1058        1945 :                     return poFeature;
    1059             :                 }
    1060          57 :                 if (m_nCurMetadataLayerIdx + 1 <
    1061          57 :                     static_cast<int>(m_apoRequestedMetadataLayers.size()))
    1062             :                 {
    1063          39 :                     m_nCurMetadataLayerIdx++;
    1064             :                 }
    1065             :                 else
    1066             :                 {
    1067          18 :                     m_nCurMetadataLayerIdx = -1;
    1068          18 :                     break;
    1069             :                 }
    1070          39 :             }
    1071             :         }
    1072             : 
    1073          26 :         if (pdfProgressPct != nullptr)
    1074           0 :             *pdfProgressPct = 1.0;
    1075          26 :         if (ppoBelongingLayer != nullptr)
    1076          26 :             *ppoBelongingLayer = nullptr;
    1077          26 :         return nullptr;
    1078             :     }
    1079             : 
    1080         803 :     const double dfInitialScanRatio = 0.1;
    1081         803 :     if (m_poReader == nullptr)
    1082             :     {
    1083          23 :         void *pScaledProgress = GDALCreateScaledProgress(
    1084             :             0.0, dfInitialScanRatio, pfnProgress, pProgressData);
    1085             : 
    1086          46 :         m_poReader.reset(CreateReader(
    1087          23 :             m_fpGMLParser, pScaledProgress ? GDALScaledProgress : nullptr,
    1088             :             pScaledProgress));
    1089             : 
    1090          23 :         GDALDestroyScaledProgress(pScaledProgress);
    1091             : 
    1092          23 :         if (m_poReader == nullptr)
    1093             :         {
    1094           1 :             if (pdfProgressPct != nullptr)
    1095           0 :                 *pdfProgressPct = 1.0;
    1096           1 :             if (ppoBelongingLayer != nullptr)
    1097           1 :                 *ppoBelongingLayer = nullptr;
    1098           1 :             m_bEndOfReaderLayers = true;
    1099           1 :             if (!m_apoRequestedMetadataLayers.empty())
    1100             :             {
    1101           1 :                 m_nCurMetadataLayerIdx = 0;
    1102           1 :                 return GetNextFeature(ppoBelongingLayer, pdfProgressPct,
    1103           1 :                                       pfnProgress, pProgressData);
    1104             :             }
    1105             :             else
    1106             :             {
    1107           0 :                 return nullptr;
    1108             :             }
    1109             :         }
    1110             :     }
    1111             : 
    1112         802 :     void *pScaledProgress = GDALCreateScaledProgress(
    1113             :         dfInitialScanRatio, 1.0, pfnProgress, pProgressData);
    1114             : 
    1115             :     while (true)
    1116             :     {
    1117         802 :         OGRGMLASLayer *poBelongingLayer = nullptr;
    1118             :         auto poFeature = std::unique_ptr<OGRFeature>(m_poReader->GetNextFeature(
    1119             :             &poBelongingLayer, pScaledProgress ? GDALScaledProgress : nullptr,
    1120         802 :             pScaledProgress));
    1121        1582 :         if (poFeature == nullptr ||
    1122         780 :             poBelongingLayer->EvaluateFilter(poFeature.get()))
    1123             :         {
    1124         802 :             if (ppoBelongingLayer != nullptr)
    1125         802 :                 *ppoBelongingLayer = poBelongingLayer;
    1126         802 :             if (pdfProgressPct != nullptr)
    1127             :             {
    1128          60 :                 const vsi_l_offset nOffset = m_fpGMLParser->Tell();
    1129          60 :                 if (nOffset == m_nFileSize)
    1130          60 :                     *pdfProgressPct = 1.0;
    1131             :                 else
    1132           0 :                     *pdfProgressPct =
    1133           0 :                         dfInitialScanRatio +
    1134           0 :                         (1.0 - dfInitialScanRatio) * nOffset / m_nFileSize;
    1135             :             }
    1136         802 :             GDALDestroyScaledProgress(pScaledProgress);
    1137         802 :             if (poFeature == nullptr)
    1138             :             {
    1139          22 :                 m_bEndOfReaderLayers = true;
    1140          22 :                 if (!m_apoRequestedMetadataLayers.empty())
    1141             :                 {
    1142          17 :                     m_nCurMetadataLayerIdx = 0;
    1143          17 :                     return GetNextFeature(ppoBelongingLayer, pdfProgressPct,
    1144          17 :                                           pfnProgress, pProgressData);
    1145             :                 }
    1146             :                 else
    1147             :                 {
    1148           5 :                     return nullptr;
    1149             :                 }
    1150             :             }
    1151             :             else
    1152         780 :                 return poFeature.release();
    1153             :         }
    1154           0 :     }
    1155             : }
    1156             : 
    1157             : /************************************************************************/
    1158             : /*                          GetLayerByXPath()                           */
    1159             : /************************************************************************/
    1160             : 
    1161       28573 : OGRGMLASLayer *OGRGMLASDataSource::GetLayerByXPath(const CPLString &osXPath)
    1162             : {
    1163     3713550 :     for (auto &poLayer : m_apoLayers)
    1164             :     {
    1165     3712390 :         if (poLayer->GetFeatureClass().GetXPath() == osXPath)
    1166             :         {
    1167       27410 :             return poLayer.get();
    1168             :         }
    1169             :     }
    1170        1163 :     return nullptr;
    1171             : }
    1172             : 
    1173             : /************************************************************************/
    1174             : /*                       PushUnusedGMLFilePointer()                     */
    1175             : /************************************************************************/
    1176             : 
    1177        1178 : void OGRGMLASDataSource::PushUnusedGMLFilePointer(
    1178             :     std::shared_ptr<VSIVirtualHandle> &fpGML)
    1179             : {
    1180        1178 :     if (m_fpGML == nullptr)
    1181             :     {
    1182        1107 :         std::swap(m_fpGML, fpGML);
    1183             :     }
    1184             :     else
    1185             :     {
    1186          71 :         fpGML.reset();
    1187             :     }
    1188        1178 : }
    1189             : 
    1190             : /************************************************************************/
    1191             : /*                        PopUnusedGMLFilePointer()                     */
    1192             : /************************************************************************/
    1193             : 
    1194        1017 : std::shared_ptr<VSIVirtualHandle> OGRGMLASDataSource::PopUnusedGMLFilePointer()
    1195             : {
    1196        1017 :     std::shared_ptr<VSIVirtualHandle> fpGML;
    1197        1017 :     std::swap(fpGML, m_fpGML);
    1198        1017 :     return fpGML;
    1199             : }
    1200             : 
    1201             : /************************************************************************/
    1202             : /*                    InitReaderWithFirstPassElements()                 */
    1203             : /************************************************************************/
    1204             : 
    1205        1490 : void OGRGMLASDataSource::InitReaderWithFirstPassElements(GMLASReader *poReader)
    1206             : {
    1207        1490 :     if (poReader != nullptr)
    1208             :     {
    1209        1267 :         poReader->SetMapSRSNameToInvertedAxis(m_oMapSRSNameToInvertedAxis);
    1210        1267 :         poReader->SetMapGeomFieldDefnToSRSName(m_oMapGeomFieldDefnToSRSName);
    1211        1271 :         poReader->SetProcessDataRecord(m_bFoundSWE &&
    1212           4 :                                        m_oConf.m_bSWEProcessDataRecord);
    1213        1267 :         poReader->SetSWEDataArrayLayersRef(m_apoSWEDataArrayLayersRef);
    1214        1267 :         poReader->SetMapElementIdToLayer(m_oMapElementIdToLayer);
    1215        1267 :         poReader->SetMapElementIdToPKID(m_oMapElementIdToPKID);
    1216        1267 :         poReader->SetDefaultSrsDimension(m_nDefaultSrsDimension);
    1217             :     }
    1218        1490 : }
    1219             : 
    1220             : /************************************************************************/
    1221             : /*                          RunFirstPassIfNeeded()                      */
    1222             : /************************************************************************/
    1223             : 
    1224        1510 : bool OGRGMLASDataSource::RunFirstPassIfNeeded(GMLASReader *poReader,
    1225             :                                               GDALProgressFunc pfnProgress,
    1226             :                                               void *pProgressData)
    1227             : {
    1228        1510 :     if (m_bFirstPassDone)
    1229             :     {
    1230        1378 :         InitReaderWithFirstPassElements(poReader);
    1231        1378 :         return true;
    1232             :     }
    1233             : 
    1234         132 :     m_bFirstPassDone = true;
    1235             : 
    1236             :     // Determine if we have geometry fields in any layer
    1237             :     // If so, do an initial pass to determine the SRS of those geometry fields.
    1238         132 :     bool bHasGeomFields = false;
    1239       14323 :     for (auto &poLayer : m_apoLayers)
    1240             :     {
    1241       14212 :         poLayer->SetLayerDefnFinalized(true);
    1242       14212 :         if (poLayer->GetLayerDefn()->GetGeomFieldCount() > 0)
    1243             :         {
    1244          21 :             bHasGeomFields = true;
    1245          21 :             break;
    1246             :         }
    1247             :     }
    1248             : 
    1249         132 :     bool bSuccess = true;
    1250             :     const bool bHasURLSpecificRules =
    1251         132 :         !m_oXLinkResolver.GetConf().m_aoURLSpecificRules.empty();
    1252         111 :     if (bHasGeomFields || m_bValidate || m_bRemoveUnusedLayers ||
    1253          61 :         m_bRemoveUnusedFields || bHasURLSpecificRules ||
    1254         256 :         m_oXLinkResolver.GetConf().m_bResolveInternalXLinks ||
    1255          13 :         (m_bFoundSWE &&
    1256           0 :          (m_oConf.m_bSWEProcessDataRecord || m_oConf.m_bSWEProcessDataArray)))
    1257             :     {
    1258         119 :         bool bJustOpenedFiled = false;
    1259           0 :         std::shared_ptr<VSIVirtualHandle> fp;
    1260         119 :         if (poReader)
    1261          43 :             fp = poReader->GetFP();
    1262             :         else
    1263             :         {
    1264          76 :             fp.reset(VSIFOpenL(GetGMLFilename(), "rb"),
    1265             :                      VSIVirtualHandleCloser{});
    1266          76 :             if (fp == nullptr)
    1267             :             {
    1268           7 :                 return false;
    1269             :             }
    1270          69 :             bJustOpenedFiled = true;
    1271             :         }
    1272             : 
    1273             :         auto poReaderFirstPass = std::make_unique<GMLASReader>(
    1274         224 :             m_oCache, m_oIgnoredXPathMatcher, m_oXLinkResolver);
    1275         224 :         poReaderFirstPass->Init(GetGMLFilename(), fp, GetMapURIToPrefix(),
    1276         112 :                                 GetLayers(), m_bValidate,
    1277         112 :                                 m_aoXSDsManuallyPassed, m_bSchemaFullChecking,
    1278         112 :                                 m_bHandleMultipleImports);
    1279             : 
    1280         224 :         poReaderFirstPass->SetProcessDataRecord(
    1281         112 :             m_bFoundSWE && m_oConf.m_bSWEProcessDataRecord);
    1282             : 
    1283         112 :         poReaderFirstPass->SetFileSize(m_nFileSize);
    1284             : 
    1285         112 :         poReaderFirstPass->SetMapIgnoredXPathToWarn(
    1286         112 :             m_oConf.m_oMapIgnoredXPathToWarn);
    1287             : 
    1288         112 :         poReaderFirstPass->SetHash(m_osHash);
    1289             : 
    1290             :         // No need to warn afterwards
    1291         112 :         m_oConf.m_oMapIgnoredXPathToWarn.clear();
    1292             : 
    1293         224 :         std::set<CPLString> aoSetRemovedLayerNames;
    1294         112 :         bSuccess = poReaderFirstPass->RunFirstPass(
    1295         112 :             pfnProgress, pProgressData, m_bRemoveUnusedLayers,
    1296         112 :             m_bRemoveUnusedFields,
    1297         112 :             m_bFoundSWE && m_oConf.m_bSWEProcessDataArray,
    1298             :             m_poFieldsMetadataLayer.get(), m_poLayersMetadataLayer.get(),
    1299             :             m_poRelationshipsLayer.get(), aoSetRemovedLayerNames);
    1300             : 
    1301             :         std::vector<std::unique_ptr<OGRGMLASLayer>> apoSWEDataArrayLayers =
    1302         224 :             poReaderFirstPass->StealSWEDataArrayLayersOwned();
    1303         115 :         for (auto &poLayer : apoSWEDataArrayLayers)
    1304             :         {
    1305           3 :             poLayer->SetDataSource(this);
    1306           3 :             m_apoSWEDataArrayLayersRef.push_back(poLayer.get());
    1307           3 :             m_apoLayers.emplace_back(std::move(poLayer));
    1308             :         }
    1309             : 
    1310             :         // If we have removed layers, we also need to cleanup our special
    1311             :         // metadata layers
    1312         112 :         if (!aoSetRemovedLayerNames.empty())
    1313             :         {
    1314             :             // Removing features while iterating works here given the layers
    1315             :             // are MEM layers
    1316           1 :             m_poLayersMetadataLayer->ResetReading();
    1317           5 :             for (auto &poFeature : *m_poLayersMetadataLayer)
    1318             :             {
    1319             :                 const char *pszLayerName =
    1320           4 :                     poFeature->GetFieldAsString(szLAYER_NAME);
    1321           4 :                 if (aoSetRemovedLayerNames.find(pszLayerName) !=
    1322           8 :                     aoSetRemovedLayerNames.end())
    1323             :                 {
    1324           6 :                     CPL_IGNORE_RET_VAL(m_poLayersMetadataLayer->DeleteFeature(
    1325           3 :                         poFeature->GetFID()));
    1326             :                 }
    1327             :             }
    1328           1 :             m_poLayersMetadataLayer->ResetReading();
    1329             : 
    1330           1 :             m_poFieldsMetadataLayer->ResetReading();
    1331          14 :             for (auto &poFeature : *m_poFieldsMetadataLayer)
    1332             :             {
    1333             :                 const char *pszLayerName =
    1334          13 :                     poFeature->GetFieldAsString(szLAYER_NAME);
    1335             :                 const char *pszRelatedLayerName =
    1336          13 :                     poFeature->GetFieldAsString(szFIELD_RELATED_LAYER);
    1337          26 :                 if (aoSetRemovedLayerNames.find(pszLayerName) !=
    1338          47 :                         aoSetRemovedLayerNames.end() ||
    1339          21 :                     aoSetRemovedLayerNames.find(pszRelatedLayerName) !=
    1340          21 :                         aoSetRemovedLayerNames.end())
    1341             :                 {
    1342          12 :                     CPL_IGNORE_RET_VAL(m_poFieldsMetadataLayer->DeleteFeature(
    1343           6 :                         poFeature->GetFID()));
    1344             :                 }
    1345             :             }
    1346           1 :             m_poFieldsMetadataLayer->ResetReading();
    1347             : 
    1348           1 :             m_poRelationshipsLayer->ResetReading();
    1349           2 :             for (auto &poFeature : *m_poRelationshipsLayer)
    1350             :             {
    1351             :                 const char *pszParentLayerName =
    1352           1 :                     poFeature->GetFieldAsString(szPARENT_LAYER);
    1353             :                 const char *pszChildLayerName =
    1354           1 :                     poFeature->GetFieldAsString(szCHILD_LAYER);
    1355           2 :                 if (aoSetRemovedLayerNames.find(pszParentLayerName) !=
    1356           4 :                         aoSetRemovedLayerNames.end() ||
    1357           2 :                     aoSetRemovedLayerNames.find(pszChildLayerName) !=
    1358           2 :                         aoSetRemovedLayerNames.end())
    1359             :                 {
    1360           2 :                     CPL_IGNORE_RET_VAL(m_poRelationshipsLayer->DeleteFeature(
    1361           1 :                         poFeature->GetFID()));
    1362             :                 }
    1363             :             }
    1364           1 :             m_poRelationshipsLayer->ResetReading();
    1365             :         }
    1366             : 
    1367             :         // Store  maps to reinject them in real readers
    1368             :         m_oMapSRSNameToInvertedAxis =
    1369         112 :             poReaderFirstPass->GetMapSRSNameToInvertedAxis();
    1370             :         m_oMapGeomFieldDefnToSRSName =
    1371         112 :             poReaderFirstPass->GetMapGeomFieldDefnToSRSName();
    1372             : 
    1373         112 :         m_oMapElementIdToLayer = poReaderFirstPass->GetMapElementIdToLayer();
    1374         112 :         m_oMapElementIdToPKID = poReaderFirstPass->GetMapElementIdToPKID();
    1375         112 :         m_nDefaultSrsDimension = poReaderFirstPass->GetDefaultSrsDimension();
    1376             : 
    1377         112 :         poReaderFirstPass.reset();
    1378             : 
    1379         112 :         fp->Seek(0, SEEK_SET);
    1380         112 :         if (bJustOpenedFiled)
    1381          69 :             PushUnusedGMLFilePointer(fp);
    1382             : 
    1383         112 :         InitReaderWithFirstPassElements(poReader);
    1384             :     }
    1385             : 
    1386         125 :     return bSuccess;
    1387             : }

Generated by: LCOV version 1.14