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

Generated by: LCOV version 1.14