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

Generated by: LCOV version 1.14