Coverage Report

Created: 2026-02-14 09:00

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