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-01-18 12:42:00 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         190 : OGRGMLASDataSource::XercesInitializer::XercesInitializer()
      27             : {
      28         190 :     OGRInitializeXerces();
      29         190 : }
      30             : 
      31             : /************************************************************************/
      32             : /*                 XercesInitializer::~XercesInitializer()              */
      33             : /************************************************************************/
      34             : 
      35         190 : OGRGMLASDataSource::XercesInitializer::~XercesInitializer()
      36             : {
      37         190 :     OGRDeinitializeXerces();
      38         190 : }
      39             : 
      40             : /************************************************************************/
      41             : /*                          OGRGMLASDataSource()                        */
      42             : /************************************************************************/
      43             : 
      44         190 : OGRGMLASDataSource::OGRGMLASDataSource()
      45         190 :     : m_poFieldsMetadataLayer(std::make_unique<OGRMemLayer>(
      46           0 :           szOGR_FIELDS_METADATA, nullptr, wkbNone)),
      47         190 :       m_poLayersMetadataLayer(std::make_unique<OGRMemLayer>(
      48           0 :           szOGR_LAYERS_METADATA, nullptr, wkbNone)),
      49         190 :       m_poRelationshipsLayer(std::make_unique<OGRMemLayer>(
      50           0 :           szOGR_LAYER_RELATIONSHIPS, nullptr, wkbNone)),
      51             :       m_poOtherMetadataLayer(
      52         760 :           std::make_unique<OGRMemLayer>(szOGR_OTHER_METADATA, nullptr, wkbNone))
      53             : {
      54             :     // Initialize m_poFieldsMetadataLayer
      55             :     {
      56         380 :         OGRFieldDefn oFieldDefn(szLAYER_NAME, OFTString);
      57         190 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      58             :     }
      59             :     {
      60         380 :         OGRFieldDefn oFieldDefn(szFIELD_INDEX, OFTInteger);
      61         190 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      62             :     }
      63             :     {
      64         380 :         OGRFieldDefn oFieldDefn(szFIELD_NAME, OFTString);
      65         190 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      66             :     }
      67             :     {
      68         380 :         OGRFieldDefn oFieldDefn(szFIELD_XPATH, OFTString);
      69         190 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      70             :     }
      71             :     {
      72         380 :         OGRFieldDefn oFieldDefn(szFIELD_TYPE, OFTString);
      73         190 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      74             :     }
      75             :     {
      76         380 :         OGRFieldDefn oFieldDefn(szFIELD_IS_LIST, OFTInteger);
      77         190 :         oFieldDefn.SetSubType(OFSTBoolean);
      78         190 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      79             :     }
      80             :     {
      81         380 :         OGRFieldDefn oFieldDefn(szFIELD_MIN_OCCURS, OFTInteger);
      82         190 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      83             :     }
      84             :     {
      85         380 :         OGRFieldDefn oFieldDefn(szFIELD_MAX_OCCURS, OFTInteger);
      86         190 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      87             :     }
      88             :     {
      89         380 :         OGRFieldDefn oFieldDefn(szFIELD_REPETITION_ON_SEQUENCE, OFTInteger);
      90         190 :         oFieldDefn.SetSubType(OFSTBoolean);
      91         190 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      92             :     }
      93             :     {
      94         380 :         OGRFieldDefn oFieldDefn(szFIELD_DEFAULT_VALUE, OFTString);
      95         190 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
      96             :     }
      97             :     {
      98         380 :         OGRFieldDefn oFieldDefn(szFIELD_FIXED_VALUE, OFTString);
      99         190 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
     100             :     }
     101             :     {
     102         380 :         OGRFieldDefn oFieldDefn(szFIELD_CATEGORY, OFTString);
     103         190 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
     104             :     }
     105             :     {
     106         380 :         OGRFieldDefn oFieldDefn(szFIELD_RELATED_LAYER, OFTString);
     107         190 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
     108             :     }
     109             :     {
     110         380 :         OGRFieldDefn oFieldDefn(szFIELD_JUNCTION_LAYER, OFTString);
     111         190 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
     112             :     }
     113             :     {
     114         380 :         OGRFieldDefn oFieldDefn(szFIELD_DOCUMENTATION, OFTString);
     115         190 :         m_poFieldsMetadataLayer->CreateField(&oFieldDefn);
     116             :     }
     117             : 
     118             :     // Initialize m_poLayersMetadataLayer
     119             :     {
     120         380 :         OGRFieldDefn oFieldDefn(szLAYER_NAME, OFTString);
     121         190 :         m_poLayersMetadataLayer->CreateField(&oFieldDefn);
     122             :     }
     123             :     {
     124         380 :         OGRFieldDefn oFieldDefn(szLAYER_XPATH, OFTString);
     125         190 :         m_poLayersMetadataLayer->CreateField(&oFieldDefn);
     126             :     }
     127             :     {
     128         380 :         OGRFieldDefn oFieldDefn(szLAYER_CATEGORY, OFTString);
     129         190 :         m_poLayersMetadataLayer->CreateField(&oFieldDefn);
     130             :     }
     131             :     {
     132         380 :         OGRFieldDefn oFieldDefn(szLAYER_PKID_NAME, OFTString);
     133         190 :         m_poLayersMetadataLayer->CreateField(&oFieldDefn);
     134             :     }
     135             :     {
     136         380 :         OGRFieldDefn oFieldDefn(szLAYER_PARENT_PKID_NAME, OFTString);
     137         190 :         m_poLayersMetadataLayer->CreateField(&oFieldDefn);
     138             :     }
     139             :     {
     140         380 :         OGRFieldDefn oFieldDefn(szLAYER_DOCUMENTATION, OFTString);
     141         190 :         m_poLayersMetadataLayer->CreateField(&oFieldDefn);
     142             :     }
     143             : 
     144             :     // Initialize m_poRelationshipsLayer
     145             :     {
     146         380 :         OGRFieldDefn oFieldDefn(szPARENT_LAYER, OFTString);
     147         190 :         m_poRelationshipsLayer->CreateField(&oFieldDefn);
     148             :     }
     149             :     {
     150         380 :         OGRFieldDefn oFieldDefn(szPARENT_PKID, OFTString);
     151         190 :         m_poRelationshipsLayer->CreateField(&oFieldDefn);
     152             :     }
     153             :     {
     154         380 :         OGRFieldDefn oFieldDefn(szPARENT_ELEMENT_NAME, OFTString);
     155         190 :         m_poRelationshipsLayer->CreateField(&oFieldDefn);
     156             :     }
     157             :     {
     158         380 :         OGRFieldDefn oFieldDefn(szCHILD_LAYER, OFTString);
     159         190 :         m_poRelationshipsLayer->CreateField(&oFieldDefn);
     160             :     }
     161             :     {
     162         380 :         OGRFieldDefn oFieldDefn(szCHILD_PKID, OFTString);
     163         190 :         m_poRelationshipsLayer->CreateField(&oFieldDefn);
     164             :     }
     165             : 
     166             :     // Initialize m_poOtherMetadataLayer
     167             :     {
     168         380 :         OGRFieldDefn oFieldDefn(szKEY, OFTString);
     169         190 :         m_poOtherMetadataLayer->CreateField(&oFieldDefn);
     170             :     }
     171             :     {
     172         380 :         OGRFieldDefn oFieldDefn(szVALUE, OFTString);
     173         190 :         m_poOtherMetadataLayer->CreateField(&oFieldDefn);
     174             :     }
     175         190 : }
     176             : 
     177             : /************************************************************************/
     178             : /*                        ~OGRGMLASDataSource()                         */
     179             : /************************************************************************/
     180             : 
     181         380 : OGRGMLASDataSource::~OGRGMLASDataSource()
     182             : {
     183         190 :     if (m_bUnlinkConfigFileAfterUse)
     184             :     {
     185           0 :         VSIUnlink(m_osConfigFile.c_str());
     186             :     }
     187         380 : }
     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        8902 : OGRLayer *OGRGMLASDataSource::GetLayer(int i)
     204             : {
     205        8902 :     const int nBaseLayers = static_cast<int>(m_apoLayers.size());
     206        8902 :     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        8750 :     if (i < 0 || i >= nBaseLayers)
     215           2 :         return nullptr;
     216        8748 :     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       15417 : void OGRGMLASDataSource::TranslateClasses(OGRGMLASLayer *poParentLayer,
     254             :                                           const GMLASFeatureClass &oFC)
     255             : {
     256       15417 :     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       15417 :     m_apoLayers.emplace_back(std::make_unique<OGRGMLASLayer>(
     262       15417 :         this, oFC, poParentLayer, m_oConf.m_bAlwaysGenerateOGRId));
     263       15417 :     auto poLayer = m_apoLayers.back().get();
     264             : 
     265       18757 :     for (size_t i = 0; i < aoClasses.size(); ++i)
     266             :     {
     267        3340 :         TranslateClasses(poLayer, aoClasses[i]);
     268             :     }
     269       15417 : }
     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         185 :     GMLASTopElementParser() = default;
     285             : 
     286             :     void Parse(const CPLString &osFilename,
     287             :                const std::shared_ptr<VSIVirtualHandle> &fp);
     288             : 
     289         150 :     const std::vector<PairURIFilename> &GetXSDs() const
     290             :     {
     291         150 :         return m_aoFilenames;
     292             :     }
     293             : 
     294         153 :     bool GetSWE() const
     295             :     {
     296         153 :         return m_bFoundSWE;
     297             :     }
     298             : 
     299         153 :     const std::map<CPLString, CPLString> &GetMapDocNSURIToPrefix() const
     300             :     {
     301         153 :         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         153 : void GMLASTopElementParser::Parse(const CPLString &osFilename,
     315             :                                   const std::shared_ptr<VSIVirtualHandle> &fp)
     316             : {
     317             :     auto poSAXReader =
     318         306 :         std::unique_ptr<SAX2XMLReader>(XMLReaderFactory::createXMLReader());
     319             : 
     320         153 :     poSAXReader->setFeature(XMLUni::fgSAX2CoreNameSpaces, true);
     321         153 :     poSAXReader->setFeature(XMLUni::fgSAX2CoreNameSpacePrefixes, true);
     322             : 
     323         153 :     poSAXReader->setContentHandler(this);
     324         153 :     poSAXReader->setLexicalHandler(this);
     325         153 :     poSAXReader->setDTDHandler(this);
     326             : 
     327         153 :     poSAXReader->setFeature(XMLUni::fgXercesLoadSchema, false);
     328             : 
     329         306 :     GMLASErrorHandler oErrorHandler;
     330         153 :     poSAXReader->setErrorHandler(&oErrorHandler);
     331             : 
     332         306 :     GMLASInputSource oIS(osFilename, fp);
     333             : 
     334             :     try
     335             :     {
     336         306 :         XMLPScanToken oToFill;
     337         153 :         if (poSAXReader->parseFirst(oIS, oToFill))
     338             :         {
     339         306 :             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         153 : }
     356             : 
     357             : /************************************************************************/
     358             : /*                             startElement()                           */
     359             : /************************************************************************/
     360             : 
     361         153 : void GMLASTopElementParser::startElement(const XMLCh *const /*uri*/,
     362             :                                          const XMLCh *const /*localname*/,
     363             :                                          const XMLCh *const /*qname*/,
     364             :                                          const Attributes &attrs)
     365             : {
     366         153 :     m_nStartElementCounter++;
     367             : 
     368         945 :     for (unsigned int i = 0; i < attrs.getLength(); i++)
     369             :     {
     370        1584 :         const std::string osAttrURIPrefix(transcode(attrs.getURI(i)));
     371        1584 :         const std::string osAttrLocalname(transcode(attrs.getLocalName(i)));
     372        1584 :         const std::string osAttrValue(transcode(attrs.getValue(i)));
     373             : 
     374         944 :         if (osAttrURIPrefix == szXSI_URI &&
     375         152 :             osAttrLocalname == szSCHEMA_LOCATION)
     376             :         {
     377         126 :             CPLDebug("GMLAS", "%s=%s", szSCHEMA_LOCATION, osAttrValue.c_str());
     378             : 
     379             :             const CPLStringList aosTokens(
     380         252 :                 CSLTokenizeString2(osAttrValue.c_str(), " ", 0));
     381         126 :             const int nTokens = aosTokens.size();
     382         126 :             if ((nTokens % 2) == 0)
     383             :             {
     384         335 :                 for (int j = 0; j < nTokens; j += 2)
     385             :                 {
     386         414 :                     if (!STARTS_WITH(aosTokens[j], szWFS_URI) &&
     387         205 :                         !(EQUAL(aosTokens[j], szGML_URI) ||
     388         414 :                           STARTS_WITH(aosTokens[j],
     389             :                                       (CPLString(szGML_URI) + "/").c_str())))
     390             :                     {
     391         205 :                         CPLDebug("GMLAS", "Schema to analyze: %s -> %s",
     392             :                                  aosTokens[j], aosTokens[j + 1]);
     393         205 :                         m_aoFilenames.push_back(
     394         410 :                             PairURIFilename(aosTokens[j], aosTokens[j + 1]));
     395             :                     }
     396             :                 }
     397             :             }
     398             :         }
     399         692 :         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         640 :         else if (osAttrURIPrefix == szXMLNS_URI && osAttrValue == szSWE_URI)
     407             :         {
     408           3 :             CPLDebug("GMLAS", "SWE namespace found");
     409           3 :             m_bFoundSWE = true;
     410             :         }
     411        1071 :         else if (osAttrURIPrefix == szXMLNS_URI && !osAttrValue.empty() &&
     412         434 :                  !osAttrLocalname.empty())
     413             :         {
     414             : #ifdef DEBUG_VERBOSE
     415             :             CPLDebug("GMLAS", "Namespace %s = %s", osAttrLocalname.c_str(),
     416             :                      osAttrValue.c_str());
     417             : #endif
     418         434 :             m_oMapDocNSURIToPrefix[osAttrValue] = osAttrLocalname;
     419             :         }
     420             :     }
     421             : 
     422         153 :     if (m_nStartElementCounter == 1)
     423         153 :         m_bFinish = true;
     424         153 : }
     425             : 
     426             : /************************************************************************/
     427             : /*                         FillOtherMetadataLayer()                     */
     428             : /************************************************************************/
     429             : 
     430         173 : 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         173 :     const bool bKeepRelativePathsForMetadata = CPLTestBool(
     437         173 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     438             :                              szKEEP_RELATIVE_PATHS_FOR_METADATA_OPTION, "NO"));
     439             : 
     440         173 :     const bool bExposeConfiguration = CPLTestBool(
     441         173 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     442             :                              szEXPOSE_CONFIGURATION_IN_METADATA_OPTION, "YES"));
     443             : 
     444         173 :     const bool bExposeSchemaNames = CPLTestBool(
     445         173 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     446             :                              szEXPOSE_SCHEMAS_NAME_IN_METADATA_OPTION, "YES"));
     447             : 
     448         173 :     OGRFeatureDefn *poFDefn = m_poOtherMetadataLayer->GetLayerDefn();
     449             : 
     450         173 :     if (!osConfigFile.empty() && bExposeConfiguration)
     451             :     {
     452         171 :         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          86 :                 OGRFeature oFeature(poFDefn);
     464          86 :                 oFeature.SetField(szKEY, szCONFIGURATION_FILENAME);
     465          86 :                 char *pszCurDir = CPLGetCurrentDir();
     466         258 :                 if (!bKeepRelativePathsForMetadata &&
     467          86 :                     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          86 :                     oFeature.SetField(szVALUE, osConfigFile.c_str());
     477             :                 }
     478          86 :                 CPLFree(pszCurDir);
     479          86 :                 CPL_IGNORE_RET_VAL(
     480          86 :                     m_poOtherMetadataLayer->CreateFeature(&oFeature));
     481             :             }
     482             : 
     483          86 :             GByte *pabyRet = nullptr;
     484          86 :             if (VSIIngestFile(nullptr, osConfigFile, &pabyRet, nullptr, -1))
     485             :             {
     486          86 :                 OGRFeature oFeature(poFDefn);
     487          86 :                 oFeature.SetField(szKEY, szCONFIGURATION_INLINED);
     488          86 :                 oFeature.SetField(szVALUE, reinterpret_cast<char *>(pabyRet));
     489          86 :                 CPL_IGNORE_RET_VAL(
     490          86 :                     m_poOtherMetadataLayer->CreateFeature(&oFeature));
     491             :             }
     492          86 :             VSIFree(pabyRet);
     493             :         }
     494             :     }
     495             : 
     496         173 :     const char *const apszMeaningfulOptionsToStoreInMD[] = {
     497             :         szSWAP_COORDINATES_OPTION, szREMOVE_UNUSED_LAYERS_OPTION,
     498             :         szREMOVE_UNUSED_FIELDS_OPTION};
     499         692 :     for (size_t i = 0; i < CPL_ARRAYSIZE(apszMeaningfulOptionsToStoreInMD); ++i)
     500             :     {
     501         519 :         const char *pszKey = apszMeaningfulOptionsToStoreInMD[i];
     502             :         const char *pszVal =
     503         519 :             CSLFetchNameValue(poOpenInfo->papszOpenOptions, pszKey);
     504         519 :         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         346 :     CPLString osAbsoluteGMLFilename;
     515         173 :     if (!m_osGMLFilename.empty())
     516             :     {
     517         145 :         OGRFeature oFeature(poFDefn);
     518         145 :         oFeature.SetField(szKEY, szDOCUMENT_FILENAME);
     519         145 :         char *pszCurDir = CPLGetCurrentDir();
     520         433 :         if (!bKeepRelativePathsForMetadata &&
     521         145 :             CPLIsFilenameRelative(m_osGMLFilename) && pszCurDir != nullptr)
     522             :         {
     523             :             osAbsoluteGMLFilename =
     524          74 :                 CPLFormFilenameSafe(pszCurDir, m_osGMLFilename, nullptr);
     525             :         }
     526             :         else
     527          71 :             osAbsoluteGMLFilename = m_osGMLFilename;
     528         145 :         oFeature.SetField(szVALUE, osAbsoluteGMLFilename.c_str());
     529         145 :         CPLFree(pszCurDir);
     530         145 :         CPL_IGNORE_RET_VAL(m_poOtherMetadataLayer->CreateFeature(&oFeature));
     531             :     }
     532             : 
     533         173 :     int nNSIdx = 1;
     534         346 :     std::set<CPLString> oSetVisitedURI;
     535         425 :     for (int i = 0; i < static_cast<int>(aoXSDs.size()); i++)
     536             :     {
     537         252 :         const CPLString osURI(aoXSDs[i].first);
     538         252 :         const std::string osXSDFilename(aoXSDs[i].second);
     539             : 
     540         252 :         oSetVisitedURI.insert(osURI);
     541             : 
     542         252 :         if (osURI == szOGRGMLAS_URI)
     543           9 :             continue;
     544             : 
     545             :         {
     546         243 :             OGRFeature oFeature(poFDefn);
     547         243 :             oFeature.SetField(szKEY, CPLSPrintf(szNAMESPACE_URI_FMT, nNSIdx));
     548         243 :             oFeature.SetField(szVALUE, osURI.c_str());
     549         243 :             CPL_IGNORE_RET_VAL(
     550         243 :                 m_poOtherMetadataLayer->CreateFeature(&oFeature));
     551             :         }
     552             : 
     553             :         {
     554         486 :             OGRFeature oFeature(poFDefn);
     555         243 :             oFeature.SetField(szKEY,
     556             :                               CPLSPrintf(szNAMESPACE_LOCATION_FMT, nNSIdx));
     557             : 
     558             :             const CPLString osAbsoluteXSDFilename(
     559         231 :                 (osXSDFilename.find("http://") != 0 &&
     560         231 :                  osXSDFilename.find("https://") != 0 &&
     561         128 :                  CPLIsFilenameRelative(osXSDFilename.c_str()))
     562         560 :                     ? CPLFormFilenameSafe(
     563         329 :                           CPLGetDirnameSafe(osAbsoluteGMLFilename).c_str(),
     564             :                           osXSDFilename.c_str(), nullptr)
     565         243 :                     : osXSDFilename);
     566         243 :             oFeature.SetField(szVALUE, osAbsoluteXSDFilename.c_str());
     567         243 :             CPL_IGNORE_RET_VAL(
     568         243 :                 m_poOtherMetadataLayer->CreateFeature(&oFeature));
     569             :         }
     570             : 
     571         243 :         if (m_oMapURIToPrefix.find(osURI) != m_oMapURIToPrefix.end())
     572             :         {
     573         215 :             OGRFeature oFeature(poFDefn);
     574         215 :             oFeature.SetField(szKEY,
     575             :                               CPLSPrintf(szNAMESPACE_PREFIX_FMT, nNSIdx));
     576         215 :             oFeature.SetField(szVALUE, m_oMapURIToPrefix[osURI].c_str());
     577         215 :             CPL_IGNORE_RET_VAL(
     578         215 :                 m_poOtherMetadataLayer->CreateFeature(&oFeature));
     579             :         }
     580             : 
     581         243 :         nNSIdx++;
     582             :     }
     583             : 
     584        1068 :     for (const auto &oIter : m_oMapURIToPrefix)
     585             :     {
     586         895 :         const CPLString &osURI(oIter.first);
     587         895 :         const CPLString &osPrefix(oIter.second);
     588             : 
     589        1566 :         if (oSetVisitedURI.find(osURI) == oSetVisitedURI.end() &&
     590        1668 :             osURI != szXML_URI && osURI != szXS_URI && osURI != szXSI_URI &&
     591        1892 :             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         173 :     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         173 :     int nSchemaIdx = 1;
     624         173 :     if (bExposeSchemaNames)
     625             :     {
     626         502 :         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         173 : }
     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         190 : bool OGRGMLASDataSource::Open(GDALOpenInfo *poOpenInfo)
     674             : {
     675         190 :     m_osConfigFile = CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
     676         190 :                                           szCONFIG_FILE_OPTION, "");
     677         190 :     if (m_osConfigFile.empty())
     678             :     {
     679             :         m_osConfigFile =
     680          99 :             GMLASConfiguration::GetDefaultConfFile(m_bUnlinkConfigFileAfterUse);
     681             :     }
     682         190 :     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         190 :         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         188 :     m_oCache.SetCacheDirectory(m_oConf.m_osXSDCacheDirectory);
     699         188 :     const bool bRefreshCache(CPLTestBool(CSLFetchNameValueDef(
     700         188 :         poOpenInfo->papszOpenOptions, szREFRESH_CACHE_OPTION, "NO")));
     701         188 :     m_oCache.SetRefreshMode(bRefreshCache);
     702         188 :     m_oCache.SetAllowDownload(m_oConf.m_bAllowRemoteSchemaDownload);
     703             : 
     704         188 :     m_oIgnoredXPathMatcher.SetRefXPaths(m_oConf.m_oMapPrefixToURIIgnoredXPaths,
     705         188 :                                         m_oConf.m_aosIgnoredXPaths);
     706             : 
     707             :     {
     708         376 :         std::vector<CPLString> oVector;
     709         290 :         for (const auto &oIter : m_oConf.m_oMapChildrenElementsConstraints)
     710         102 :             oVector.push_back(oIter.first);
     711         188 :         m_oChildrenElementsConstraintsXPathMatcher.SetRefXPaths(
     712         188 :             m_oConf.m_oMapPrefixToURITypeConstraints, oVector);
     713             :     }
     714             : 
     715         188 :     m_oForcedFlattenedXPathMatcher.SetRefXPaths(
     716         188 :         m_oConf.m_oMapPrefixToURIFlatteningRules,
     717         188 :         m_oConf.m_osForcedFlattenedXPath);
     718             : 
     719         188 :     m_oDisabledFlattenedXPathMatcher.SetRefXPaths(
     720         188 :         m_oConf.m_oMapPrefixToURIFlatteningRules,
     721         188 :         m_oConf.m_osDisabledFlattenedXPath);
     722             : 
     723             :     GMLASSchemaAnalyzer oAnalyzer(
     724         188 :         m_oIgnoredXPathMatcher, m_oChildrenElementsConstraintsXPathMatcher,
     725         188 :         m_oConf.m_oMapChildrenElementsConstraints,
     726         376 :         m_oForcedFlattenedXPathMatcher, m_oDisabledFlattenedXPathMatcher);
     727         188 :     oAnalyzer.SetUseArrays(m_oConf.m_bUseArrays);
     728         188 :     oAnalyzer.SetUseNullState(m_oConf.m_bUseNullState);
     729         188 :     oAnalyzer.SetInstantiateGMLFeaturesOnly(
     730         188 :         m_oConf.m_bInstantiateGMLFeaturesOnly);
     731         188 :     oAnalyzer.SetIdentifierMaxLength(m_oConf.m_nIdentifierMaxLength);
     732         188 :     oAnalyzer.SetCaseInsensitiveIdentifier(
     733         188 :         m_oConf.m_bCaseInsensitiveIdentifier);
     734         188 :     oAnalyzer.SetPGIdentifierLaundering(m_oConf.m_bPGIdentifierLaundering);
     735         188 :     oAnalyzer.SetMaximumFieldsForFlattening(
     736             :         m_oConf.m_nMaximumFieldsForFlattening);
     737         188 :     oAnalyzer.SetAlwaysGenerateOGRId(m_oConf.m_bAlwaysGenerateOGRId);
     738             : 
     739         188 :     m_osGMLFilename = STARTS_WITH_CI(poOpenInfo->pszFilename, szGMLAS_PREFIX)
     740         376 :                           ? CPLExpandTildeSafe(poOpenInfo->pszFilename +
     741             :                                                strlen(szGMLAS_PREFIX))
     742         188 :                           : poOpenInfo->pszFilename;
     743             : 
     744             :     CPLString osXSDFilenames =
     745         376 :         CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, szXSD_OPTION, "");
     746             : 
     747         188 :     std::shared_ptr<VSIVirtualHandle> fpGML;
     748         188 :     if (!m_osGMLFilename.empty())
     749             :     {
     750         155 :         fpGML.reset(VSIFOpenL(m_osGMLFilename, "rb"), VSIVirtualHandleCloser{});
     751         155 :         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         370 :     GMLASTopElementParser topElementParser;
     768         185 :     if (!m_osGMLFilename.empty())
     769             :     {
     770         153 :         topElementParser.Parse(m_osGMLFilename, fpGML);
     771         153 :         if (m_oConf.m_eSWEActivationMode ==
     772             :             GMLASConfiguration::SWE_ACTIVATE_IF_NAMESPACE_FOUND)
     773             :         {
     774         153 :             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         153 :         oAnalyzer.SetMapDocNSURIToPrefix(
     782             :             topElementParser.GetMapDocNSURIToPrefix());
     783             :     }
     784         370 :     std::vector<PairURIFilename> aoXSDs;
     785         185 :     if (osXSDFilenames.empty())
     786             :     {
     787         150 :         aoXSDs = topElementParser.GetXSDs();
     788             :     }
     789             :     else
     790             :     {
     791          35 :         aoXSDs = BuildXSDVector(osXSDFilenames);
     792             :     }
     793         185 :     if (fpGML)
     794             :     {
     795             :         m_osHash =
     796         153 :             CSLFetchNameValueDef(poOpenInfo->papszOpenOptions, "HASH", "");
     797         153 :         if (m_osHash.empty())
     798             :         {
     799         147 :             fpGML->Seek(0, SEEK_SET);
     800         294 :             std::string osBuffer;
     801         147 :             osBuffer.resize(8192);
     802         147 :             size_t nRead = fpGML->Read(&osBuffer[0], 1, 8192);
     803         147 :             osBuffer.resize(nRead);
     804         147 :             size_t nPos = osBuffer.find("timeStamp=\"");
     805         147 :             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         147 :             CPL_SHA256Init(&ctxt);
     815         147 :             CPL_SHA256Update(&ctxt, osBuffer.data(), osBuffer.size());
     816             : 
     817             :             VSIStatBufL sStat;
     818         147 :             if (VSIStatL(m_osGMLFilename, &sStat) == 0)
     819             :             {
     820         147 :                 m_nFileSize = sStat.st_size;
     821         147 :                 GUInt64 nFileSizeLittleEndian =
     822         147 :                     static_cast<GUInt64>(sStat.st_size);
     823         147 :                 CPL_LSBPTR64(&nFileSizeLittleEndian);
     824         147 :                 CPL_SHA256Update(&ctxt, &nFileSizeLittleEndian,
     825             :                                  sizeof(nFileSizeLittleEndian));
     826             :             }
     827             : 
     828             :             GByte abyHash[CPL_SHA256_HASH_SIZE];
     829         147 :             CPL_SHA256Final(&ctxt, abyHash);
     830             :             // Half of the hash should be enough for our purpose
     831         147 :             char *pszHash = CPLBinaryToHex(CPL_SHA256_HASH_SIZE / 2, abyHash);
     832         147 :             m_osHash = pszHash;
     833         147 :             CPLFree(pszHash);
     834             :         }
     835             : 
     836         153 :         fpGML->Seek(0, SEEK_SET);
     837         153 :         PushUnusedGMLFilePointer(fpGML);
     838             :     }
     839             : 
     840         185 :     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         368 :     m_bSchemaFullChecking = CPLFetchBool(poOpenInfo->papszOpenOptions,
     857             :                                          szSCHEMA_FULL_CHECKING_OPTION,
     858         184 :                                          m_oConf.m_bSchemaFullChecking);
     859             : 
     860         368 :     m_bHandleMultipleImports = CPLFetchBool(poOpenInfo->papszOpenOptions,
     861             :                                             szHANDLE_MULTIPLE_IMPORTS_OPTION,
     862         184 :                                             m_oConf.m_bHandleMultipleImports);
     863             : 
     864         368 :     bool bRet = oAnalyzer.Analyze(
     865         552 :         m_oCache, CPLGetDirnameSafe(m_osGMLFilename).c_str(), aoXSDs,
     866         184 :         m_bSchemaFullChecking, m_bHandleMultipleImports);
     867         184 :     if (!bRet)
     868             :     {
     869          11 :         return false;
     870             :     }
     871             : 
     872         173 :     if (!osXSDFilenames.empty())
     873          31 :         m_aoXSDsManuallyPassed = aoXSDs;
     874             : 
     875         173 :     m_oMapURIToPrefix = oAnalyzer.GetMapURIToPrefix();
     876             : 
     877         173 :     m_osGMLVersionFound = oAnalyzer.GetGMLVersionFound();
     878             : 
     879         173 :     const std::set<CPLString> &oSetSchemaURLs = oAnalyzer.GetSchemaURLS();
     880             : 
     881         173 :     FillOtherMetadataLayer(poOpenInfo, m_osConfigFile, aoXSDs, oSetSchemaURLs);
     882             : 
     883         173 :     if (CPLFetchBool(poOpenInfo->papszOpenOptions,
     884             :                      szEXPOSE_METADATA_LAYERS_OPTION,
     885         173 :                      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         346 :     const char *pszSwapCoordinates = CSLFetchNameValueDef(
     894         173 :         poOpenInfo->papszOpenOptions, szSWAP_COORDINATES_OPTION, "AUTO");
     895         173 :     if (EQUAL(pszSwapCoordinates, "AUTO"))
     896             :     {
     897         171 :         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         173 :     const std::vector<GMLASFeatureClass> &aoClasses = oAnalyzer.GetClasses();
     909             : 
     910             :     // First "standard" tables
     911       12250 :     for (auto &oClass : aoClasses)
     912             :     {
     913       12077 :         if (oClass.GetParentXPath().empty())
     914        3138 :             TranslateClasses(nullptr, oClass);
     915             :     }
     916             :     // Then junction tables
     917       12250 :     for (auto &oClass : aoClasses)
     918             :     {
     919       12077 :         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       15590 :     for (auto &poLayer : m_apoLayers)
     926             :     {
     927       15417 :         poLayer->PostInit(m_oConf.m_bIncludeGeometryXML);
     928             :     }
     929         173 :     m_bLayerInitFinished = true;
     930             : 
     931             :     // Do optional validation
     932         346 :     m_bValidate = CPLFetchBool(poOpenInfo->papszOpenOptions, szVALIDATE_OPTION,
     933         173 :                                m_oConf.m_bValidate);
     934             : 
     935         346 :     m_bRemoveUnusedLayers = CPLFetchBool(poOpenInfo->papszOpenOptions,
     936             :                                          szREMOVE_UNUSED_LAYERS_OPTION,
     937         173 :                                          m_oConf.m_bRemoveUnusedLayers);
     938             : 
     939         346 :     m_bRemoveUnusedFields = CPLFetchBool(poOpenInfo->papszOpenOptions,
     940             :                                          szREMOVE_UNUSED_FIELDS_OPTION,
     941         173 :                                          m_oConf.m_bRemoveUnusedFields);
     942             : 
     943         173 :     m_oXLinkResolver.SetConf(m_oConf.m_oXLinkResolution);
     944         173 :     m_oXLinkResolver.SetRefreshMode(bRefreshCache);
     945             : 
     946         173 :     if (m_bValidate || m_bRemoveUnusedLayers ||
     947         121 :         (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         171 :     if (CPLGetLastErrorType() == CE_Failure)
     963           1 :         CPLErrorReset();
     964             : 
     965         171 :     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        1278 : OGRGMLASDataSource::CreateReader(std::shared_ptr<VSIVirtualHandle> &fpGML,
     983             :                                  GDALProgressFunc pfnProgress,
     984             :                                  void *pProgressData)
     985             : {
     986        1278 :     if (fpGML == nullptr)
     987             :     {
     988             :         // Try recycling an already opened and unused file pointer
     989        1013 :         fpGML = PopUnusedGMLFilePointer();
     990        1013 :         if (fpGML == nullptr)
     991           7 :             fpGML.reset(VSIFOpenL(GetGMLFilename(), "rb"),
     992             :                         VSIVirtualHandleCloser{});
     993        1013 :         if (fpGML == nullptr)
     994           2 :             return nullptr;
     995             :     }
     996             : 
     997             :     auto poReader = std::make_unique<GMLASReader>(
     998        2552 :         GetCache(), GetIgnoredXPathMatcher(), m_oXLinkResolver);
     999        2552 :     poReader->Init(GetGMLFilename(), fpGML, GetMapURIToPrefix(), GetLayers(),
    1000        1276 :                    false, std::vector<PairURIFilename>(), m_bSchemaFullChecking,
    1001        1276 :                    m_bHandleMultipleImports);
    1002             : 
    1003        1276 :     poReader->SetSwapCoordinates(GetSwapCoordinates());
    1004             : 
    1005        1276 :     poReader->SetFileSize(m_nFileSize);
    1006             : 
    1007        1276 :     if (!RunFirstPassIfNeeded(poReader.get(), pfnProgress, pProgressData))
    1008             :     {
    1009           0 :         return nullptr;
    1010             :     }
    1011             : 
    1012        1276 :     poReader->SetMapIgnoredXPathToWarn(GetMapIgnoredXPathToWarn());
    1013             : 
    1014        1276 :     poReader->SetHash(m_osHash);
    1015             : 
    1016        1276 :     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        1174 : void OGRGMLASDataSource::PushUnusedGMLFilePointer(
    1178             :     std::shared_ptr<VSIVirtualHandle> &fpGML)
    1179             : {
    1180        1174 :     if (m_fpGML == nullptr)
    1181             :     {
    1182        1103 :         std::swap(m_fpGML, fpGML);
    1183             :     }
    1184             :     else
    1185             :     {
    1186          71 :         fpGML.reset();
    1187             :     }
    1188        1174 : }
    1189             : 
    1190             : /************************************************************************/
    1191             : /*                        PopUnusedGMLFilePointer()                     */
    1192             : /************************************************************************/
    1193             : 
    1194        1013 : std::shared_ptr<VSIVirtualHandle> OGRGMLASDataSource::PopUnusedGMLFilePointer()
    1195             : {
    1196        1013 :     std::shared_ptr<VSIVirtualHandle> fpGML;
    1197        1013 :     std::swap(fpGML, m_fpGML);
    1198        1013 :     return fpGML;
    1199             : }
    1200             : 
    1201             : /************************************************************************/
    1202             : /*                    InitReaderWithFirstPassElements()                 */
    1203             : /************************************************************************/
    1204             : 
    1205        1486 : void OGRGMLASDataSource::InitReaderWithFirstPassElements(GMLASReader *poReader)
    1206             : {
    1207        1486 :     if (poReader != nullptr)
    1208             :     {
    1209        1263 :         poReader->SetMapSRSNameToInvertedAxis(m_oMapSRSNameToInvertedAxis);
    1210        1263 :         poReader->SetMapGeomFieldDefnToSRSName(m_oMapGeomFieldDefnToSRSName);
    1211        1267 :         poReader->SetProcessDataRecord(m_bFoundSWE &&
    1212           4 :                                        m_oConf.m_bSWEProcessDataRecord);
    1213        1263 :         poReader->SetSWEDataArrayLayersRef(m_apoSWEDataArrayLayersRef);
    1214        1263 :         poReader->SetMapElementIdToLayer(m_oMapElementIdToLayer);
    1215        1263 :         poReader->SetMapElementIdToPKID(m_oMapElementIdToPKID);
    1216        1263 :         poReader->SetDefaultSrsDimension(m_nDefaultSrsDimension);
    1217             :     }
    1218        1486 : }
    1219             : 
    1220             : /************************************************************************/
    1221             : /*                          RunFirstPassIfNeeded()                      */
    1222             : /************************************************************************/
    1223             : 
    1224        1506 : bool OGRGMLASDataSource::RunFirstPassIfNeeded(GMLASReader *poReader,
    1225             :                                               GDALProgressFunc pfnProgress,
    1226             :                                               void *pProgressData)
    1227             : {
    1228        1506 :     if (m_bFirstPassDone)
    1229             :     {
    1230        1378 :         InitReaderWithFirstPassElements(poReader);
    1231        1378 :         return true;
    1232             :     }
    1233             : 
    1234         128 :     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         128 :     bool bHasGeomFields = false;
    1239       14315 :     for (auto &poLayer : m_apoLayers)
    1240             :     {
    1241       14208 :         poLayer->SetLayerDefnFinalized(true);
    1242       14208 :         if (poLayer->GetLayerDefn()->GetGeomFieldCount() > 0)
    1243             :         {
    1244          21 :             bHasGeomFields = true;
    1245          21 :             break;
    1246             :         }
    1247             :     }
    1248             : 
    1249         128 :     bool bSuccess = true;
    1250             :     const bool bHasURLSpecificRules =
    1251         128 :         !m_oXLinkResolver.GetConf().m_aoURLSpecificRules.empty();
    1252         107 :     if (bHasGeomFields || m_bValidate || m_bRemoveUnusedLayers ||
    1253          57 :         m_bRemoveUnusedFields || bHasURLSpecificRules ||
    1254         248 :         m_oXLinkResolver.GetConf().m_bResolveInternalXLinks ||
    1255          13 :         (m_bFoundSWE &&
    1256           0 :          (m_oConf.m_bSWEProcessDataRecord || m_oConf.m_bSWEProcessDataArray)))
    1257             :     {
    1258         115 :         bool bJustOpenedFiled = false;
    1259           0 :         std::shared_ptr<VSIVirtualHandle> fp;
    1260         115 :         if (poReader)
    1261          39 :             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         216 :             m_oCache, m_oIgnoredXPathMatcher, m_oXLinkResolver);
    1275         216 :         poReaderFirstPass->Init(GetGMLFilename(), fp, GetMapURIToPrefix(),
    1276         108 :                                 GetLayers(), m_bValidate,
    1277         108 :                                 m_aoXSDsManuallyPassed, m_bSchemaFullChecking,
    1278         108 :                                 m_bHandleMultipleImports);
    1279             : 
    1280         216 :         poReaderFirstPass->SetProcessDataRecord(
    1281         108 :             m_bFoundSWE && m_oConf.m_bSWEProcessDataRecord);
    1282             : 
    1283         108 :         poReaderFirstPass->SetFileSize(m_nFileSize);
    1284             : 
    1285         108 :         poReaderFirstPass->SetMapIgnoredXPathToWarn(
    1286         108 :             m_oConf.m_oMapIgnoredXPathToWarn);
    1287             : 
    1288         108 :         poReaderFirstPass->SetHash(m_osHash);
    1289             : 
    1290             :         // No need to warn afterwards
    1291         108 :         m_oConf.m_oMapIgnoredXPathToWarn.clear();
    1292             : 
    1293         216 :         std::set<CPLString> aoSetRemovedLayerNames;
    1294         108 :         bSuccess = poReaderFirstPass->RunFirstPass(
    1295         108 :             pfnProgress, pProgressData, m_bRemoveUnusedLayers,
    1296         108 :             m_bRemoveUnusedFields,
    1297         108 :             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         216 :             poReaderFirstPass->StealSWEDataArrayLayersOwned();
    1303         111 :         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         108 :         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         108 :             poReaderFirstPass->GetMapSRSNameToInvertedAxis();
    1370             :         m_oMapGeomFieldDefnToSRSName =
    1371         108 :             poReaderFirstPass->GetMapGeomFieldDefnToSRSName();
    1372             : 
    1373         108 :         m_oMapElementIdToLayer = poReaderFirstPass->GetMapElementIdToLayer();
    1374         108 :         m_oMapElementIdToPKID = poReaderFirstPass->GetMapElementIdToPKID();
    1375         108 :         m_nDefaultSrsDimension = poReaderFirstPass->GetDefaultSrsDimension();
    1376             : 
    1377         108 :         poReaderFirstPass.reset();
    1378             : 
    1379         108 :         fp->Seek(0, SEEK_SET);
    1380         108 :         if (bJustOpenedFiled)
    1381          69 :             PushUnusedGMLFilePointer(fp);
    1382             : 
    1383         108 :         InitReaderWithFirstPassElements(poReader);
    1384             :     }
    1385             : 
    1386         121 :     return bSuccess;
    1387             : }

Generated by: LCOV version 1.14