Coverage Report

Created: 2025-08-11 09:23

/src/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasschemaanalyzer.cpp
Line
Count
Source (jump to first uncovered line)
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 "xercesc_headers.h"
16
17
// Hack to avoid bool, possibly redefined to pedantic bool class, being later
18
// used
19
static XSModel *getGrammarPool(XMLGrammarPool *pool)
20
673
{
21
673
    bool changed;
22
673
    return pool->getXSModel(changed);
23
673
}
24
25
#include "ogr_gmlas.h"
26
#include "ogr_pgdump.h"
27
28
#include <list>
29
30
static OGRwkbGeometryType GetOGRGeometryType(XSTypeDefinition *poTypeDef);
31
32
/************************************************************************/
33
/*                        IsCompatibleOfArray()                         */
34
/************************************************************************/
35
36
static bool IsCompatibleOfArray(GMLASFieldType eType)
37
153
{
38
153
    return eType == GMLAS_FT_STRING || eType == GMLAS_FT_BOOLEAN ||
39
153
           eType == GMLAS_FT_SHORT || eType == GMLAS_FT_INT32 ||
40
153
           eType == GMLAS_FT_INT64 || eType == GMLAS_FT_FLOAT ||
41
153
           eType == GMLAS_FT_DOUBLE || eType == GMLAS_FT_DECIMAL ||
42
153
           eType == GMLAS_FT_ANYURI;
43
153
}
44
45
/************************************************************************/
46
/*                       GMLASPrefixMappingHander                       */
47
/************************************************************************/
48
49
class GMLASPrefixMappingHander : public DefaultHandler
50
{
51
    std::map<CPLString, CPLString> &m_oMapURIToPrefix;
52
    const std::map<CPLString, CPLString> &m_oMapDocNSURIToPrefix;
53
    CPLString &m_osGMLVersionFound;
54
55
  public:
56
    GMLASPrefixMappingHander(
57
        std::map<CPLString, CPLString> &oMapURIToPrefix,
58
        const std::map<CPLString, CPLString> &oMapDocNSURIToPrefix,
59
        CPLString &osGMLVersionFound)
60
3.95k
        : m_oMapURIToPrefix(oMapURIToPrefix),
61
3.95k
          m_oMapDocNSURIToPrefix(oMapDocNSURIToPrefix),
62
3.95k
          m_osGMLVersionFound(osGMLVersionFound)
63
3.95k
    {
64
3.95k
    }
65
66
    virtual void startElement(const XMLCh *const uri,
67
                              const XMLCh *const localname,
68
                              const XMLCh *const qname,
69
                              const Attributes &attrs) override;
70
71
    virtual void startPrefixMapping(const XMLCh *const prefix,
72
                                    const XMLCh *const uri) override;
73
};
74
75
/************************************************************************/
76
/*                           startElement()                             */
77
/************************************************************************/
78
79
void GMLASPrefixMappingHander::startElement(const XMLCh *const uri,
80
                                            const XMLCh *const localname,
81
                                            const XMLCh *const /*qname*/,
82
                                            const Attributes &attrs)
83
284k
{
84
284k
    if (!m_osGMLVersionFound.empty())
85
126k
        return;
86
87
158k
    const CPLString osURI(transcode(uri));
88
158k
    const CPLString osLocalname(transcode(localname));
89
158k
    if (osURI == szXS_URI && osLocalname == "schema")
90
2.42k
    {
91
2.42k
        bool bIsGML = false;
92
2.42k
        std::string osVersion;
93
6.42k
        for (unsigned int i = 0; i < attrs.getLength(); i++)
94
3.99k
        {
95
3.99k
            const std::string osAttrLocalName(transcode(attrs.getLocalName(i)));
96
3.99k
            if (osAttrLocalName == "targetNamespace")
97
2.08k
            {
98
2.08k
                bIsGML = transcode(attrs.getValue(i)) == szGML_URI;
99
2.08k
            }
100
1.90k
            else if (osAttrLocalName == "version")
101
239
            {
102
239
                osVersion = transcode(attrs.getValue(i));
103
239
            }
104
3.99k
        }
105
2.42k
        if (bIsGML && !osVersion.empty())
106
1
        {
107
1
            m_osGMLVersionFound = std::move(osVersion);
108
1
        }
109
2.42k
    }
110
158k
}
111
112
/************************************************************************/
113
/*                         startPrefixMapping()                         */
114
/************************************************************************/
115
116
void GMLASPrefixMappingHander::startPrefixMapping(const XMLCh *const prefix,
117
                                                  const XMLCh *const uri)
118
15.3k
{
119
15.3k
    const CPLString osURI(transcode(uri));
120
15.3k
    CPLString osPrefix(transcode(prefix));
121
15.3k
    if (osPrefix.empty())
122
2.12k
    {
123
2.12k
        const auto oIter = m_oMapDocNSURIToPrefix.find(osURI);
124
2.12k
        if (oIter != m_oMapDocNSURIToPrefix.end())
125
0
        {
126
0
            osPrefix = oIter->second;
127
0
        }
128
2.12k
    }
129
15.3k
    if (!osPrefix.empty())
130
13.1k
    {
131
13.1k
        const auto oIter = m_oMapURIToPrefix.find(osURI);
132
13.1k
        if (oIter == m_oMapURIToPrefix.end())
133
2.69k
        {
134
2.69k
            m_oMapURIToPrefix[osURI] = osPrefix;
135
2.69k
            CPLDebug("GMLAS", "Registering prefix=%s for uri=%s",
136
2.69k
                     osPrefix.c_str(), osURI.c_str());
137
2.69k
        }
138
10.5k
        else if (oIter->second != osPrefix)
139
178
        {
140
178
            CPLDebug("GMLAS",
141
178
                     "Existing prefix=%s for uri=%s (new prefix %s not used)",
142
178
                     oIter->second.c_str(), osURI.c_str(), osPrefix.c_str());
143
178
        }
144
13.1k
    }
145
15.3k
}
146
147
/************************************************************************/
148
/*                        CollectNamespacePrefixes()                    */
149
/************************************************************************/
150
151
static void CollectNamespacePrefixes(
152
    const char *pszXSDFilename, const std::shared_ptr<VSIVirtualHandle> &fpXSD,
153
    std::map<CPLString, CPLString> &oMapURIToPrefix,
154
    const std::map<CPLString, CPLString> &oMapDocNSURIToPrefix,
155
    CPLString &osGMLVersionFound)
156
3.95k
{
157
3.95k
    GMLASInputSource oSource(pszXSDFilename, fpXSD);
158
    // This is a bit silly but the startPrefixMapping() callback only gets
159
    // called when using SAX2XMLReader::parse(), and not when using
160
    // loadGrammar(), so we have to parse the doc twice.
161
3.95k
    SAX2XMLReader *poReader = XMLReaderFactory::createXMLReader();
162
163
3.95k
    GMLASPrefixMappingHander contentHandler(
164
3.95k
        oMapURIToPrefix, oMapDocNSURIToPrefix, osGMLVersionFound);
165
3.95k
    poReader->setContentHandler(&contentHandler);
166
167
3.95k
    GMLASErrorHandler oErrorHandler;
168
3.95k
    poReader->setErrorHandler(&oErrorHandler);
169
170
3.95k
    poReader->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution, true);
171
172
3.95k
    std::string osErrorMsg;
173
3.95k
    try
174
3.95k
    {
175
3.95k
        poReader->parse(oSource);
176
3.95k
    }
177
3.95k
    catch (const SAXException &e)
178
3.95k
    {
179
0
        osErrorMsg += transcode(e.getMessage());
180
0
    }
181
3.95k
    catch (const XMLException &e)
182
3.95k
    {
183
0
        osErrorMsg += transcode(e.getMessage());
184
0
    }
185
3.95k
    catch (const OutOfMemoryException &e)
186
3.95k
    {
187
0
        if (strstr(CPLGetLastErrorMsg(), "configuration option") == nullptr)
188
0
        {
189
0
            osErrorMsg += transcode(e.getMessage());
190
0
        }
191
0
    }
192
3.95k
    catch (const DOMException &e)
193
3.95k
    {
194
0
        osErrorMsg += transcode(e.getMessage());
195
0
    }
196
3.95k
    if (!osErrorMsg.empty())
197
0
    {
198
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrorMsg.c_str());
199
0
    }
200
3.95k
    delete poReader;
201
3.95k
}
202
203
/************************************************************************/
204
/*                       GMLASAnalyzerEntityResolver                    */
205
/************************************************************************/
206
207
class GMLASAnalyzerEntityResolver final : public GMLASBaseEntityResolver
208
{
209
    std::map<CPLString, CPLString> &m_oMapURIToPrefix;
210
    const std::map<CPLString, CPLString> &m_oMapDocNSURIToPrefix;
211
212
  public:
213
    GMLASAnalyzerEntityResolver(
214
        const CPLString &osBasePath,
215
        std::map<CPLString, CPLString> &oMapURIToPrefix,
216
        const std::map<CPLString, CPLString> &oMapDocNSURIToPrefix,
217
        GMLASXSDCache &oCache)
218
1.04k
        : GMLASBaseEntityResolver(osBasePath, oCache),
219
1.04k
          m_oMapURIToPrefix(oMapURIToPrefix),
220
1.04k
          m_oMapDocNSURIToPrefix(oMapDocNSURIToPrefix)
221
1.04k
    {
222
1.04k
    }
223
224
    virtual void DoExtraSchemaProcessing(
225
        const CPLString &osFilename,
226
        const std::shared_ptr<VSIVirtualHandle> &fp) override;
227
};
228
229
/************************************************************************/
230
/*                         DoExtraSchemaProcessing()                    */
231
/************************************************************************/
232
233
void GMLASAnalyzerEntityResolver::DoExtraSchemaProcessing(
234
    const CPLString &osFilename, const std::shared_ptr<VSIVirtualHandle> &fp)
235
3.95k
{
236
3.95k
    CollectNamespacePrefixes(osFilename, fp, m_oMapURIToPrefix,
237
3.95k
                             m_oMapDocNSURIToPrefix, m_osGMLVersionFound);
238
3.95k
    fp->Seek(0, SEEK_SET);
239
3.95k
}
240
241
/************************************************************************/
242
/*                        GMLASSchemaAnalyzer()                         */
243
/************************************************************************/
244
245
GMLASSchemaAnalyzer::GMLASSchemaAnalyzer(
246
    GMLASXPathMatcher &oIgnoredXPathMatcher,
247
    GMLASXPathMatcher &oChildrenElementsConstraintsXPathMatcher,
248
    const std::map<CPLString, std::vector<CPLString>>
249
        &oMapChildrenElementsConstraints,
250
    GMLASXPathMatcher &oForcedFlattenedXPathMatcher,
251
    GMLASXPathMatcher &oDisabledFlattenedXPathMatcher)
252
1.96k
    : m_oIgnoredXPathMatcher(oIgnoredXPathMatcher),
253
      m_oChildrenElementsConstraintsXPathMatcher(
254
1.96k
          oChildrenElementsConstraintsXPathMatcher),
255
1.96k
      m_oForcedFlattenedXPathMatcher(oForcedFlattenedXPathMatcher),
256
1.96k
      m_oDisabledFlattenedXPathMatcher(oDisabledFlattenedXPathMatcher),
257
1.96k
      m_oMapChildrenElementsConstraints(oMapChildrenElementsConstraints),
258
1.96k
      m_bUseArrays(true), m_bUseNullState(false),
259
1.96k
      m_bInstantiateGMLFeaturesOnly(true), m_nIdentifierMaxLength(0),
260
1.96k
      m_bCaseInsensitiveIdentifier(CASE_INSENSITIVE_IDENTIFIER_DEFAULT),
261
1.96k
      m_bPGIdentifierLaundering(PG_IDENTIFIER_LAUNDERING_DEFAULT),
262
1.96k
      m_nMaximumFieldsForFlattening(MAXIMUM_FIELDS_FLATTENING_DEFAULT),
263
1.96k
      m_bAlwaysGenerateOGRId(ALWAYS_GENERATE_OGR_ID_DEFAULT)
264
1.96k
{
265
    // A few hardcoded namespace uri->prefix mappings
266
1.96k
    m_oMapURIToPrefix[szXMLNS_URI] = szXMLNS_PREFIX;
267
1.96k
    m_oMapURIToPrefix[szXSI_URI] = szXSI_PREFIX;
268
1.96k
}
269
270
/************************************************************************/
271
/*                               GetPrefix()                            */
272
/************************************************************************/
273
274
CPLString GMLASSchemaAnalyzer::GetPrefix(const CPLString &osNamespaceURI)
275
2.95M
{
276
2.95M
    if (osNamespaceURI.empty())
277
2.87M
        return "";
278
77.3k
    const auto oIter = m_oMapURIToPrefix.find(osNamespaceURI);
279
77.3k
    if (oIter != m_oMapURIToPrefix.end())
280
77.1k
        return oIter->second;
281
195
    else if (!osNamespaceURI.empty())
282
195
    {
283
        // If the schema doesn't define a xmlns:MYPREFIX=myuri, then forge a
284
        // fake prefix for conveniency
285
195
        CPLString osPrefix;
286
195
        if (osNamespaceURI.find(szOPENGIS_URL) == 0)
287
5
            osPrefix = osNamespaceURI.substr(strlen(szOPENGIS_URL));
288
190
        else if (osNamespaceURI.find("http://") == 0)
289
110
            osPrefix = osNamespaceURI.substr(strlen("http://"));
290
80
        else
291
80
            osPrefix = osNamespaceURI;
292
13.8M
        for (size_t i = 0; i < osPrefix.size(); i++)
293
13.8M
        {
294
13.8M
            if (!isalnum(static_cast<unsigned char>(osPrefix[i])))
295
2.96M
                osPrefix[i] = '_';
296
13.8M
        }
297
195
        m_oMapURIToPrefix[osNamespaceURI] = osPrefix;
298
195
        CPLDebug("GMLAS", "Cannot find prefix for ns='%s'. Forging %s",
299
195
                 osNamespaceURI.c_str(), osPrefix.c_str());
300
195
        return osPrefix;
301
195
    }
302
0
    else
303
0
    {
304
0
        CPLDebug("GMLAS", "Cannot find prefix for ns='%s'.",
305
0
                 osNamespaceURI.c_str());
306
0
        return "";
307
0
    }
308
77.3k
}
309
310
/************************************************************************/
311
/*                               MakeXPath()                            */
312
/************************************************************************/
313
314
CPLString GMLASSchemaAnalyzer::MakeXPath(const CPLString &osNamespaceURI,
315
                                         const CPLString &osName)
316
2.06M
{
317
2.06M
    const CPLString osPrefix(GetPrefix(osNamespaceURI));
318
2.06M
    if (osPrefix.empty())
319
2.00M
        return osName;
320
55.9k
    return osPrefix + ":" + osName;
321
2.06M
}
322
323
/************************************************************************/
324
/*                         GetNSOfLastXPathComponent()                  */
325
/************************************************************************/
326
327
// Return the namespace (if any) of the last component of the XPath
328
static CPLString GetNSOfLastXPathComponent(const CPLString &osXPath)
329
8.74M
{
330
8.74M
    size_t nPos = osXPath.rfind('@');
331
8.74M
    if (nPos != std::string::npos)
332
5.79M
        nPos++;
333
2.95M
    else
334
2.95M
    {
335
2.95M
        nPos = osXPath.rfind('/');
336
2.95M
        if (nPos != std::string::npos)
337
33.7k
            nPos++;
338
2.91M
        else
339
2.91M
            nPos = 0;
340
2.95M
    }
341
8.74M
    size_t nPosColumn = osXPath.find(':', nPos);
342
8.74M
    if (nPosColumn == std::string::npos)
343
8.31M
        return CPLString();
344
429k
    return CPLString(osXPath.substr(nPos, nPosColumn - nPos));
345
8.74M
}
346
347
/************************************************************************/
348
/*                         LaunderFieldNames()                          */
349
/************************************************************************/
350
351
// Make sure that field names are unique within the class
352
bool GMLASSchemaAnalyzer::LaunderFieldNames(GMLASFeatureClass &oClass)
353
12.0k
{
354
12.0k
    std::vector<GMLASField> &aoFields = oClass.GetFields();
355
356
    // Duplicates can happen if a class has both an element and an attribute
357
    // with same name, and/or attributes/elements with same name in different
358
    // namespaces.
359
360
    // Detect duplicated field names
361
12.0k
    std::map<CPLString, std::list<int>> oMapNameToFieldIndex;
362
601k
    for (int i = 0; i < static_cast<int>(aoFields.size()); i++)
363
589k
    {
364
589k
        if (aoFields[i].GetCategory() == GMLASField::REGULAR)
365
132k
        {
366
132k
            oMapNameToFieldIndex[aoFields[i].GetName()].push_back(i);
367
132k
        }
368
589k
    }
369
370
12.0k
    std::set<CPLString> oSetDuplicates;
371
12.0k
    for (const auto &oIter : oMapNameToFieldIndex)
372
34.7k
    {
373
        // Has it duplicates ?
374
34.7k
        const size_t nOccurrences = oIter.second.size();
375
34.7k
        if (nOccurrences > 1)
376
11.6k
        {
377
11.6k
            oSetDuplicates.insert(oIter.first);
378
11.6k
        }
379
34.7k
    }
380
381
36.3k
    while (!oSetDuplicates.empty())
382
24.3k
    {
383
        // Iterate over the unique names
384
24.3k
        auto oIterSet = oSetDuplicates.begin();
385
2.94M
        while (oIterSet != oSetDuplicates.end())
386
2.92M
        {
387
2.92M
            auto oIterSetNext = oIterSet;
388
2.92M
            ++oIterSetNext;
389
390
2.92M
            auto oIterMap = oMapNameToFieldIndex.find(*oIterSet);
391
2.92M
            CPLAssert(oIterMap != oMapNameToFieldIndex.end());
392
2.92M
            auto &list = oIterMap->second;
393
394
2.92M
            const CPLString oClassNS =
395
2.92M
                GetNSOfLastXPathComponent(oClass.GetXPath());
396
2.92M
            bool bHasDoneRenamingForThatCase = false;
397
398
2.92M
            auto oIterList = list.begin();
399
400
            // Update oMapNameToFieldIndex and oSetDuplicates with the
401
            // new field name, and removing the old one.
402
2.92M
            const auto updateSetAndMapWithNewName =
403
2.92M
                [&oIterList, &list, &oMapNameToFieldIndex,
404
2.92M
                 &oSetDuplicates](int nFieldIdx, const std::string &osNewName)
405
2.97M
            {
406
2.97M
                list.erase(oIterList);
407
2.97M
                auto &newList = oMapNameToFieldIndex[osNewName];
408
2.97M
                newList.push_back(nFieldIdx);
409
2.97M
                if (newList.size() > 1)
410
2.87M
                    oSetDuplicates.insert(osNewName);
411
2.97M
            };
412
413
8.67M
            while (oIterList != list.end())
414
5.82M
            {
415
5.82M
                auto oIterListNext = oIterList;
416
5.82M
                ++oIterListNext;
417
418
5.82M
                const int nFieldIdx = *oIterList;
419
5.82M
                GMLASField &oField = aoFields[nFieldIdx];
420
                // CPLDebug("GMLAS", "%s", oField.GetXPath().c_str() );
421
5.82M
                const CPLString oNS(
422
5.82M
                    GetNSOfLastXPathComponent(oField.GetXPath()));
423
                // If the field has a namespace that is not the one of its
424
                // class, then prefix its name with its namespace
425
5.82M
                if (!oNS.empty() && oNS != oClassNS &&
426
5.82M
                    !STARTS_WITH(oField.GetName(), (oNS + "_").c_str()))
427
0
                {
428
0
                    bHasDoneRenamingForThatCase = true;
429
0
                    const auto osNewName = oNS + "_" + oField.GetName();
430
0
                    oField.SetName(osNewName);
431
0
                    updateSetAndMapWithNewName(nFieldIdx, osNewName);
432
0
                    break;
433
0
                }
434
                // If it is an attribute without a particular namespace,
435
                // then suffix with _attr
436
5.82M
                else if (oNS.empty() &&
437
5.82M
                         oField.GetXPath().find('@') != std::string::npos &&
438
5.82M
                         oField.GetName().find("_attr") == std::string::npos)
439
72.9k
                {
440
72.9k
                    bHasDoneRenamingForThatCase = true;
441
72.9k
                    const auto osNewName = oField.GetName() + "_attr";
442
72.9k
                    oField.SetName(osNewName);
443
72.9k
                    updateSetAndMapWithNewName(nFieldIdx, osNewName);
444
72.9k
                    break;
445
72.9k
                }
446
447
5.75M
                oIterList = oIterListNext;
448
5.75M
            }
449
450
            // If none of the above renaming strategies have worked, then
451
            // append a counter to the duplicates.
452
2.92M
            if (!bHasDoneRenamingForThatCase)
453
2.84M
            {
454
2.84M
                int i = 0;
455
2.84M
                oIterList = list.begin();
456
8.60M
                while (oIterList != list.end())
457
5.75M
                {
458
5.75M
                    auto oIterListNext = oIterList;
459
5.75M
                    ++oIterListNext;
460
461
5.75M
                    const int nFieldIdx = *oIterList;
462
5.75M
                    GMLASField &oField = aoFields[nFieldIdx];
463
5.75M
                    if (i > 0)
464
2.90M
                    {
465
2.90M
                        const auto osNewName =
466
2.90M
                            oField.GetName() +
467
2.90M
                            CPLSPrintf("%d", static_cast<int>(i) + 1);
468
2.90M
                        oField.SetName(osNewName);
469
2.90M
                        updateSetAndMapWithNewName(nFieldIdx, osNewName);
470
2.90M
                    }
471
472
5.75M
                    ++i;
473
5.75M
                    oIterList = oIterListNext;
474
5.75M
                }
475
2.84M
            }
476
477
            // Update oSetDuplicates and oMapNameToFieldIndex if we have
478
            // no longer duplicates for the current name
479
2.92M
            if (list.size() <= 1)
480
2.85M
            {
481
2.85M
                if (list.empty())
482
0
                {
483
0
                    oMapNameToFieldIndex.erase(oIterMap);
484
0
                }
485
2.85M
                oSetDuplicates.erase(oIterSet);
486
2.85M
            }
487
488
2.92M
            oIterSet = oIterSetNext;
489
2.92M
        }
490
24.3k
    }
491
492
#ifdef DEBUG
493
    {
494
        // Check that the above algorithm managed to deduplicate names
495
        std::set<CPLString> oSetNames;
496
        for (const auto &oField : aoFields)
497
        {
498
            if (oField.GetCategory() == GMLASField::REGULAR)
499
            {
500
                const auto &osName = oField.GetName();
501
                CPLAssert(oSetNames.find(osName) == oSetNames.end());
502
                oSetNames.insert(osName);
503
            }
504
        }
505
    }
506
#endif
507
508
    // Now check if we must truncate names
509
12.0k
    if (m_nIdentifierMaxLength >= MIN_VALUE_OF_MAX_IDENTIFIER_LENGTH)
510
0
    {
511
0
        for (size_t i = 0; i < aoFields.size(); i++)
512
0
        {
513
0
            int nNameSize = static_cast<int>(aoFields[i].GetName().size());
514
            /* Somewhat arbitrary limitation to avoid performance issues in */
515
            /* OGRGMLASTruncateIdentifier() */
516
0
            if (nNameSize > 1024)
517
0
            {
518
0
                CPLError(CE_Failure, CPLE_NotSupported,
519
0
                         "Field name with excessive length (%d) found",
520
0
                         nNameSize);
521
0
                return false;
522
0
            }
523
0
            if (nNameSize > m_nIdentifierMaxLength)
524
0
            {
525
0
                aoFields[i].SetName(OGRGMLASTruncateIdentifier(
526
0
                    aoFields[i].GetName(), m_nIdentifierMaxLength));
527
0
            }
528
0
        }
529
0
    }
530
531
12.0k
    if (m_bPGIdentifierLaundering)
532
12.0k
    {
533
601k
        for (size_t i = 0; i < aoFields.size(); i++)
534
589k
        {
535
589k
            char *pszLaundered =
536
589k
                OGRPGCommonLaunderName(aoFields[i].GetName(), "GMLAS", false);
537
589k
            aoFields[i].SetName(pszLaundered);
538
589k
            CPLFree(pszLaundered);
539
589k
        }
540
12.0k
    }
541
542
    // Detect duplicated field names
543
12.0k
    std::map<CPLString, std::vector<int>> oSetNames;
544
601k
    for (int i = 0; i < static_cast<int>(aoFields.size()); i++)
545
589k
    {
546
589k
        if (aoFields[i].GetCategory() == GMLASField::REGULAR)
547
132k
        {
548
132k
            CPLString osName(aoFields[i].GetName());
549
132k
            if (m_bCaseInsensitiveIdentifier)
550
132k
                osName.toupper();
551
132k
            oSetNames[osName].push_back(i);
552
132k
        }
553
589k
    }
554
555
    // Iterate over the unique names
556
12.0k
    for (const auto &oIter : oSetNames)
557
132k
    {
558
        // Has it duplicates ?
559
132k
        const size_t nOccurrences = oIter.second.size();
560
132k
        if (nOccurrences > 1)
561
581
        {
562
1.86k
            for (size_t i = 0; i < nOccurrences; i++)
563
1.28k
            {
564
1.28k
                GMLASField &oField = aoFields[oIter.second[i]];
565
1.28k
                oField.SetName(OGRGMLASAddSerialNumber(
566
1.28k
                    oField.GetName(), static_cast<int>(i + 1), nOccurrences,
567
1.28k
                    m_nIdentifierMaxLength));
568
1.28k
            }
569
581
        }
570
132k
    }
571
572
    // Recursively process nested classes
573
12.0k
    std::vector<GMLASFeatureClass> &aoNestedClasses = oClass.GetNestedClasses();
574
14.6k
    for (size_t i = 0; i < aoNestedClasses.size(); i++)
575
2.60k
    {
576
2.60k
        if (!LaunderFieldNames(aoNestedClasses[i]))
577
0
            return false;
578
2.60k
    }
579
12.0k
    return true;
580
12.0k
}
581
582
/************************************************************************/
583
/*                       CollectClassesReferences()                     */
584
/************************************************************************/
585
586
void GMLASSchemaAnalyzer::CollectClassesReferences(
587
    GMLASFeatureClass &oClass, std::vector<GMLASFeatureClass *> &aoClasses)
588
81.1k
{
589
81.1k
    aoClasses.push_back(&oClass);
590
81.1k
    std::vector<GMLASFeatureClass> &aoNestedClasses = oClass.GetNestedClasses();
591
83.7k
    for (size_t i = 0; i < aoNestedClasses.size(); i++)
592
2.60k
    {
593
2.60k
        CollectClassesReferences(aoNestedClasses[i], aoClasses);
594
2.60k
    }
595
81.1k
}
596
597
/************************************************************************/
598
/*                         LaunderClassNames()                          */
599
/************************************************************************/
600
601
void GMLASSchemaAnalyzer::LaunderClassNames()
602
673
{
603
673
    std::vector<GMLASFeatureClass *> aoClasses;
604
79.2k
    for (size_t i = 0; i < m_aoClasses.size(); i++)
605
78.5k
    {
606
78.5k
        CollectClassesReferences(m_aoClasses[i], aoClasses);
607
78.5k
    }
608
609
673
    if (m_nIdentifierMaxLength >= MIN_VALUE_OF_MAX_IDENTIFIER_LENGTH)
610
0
    {
611
0
        for (size_t i = 0; i < aoClasses.size(); i++)
612
0
        {
613
0
            int nNameSize = static_cast<int>(aoClasses[i]->GetName().size());
614
0
            if (nNameSize > m_nIdentifierMaxLength)
615
0
            {
616
0
                aoClasses[i]->SetName(OGRGMLASTruncateIdentifier(
617
0
                    aoClasses[i]->GetName(), m_nIdentifierMaxLength));
618
0
            }
619
0
        }
620
0
    }
621
622
673
    if (m_bPGIdentifierLaundering)
623
673
    {
624
81.8k
        for (size_t i = 0; i < aoClasses.size(); i++)
625
81.1k
        {
626
81.1k
            char *pszLaundered =
627
81.1k
                OGRPGCommonLaunderName(aoClasses[i]->GetName(), "GMLAS", false);
628
81.1k
            aoClasses[i]->SetName(pszLaundered);
629
81.1k
            CPLFree(pszLaundered);
630
81.1k
        }
631
673
    }
632
633
    // Detect duplicated names. This should normally not happen in normal
634
    // conditions except if you have classes like
635
    // prefix_foo, prefix:foo, other_prefix:foo
636
    // or if names have been truncated in the previous step
637
673
    std::map<CPLString, std::vector<int>> oSetNames;
638
81.8k
    for (int i = 0; i < static_cast<int>(aoClasses.size()); i++)
639
81.1k
    {
640
81.1k
        CPLString osName(aoClasses[i]->GetName());
641
81.1k
        if (m_bCaseInsensitiveIdentifier)
642
81.1k
            osName.toupper();
643
81.1k
        oSetNames[osName].push_back(i);
644
81.1k
    }
645
646
    // Iterate over the unique names
647
673
    for (const auto &oIter : oSetNames)
648
14.7k
    {
649
        // Has it duplicates ?
650
14.7k
        const size_t nOccurrences = oIter.second.size();
651
14.7k
        if (nOccurrences > 1)
652
3.64k
        {
653
73.6k
            for (size_t i = 0; i < nOccurrences; i++)
654
70.0k
            {
655
70.0k
                GMLASFeatureClass *poClass = aoClasses[oIter.second[i]];
656
70.0k
                poClass->SetName(OGRGMLASAddSerialNumber(
657
70.0k
                    poClass->GetName(), static_cast<int>(i + 1), nOccurrences,
658
70.0k
                    m_nIdentifierMaxLength));
659
70.0k
            }
660
3.64k
        }
661
14.7k
    }
662
673
}
663
664
/************************************************************************/
665
/*                       GMLASUniquePtr()                               */
666
/************************************************************************/
667
668
// Poor-man std::unique_ptr
669
template <class T> class GMLASUniquePtr
670
{
671
    T *m_p;
672
673
    GMLASUniquePtr(const GMLASUniquePtr &);
674
    GMLASUniquePtr &operator=(const GMLASUniquePtr &);
675
676
  public:
677
2.91k
    explicit GMLASUniquePtr(T *p) : m_p(p)
678
2.91k
    {
679
2.91k
    }
GMLASUniquePtr<xercesc_4_0::XMLGrammarPool>::GMLASUniquePtr(xercesc_4_0::XMLGrammarPool*)
Line
Count
Source
677
1.04k
    explicit GMLASUniquePtr(T *p) : m_p(p)
678
1.04k
    {
679
1.04k
    }
GMLASUniquePtr<xercesc_4_0::SAX2XMLReader>::GMLASUniquePtr(xercesc_4_0::SAX2XMLReader*)
Line
Count
Source
677
1.86k
    explicit GMLASUniquePtr(T *p) : m_p(p)
678
1.86k
    {
679
1.86k
    }
680
681
    ~GMLASUniquePtr()
682
2.91k
    {
683
2.91k
        delete m_p;
684
2.91k
    }
GMLASUniquePtr<xercesc_4_0::XMLGrammarPool>::~GMLASUniquePtr()
Line
Count
Source
682
1.04k
    {
683
1.04k
        delete m_p;
684
1.04k
    }
GMLASUniquePtr<xercesc_4_0::SAX2XMLReader>::~GMLASUniquePtr()
Line
Count
Source
682
1.86k
    {
683
1.86k
        delete m_p;
684
1.86k
    }
685
686
    T *operator->() const
687
14.8k
    {
688
14.8k
        CPLAssert(m_p);
689
14.8k
        return m_p;
690
14.8k
    }
691
692
    T *get() const
693
4.39k
    {
694
4.39k
        return m_p;
695
4.39k
    }
GMLASUniquePtr<xercesc_4_0::XMLGrammarPool>::get() const
Line
Count
Source
693
2.53k
    {
694
2.53k
        return m_p;
695
2.53k
    }
GMLASUniquePtr<xercesc_4_0::SAX2XMLReader>::get() const
Line
Count
Source
693
1.86k
    {
694
1.86k
        return m_p;
695
1.86k
    }
696
697
    T *release()
698
    {
699
        T *ret = m_p;
700
        m_p = NULL;
701
        return ret;
702
    }
703
};
704
705
/************************************************************************/
706
/*                   GetTopElementDeclarationFromXPath()                */
707
/************************************************************************/
708
709
XSElementDeclaration *
710
GMLASSchemaAnalyzer::GetTopElementDeclarationFromXPath(const CPLString &osXPath,
711
                                                       XSModel *poModel)
712
9.46k
{
713
9.46k
    const char *pszTypename = osXPath.c_str();
714
9.46k
    const char *pszColon = strrchr(pszTypename, ':');
715
9.46k
    XSElementDeclaration *poEltDecl = nullptr;
716
9.46k
    if (pszColon != nullptr)
717
1.92k
    {
718
1.92k
        CPLString osNSPrefix = pszTypename;
719
1.92k
        osNSPrefix.resize(pszColon - pszTypename);
720
1.92k
        CPLString osName = pszColon + 1;
721
1.92k
        CPLString osNSURI;
722
723
1.92k
        for (const auto &oIterNS : m_oMapURIToPrefix)
724
6.60k
        {
725
6.60k
            const CPLString &osIterNSURI(oIterNS.first);
726
6.60k
            const CPLString &osIterNSPrefix(oIterNS.second);
727
6.60k
            if (osNSPrefix == osIterNSPrefix)
728
1.92k
            {
729
1.92k
                osNSURI = osIterNSURI;
730
1.92k
                break;
731
1.92k
            }
732
6.60k
        }
733
1.92k
        XMLCh *xmlNS = nullptr;
734
1.92k
        XMLCh *xmlName = nullptr;
735
1.92k
        try
736
1.92k
        {
737
1.92k
            xmlNS = XMLString::transcode(osNSURI);
738
1.92k
            xmlName = XMLString::transcode(osName);
739
1.92k
            poEltDecl = poModel->getElementDeclaration(xmlName, xmlNS);
740
1.92k
        }
741
1.92k
        catch (const TranscodingException &e)
742
1.92k
        {
743
0
            CPLError(CE_Failure, CPLE_AppDefined, "TranscodingException: %s",
744
0
                     transcode(e.getMessage()).c_str());
745
0
        }
746
1.92k
        XMLString::release(&xmlNS);
747
1.92k
        XMLString::release(&xmlName);
748
1.92k
    }
749
7.54k
    else
750
7.54k
    {
751
7.54k
        try
752
7.54k
        {
753
7.54k
            XMLCh *xmlName = XMLString::transcode(pszTypename);
754
7.54k
            poEltDecl = poModel->getElementDeclaration(xmlName, nullptr);
755
7.54k
            XMLString::release(&xmlName);
756
7.54k
        }
757
7.54k
        catch (const TranscodingException &e)
758
7.54k
        {
759
0
            CPLError(CE_Failure, CPLE_AppDefined, "TranscodingException: %s",
760
0
                     transcode(e.getMessage()).c_str());
761
0
        }
762
7.54k
    }
763
9.46k
    return poEltDecl;
764
9.46k
}
765
766
/************************************************************************/
767
/*                        IsEltCompatibleOfFC()                         */
768
/************************************************************************/
769
770
static XSComplexTypeDefinition *
771
IsEltCompatibleOfFC(XSElementDeclaration *poEltDecl)
772
520k
{
773
520k
    XSTypeDefinition *poTypeDef = poEltDecl->getTypeDefinition();
774
520k
    if (poTypeDef->getTypeCategory() == XSTypeDefinition::COMPLEX_TYPE &&
775
520k
        transcode(poEltDecl->getName()) != szFEATURE_COLLECTION)
776
520k
    {
777
520k
        XSComplexTypeDefinition *poCT =
778
520k
            reinterpret_cast<XSComplexTypeDefinition *>(poTypeDef);
779
520k
        XSComplexTypeDefinition::CONTENT_TYPE eContentType(
780
520k
            poCT->getContentType());
781
520k
        if (eContentType == XSComplexTypeDefinition::CONTENTTYPE_ELEMENT ||
782
520k
            eContentType == XSComplexTypeDefinition::CONTENTTYPE_MIXED)
783
493k
        {
784
493k
            return poCT;
785
493k
        }
786
520k
    }
787
27.3k
    return nullptr;
788
520k
}
789
790
/************************************************************************/
791
/*                          DerivesFromGMLFeature()                     */
792
/************************************************************************/
793
794
bool GMLASSchemaAnalyzer::DerivesFromGMLFeature(XSElementDeclaration *poEltDecl)
795
0
{
796
0
    XSElementDeclaration *poIter = poEltDecl;
797
0
    while (true)
798
0
    {
799
0
        XSElementDeclaration *poSubstGroup =
800
0
            poIter->getSubstitutionGroupAffiliation();
801
0
        if (poSubstGroup == nullptr)
802
0
            break;
803
0
        const CPLString osSubstNS(transcode(poSubstGroup->getNamespace()));
804
0
        const CPLString osSubstName(transcode(poSubstGroup->getName()));
805
0
        if (IsGMLNamespace(osSubstNS) && osSubstName == "_FeatureCollection")
806
0
        {
807
0
            return false;
808
0
        }
809
0
        if (IsGMLNamespace(osSubstNS) &&
810
0
            (osSubstName == "AbstractFeature" || osSubstName == "_Feature"))
811
0
        {
812
0
            return true;
813
0
        }
814
0
        poIter = poSubstGroup;
815
0
    }
816
0
    return false;
817
0
}
818
819
/************************************************************************/
820
/*                               Analyze()                              */
821
/************************************************************************/
822
823
bool GMLASSchemaAnalyzer::Analyze(GMLASXSDCache &oCache,
824
                                  const CPLString &osBaseDirname,
825
                                  std::vector<PairURIFilename> &aoXSDs,
826
                                  bool bSchemaFullChecking,
827
                                  bool bHandleMultipleImports)
828
1.04k
{
829
1.04k
    GMLASUniquePtr<XMLGrammarPool> poGrammarPool(
830
1.04k
        (new XMLGrammarPoolImpl(XMLPlatformUtils::fgMemoryManager)));
831
832
1.04k
    std::vector<CPLString> aoNamespaces;
833
1.04k
    GMLASAnalyzerEntityResolver oXSDEntityResolver(
834
1.04k
        CPLString(), m_oMapURIToPrefix, m_oMapDocNSURIToPrefix, oCache);
835
836
    // In this first pass we load the schemas that are directly pointed by
837
    // the user with the XSD open option, or that we found in the
838
    // xsi:schemaLocation attribute The namespaces of those schemas are the
839
    // "first choice" namespaces from which we will try to find elements to turn
840
    // them into layers
841
1.04k
    aoNamespaces.push_back("");
842
2.53k
    for (size_t i = 0; i < aoXSDs.size(); i++)
843
1.86k
    {
844
1.86k
        const CPLString osURI(aoXSDs[i].first);
845
1.86k
        const CPLString osXSDFilename(aoXSDs[i].second);
846
847
1.86k
        GMLASUniquePtr<SAX2XMLReader> poParser(
848
1.86k
            XMLReaderFactory::createXMLReader(XMLPlatformUtils::fgMemoryManager,
849
1.86k
                                              poGrammarPool.get()));
850
851
        // Commonly useful configuration.
852
        //
853
1.86k
        poParser->setFeature(XMLUni::fgSAX2CoreNameSpaces, true);
854
1.86k
        poParser->setFeature(XMLUni::fgSAX2CoreNameSpacePrefixes, true);
855
1.86k
        poParser->setFeature(XMLUni::fgSAX2CoreValidation, true);
856
857
        // Enable validation.
858
        //
859
1.86k
        poParser->setFeature(XMLUni::fgXercesSchema, true);
860
861
1.86k
#ifndef __COVERITY__
862
        // coverity[unsafe_xml_parse_config]
863
1.86k
        poParser->setFeature(XMLUni::fgXercesValidationErrorAsFatal, false);
864
1.86k
#endif
865
866
        // Use the loaded grammar during parsing.
867
        //
868
1.86k
        poParser->setFeature(XMLUni::fgXercesUseCachedGrammarInParse, true);
869
870
        // Don't load schemas from any other source (e.g., from XML document's
871
        // xsi:schemaLocation attributes).
872
        //
873
1.86k
        poParser->setFeature(XMLUni::fgXercesLoadSchema, false);
874
875
1.86k
        poParser->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution,
876
1.86k
                             true);
877
878
1.86k
        Grammar *poGrammar = nullptr;
879
1.86k
        if (!GMLASReader::LoadXSDInParser(
880
1.86k
                poParser.get(), oCache, oXSDEntityResolver, osBaseDirname,
881
1.86k
                osXSDFilename, &poGrammar, bSchemaFullChecking,
882
1.86k
                bHandleMultipleImports))
883
376
        {
884
376
            return false;
885
376
        }
886
887
        // Some .xsd like
888
        // http://www.opengis.net/gwml-main/2.1 ->
889
        // https://wfspoc.brgm-rec.fr/constellation/WS/wfs/BRGM:GWML2?request=DescribeFeatureType&version=2.0.0&service=WFS&namespace=xmlns(ns1=http://www.opengis.net/gwml-main/2.1)&typenames=ns1:GW_Aquifer
890
        // do not have a declared targetNamespace, so use the one of the
891
        // schemaLocation if the grammar returns an empty namespace.
892
1.48k
        CPLString osGrammarURI(transcode(poGrammar->getTargetNamespace()));
893
1.48k
        if (osGrammarURI.empty())
894
597
        {
895
597
            if (!osURI.empty())
896
496
                osGrammarURI = osURI;
897
597
        }
898
1.48k
        if (!osGrammarURI.empty())
899
1.38k
        {
900
            // Patch back the aoXSDs element in case we didn't know the
901
            // namespace URI initially
902
1.38k
            if (osURI.empty())
903
17
                aoXSDs[i].first = osGrammarURI;
904
1.38k
            aoNamespaces.push_back(std::move(osGrammarURI));
905
1.38k
        }
906
1.48k
    }
907
908
673
    m_osGMLVersionFound = oXSDEntityResolver.GetGMLVersionFound();
909
673
    m_oSetSchemaURLs = oXSDEntityResolver.GetSchemaURLS();
910
911
673
    m_oIgnoredXPathMatcher.SetDocumentMapURIToPrefix(m_oMapURIToPrefix);
912
673
    m_oChildrenElementsConstraintsXPathMatcher.SetDocumentMapURIToPrefix(
913
673
        m_oMapURIToPrefix);
914
673
    m_oForcedFlattenedXPathMatcher.SetDocumentMapURIToPrefix(m_oMapURIToPrefix);
915
673
    m_oDisabledFlattenedXPathMatcher.SetDocumentMapURIToPrefix(
916
673
        m_oMapURIToPrefix);
917
918
673
    XSModel *poModel = getGrammarPool(poGrammarPool.get());
919
673
    CPLAssert(poModel);  // should not be null according to doc
920
921
#if 0
922
    XSNamespaceItem* nsItem = poModel->getNamespaceItem(
923
                                        loadedGrammar->getTargetNamespace());
924
    if( nsItem == NULL )
925
    {
926
        CPLError(CE_Failure, CPLE_AppDefined,
927
                 "getNamespaceItem(%s) failed",
928
                 transcode(loadedGrammar->getTargetNamespace()).c_str());
929
        return false;
930
    }
931
#endif
932
933
673
    bool bFoundGMLFeature = false;
934
935
    // Second pass, in all namespaces, to figure out inheritance relationships
936
    // and group models that have names
937
673
    std::map<CPLString, CPLString> oMapURIToPrefixWithEmpty(m_oMapURIToPrefix);
938
673
    oMapURIToPrefixWithEmpty[""] = "";
939
673
    for (const auto &oIterNS : oMapURIToPrefixWithEmpty)
940
3.36k
    {
941
3.36k
        const CPLString &osNSURI(oIterNS.first);
942
3.36k
        if (osNSURI == szXS_URI || osNSURI == szXSI_URI ||
943
3.36k
            osNSURI == szXMLNS_URI || osNSURI == szXLINK_URI)
944
1.96k
        {
945
1.96k
            continue;
946
1.96k
        }
947
948
1.40k
        XMLCh *xmlNamespace = nullptr;
949
1.40k
        try
950
1.40k
        {
951
1.40k
            xmlNamespace = XMLString::transcode(osNSURI.c_str());
952
1.40k
        }
953
1.40k
        catch (const TranscodingException &e)
954
1.40k
        {
955
0
            CPLError(CE_Failure, CPLE_AppDefined, "TranscodingException: %s",
956
0
                     transcode(e.getMessage()).c_str());
957
0
            return false;
958
0
        }
959
960
1.40k
        XSNamedMap<XSObject> *poMapModelGroupDefinition =
961
1.40k
            poModel->getComponentsByNamespace(
962
1.40k
                XSConstants::MODEL_GROUP_DEFINITION, xmlNamespace);
963
964
        // Remember group models that have names
965
1.40k
        for (XMLSize_t i = 0; poMapModelGroupDefinition != nullptr &&
966
1.40k
                              i < poMapModelGroupDefinition->getLength();
967
1.40k
             i++)
968
0
        {
969
0
            XSModelGroupDefinition *modelGroupDefinition =
970
0
                reinterpret_cast<XSModelGroupDefinition *>(
971
0
                    poMapModelGroupDefinition->item(i));
972
0
            m_oMapModelGroupToMGD[modelGroupDefinition->getModelGroup()] =
973
0
                modelGroupDefinition;
974
0
        }
975
976
1.40k
        CPLDebug("GMLAS", "Discovering substitutions of %s (%s)",
977
1.40k
                 oIterNS.second.c_str(), osNSURI.c_str());
978
979
1.40k
        XSNamedMap<XSObject> *poMapElements = poModel->getComponentsByNamespace(
980
1.40k
            XSConstants::ELEMENT_DECLARATION, xmlNamespace);
981
982
1.40k
        for (XMLSize_t i = 0;
983
3.53k
             poMapElements != nullptr && i < poMapElements->getLength(); i++)
984
2.12k
        {
985
2.12k
            XSElementDeclaration *poEltDecl =
986
2.12k
                reinterpret_cast<XSElementDeclaration *>(
987
2.12k
                    poMapElements->item(i));
988
2.12k
            XSElementDeclaration *poSubstGroup =
989
2.12k
                poEltDecl->getSubstitutionGroupAffiliation();
990
2.12k
            const CPLString osEltXPath(
991
2.12k
                MakeXPath(transcode(poEltDecl->getNamespace()),
992
2.12k
                          transcode(poEltDecl->getName())));
993
2.12k
            m_oMapXPathToEltDecl[osEltXPath] = poEltDecl;
994
2.12k
            if (poSubstGroup)
995
0
            {
996
0
                m_oMapParentEltToChildElt[poSubstGroup].push_back(poEltDecl);
997
#ifdef DEBUG_VERBOSE
998
                CPLString osParentType(
999
                    MakeXPath(transcode(poSubstGroup->getNamespace()),
1000
                              transcode(poSubstGroup->getName())));
1001
                CPLDebug("GMLAS", "%s is a substitution for %s",
1002
                         osEltXPath.c_str(), osParentType.c_str());
1003
#endif
1004
1005
                // Check if this element derives from
1006
                // gml:_Feature/AbstractFeature
1007
0
                if (!bFoundGMLFeature && m_bInstantiateGMLFeaturesOnly &&
1008
0
                    !IsGMLNamespace(osNSURI) &&
1009
0
                    DerivesFromGMLFeature(poEltDecl))
1010
0
                {
1011
0
                    CPLDebug("GMLAS",
1012
0
                             "Restricting (in first pass) top level "
1013
0
                             "elements to those deriving from "
1014
0
                             "gml:_Feature/gml:AbstractFeature (due "
1015
0
                             "to %s found)",
1016
0
                             osEltXPath.c_str());
1017
0
                    bFoundGMLFeature = true;
1018
0
                }
1019
0
            }
1020
2.12k
        }
1021
1022
1.40k
        XMLString::release(&xmlNamespace);
1023
1.40k
    }
1024
1025
    // Check that we can find elements in the namespaces pointed in the
1026
    // xsi:schemaLocation of the document, then fallback to namespaces
1027
    // that might be indirectly imported by those first level namespaces
1028
673
    bool bFoundElementsInFirstChoiceNamespaces = false;
1029
673
    for (size_t iNS = 0;
1030
2.10k
         !bFoundElementsInFirstChoiceNamespaces && iNS < aoNamespaces.size();
1031
1.43k
         iNS++)
1032
1.43k
    {
1033
1.43k
        XMLCh *xmlNamespace = nullptr;
1034
1.43k
        try
1035
1.43k
        {
1036
1.43k
            xmlNamespace = XMLString::transcode(aoNamespaces[iNS].c_str());
1037
1.43k
        }
1038
1.43k
        catch (const TranscodingException &e)
1039
1.43k
        {
1040
0
            CPLError(CE_Failure, CPLE_AppDefined, "TranscodingException: %s",
1041
0
                     transcode(e.getMessage()).c_str());
1042
0
            return false;
1043
0
        }
1044
1045
1.43k
        XSNamedMap<XSObject> *poMapElements = poModel->getComponentsByNamespace(
1046
1.43k
            XSConstants::ELEMENT_DECLARATION, xmlNamespace);
1047
1.43k
        bFoundElementsInFirstChoiceNamespaces =
1048
1.43k
            poMapElements != nullptr && poMapElements->getLength() > 0;
1049
1.43k
        XMLString::release(&xmlNamespace);
1050
1.43k
    }
1051
673
    if (!bFoundElementsInFirstChoiceNamespaces)
1052
85
    {
1053
85
        CPLDebug("GMLAS", "Did not find element in 'first choice' namespaces. "
1054
85
                          "Falling back to the namespaces they import");
1055
85
        aoNamespaces.clear();
1056
85
        for (const auto &oIterNS : oMapURIToPrefixWithEmpty)
1057
468
        {
1058
468
            const CPLString &osNSURI(oIterNS.first);
1059
468
            if (osNSURI == szXS_URI || osNSURI == szXSI_URI ||
1060
468
                osNSURI == szXMLNS_URI || osNSURI == szXLINK_URI ||
1061
468
                osNSURI == szWFS_URI || osNSURI == szWFS20_URI ||
1062
468
                osNSURI == szGML_URI || osNSURI == szGML32_URI)
1063
235
            {
1064
                // Skip all boring namespaces
1065
235
                continue;
1066
235
            }
1067
233
            aoNamespaces.push_back(osNSURI);
1068
233
        }
1069
85
    }
1070
1071
    // Find which elements must be top levels (because referenced several
1072
    // times)
1073
673
    std::set<XSElementDeclaration *> oSetVisitedEltDecl;
1074
673
    std::set<XSModelGroup *> oSetVisitedModelGroups;
1075
673
    std::vector<XSElementDeclaration *> oVectorEltsForTopClass;
1076
1077
    // For some reason, different XSElementDeclaration* can point to the
1078
    // same element, but we only want to instantiate a single class.
1079
    // This is the case for base:SpatialDataSet in
1080
    // inspire/geologicalunit/geologicalunit.gml test dataset.
1081
673
    std::set<CPLString> aoSetXPathEltsForTopClass;
1082
1083
    // Third and fourth passes
1084
2.01k
    for (int iPass = 0; iPass < 2; ++iPass)
1085
1.34k
    {
1086
4.44k
        for (size_t iNS = 0; iNS < aoNamespaces.size(); iNS++)
1087
3.09k
        {
1088
3.09k
            XMLCh *xmlNamespace = nullptr;
1089
3.09k
            try
1090
3.09k
            {
1091
3.09k
                xmlNamespace = XMLString::transcode(aoNamespaces[iNS].c_str());
1092
3.09k
            }
1093
3.09k
            catch (const TranscodingException &e)
1094
3.09k
            {
1095
0
                CPLError(CE_Failure, CPLE_AppDefined,
1096
0
                         "TranscodingException: %s",
1097
0
                         transcode(e.getMessage()).c_str());
1098
0
                return false;
1099
0
            }
1100
1101
3.09k
            XSNamedMap<XSObject> *poMapElements =
1102
3.09k
                poModel->getComponentsByNamespace(
1103
3.09k
                    XSConstants::ELEMENT_DECLARATION, xmlNamespace);
1104
1105
3.09k
            for (XMLSize_t i = 0;
1106
8.58k
                 poMapElements != nullptr && i < poMapElements->getLength();
1107
5.49k
                 i++)
1108
5.49k
            {
1109
5.49k
                XSElementDeclaration *poEltDecl =
1110
5.49k
                    reinterpret_cast<XSElementDeclaration *>(
1111
5.49k
                        poMapElements->item(i));
1112
5.49k
                XSComplexTypeDefinition *poCT = IsEltCompatibleOfFC(poEltDecl);
1113
5.49k
                if (!poEltDecl->getAbstract() && poCT != nullptr)
1114
5.22k
                {
1115
5.22k
                    CPLString osXPath(
1116
5.22k
                        MakeXPath(transcode(poEltDecl->getNamespace()),
1117
5.22k
                                  transcode(poEltDecl->getName())));
1118
5.22k
                    if (!IsIgnoredXPath(osXPath))
1119
5.22k
                    {
1120
5.22k
                        if (bFoundGMLFeature && m_bInstantiateGMLFeaturesOnly &&
1121
5.22k
                            !DerivesFromGMLFeature(poEltDecl))
1122
0
                        {
1123
                            // Do nothing
1124
0
                        }
1125
5.22k
                        else if (iPass == 0)
1126
2.61k
                        {
1127
#ifdef DEBUG_VERBOSE
1128
                            CPLDebug(
1129
                                "GMLAS",
1130
                                "%s (%s) must be exposed as "
1131
                                "top-level (is top level in imported schemas)",
1132
                                osXPath.c_str(),
1133
                                transcode(
1134
                                    poEltDecl->getTypeDefinition()->getName())
1135
                                    .c_str());
1136
#endif
1137
2.61k
                            oSetVisitedEltDecl.insert(poEltDecl);
1138
2.61k
                            if (aoSetXPathEltsForTopClass.find(osXPath) ==
1139
2.61k
                                aoSetXPathEltsForTopClass.end())
1140
2.37k
                            {
1141
2.37k
                                m_oSetEltsForTopClass.insert(poEltDecl);
1142
2.37k
                                oVectorEltsForTopClass.push_back(poEltDecl);
1143
2.37k
                                aoSetXPathEltsForTopClass.insert(
1144
2.37k
                                    std::move(osXPath));
1145
2.37k
                            }
1146
2.61k
                        }
1147
2.61k
                        else
1148
2.61k
                        {
1149
2.61k
                            bool bSimpleEnoughOut = true;
1150
2.61k
                            int nSubCountSubEltOut = 0;
1151
2.61k
                            auto poParticle = poCT->getParticle();
1152
2.61k
                            if (poParticle)
1153
2.61k
                            {
1154
2.61k
                                CPL_IGNORE_RET_VAL(
1155
2.61k
                                    FindElementsWithMustBeToLevel(
1156
2.61k
                                        osXPath,
1157
2.61k
                                        poParticle->getModelGroupTerm(), 0,
1158
2.61k
                                        oSetVisitedEltDecl,
1159
2.61k
                                        oSetVisitedModelGroups,
1160
2.61k
                                        oVectorEltsForTopClass,
1161
2.61k
                                        aoSetXPathEltsForTopClass, poModel,
1162
2.61k
                                        bSimpleEnoughOut, nSubCountSubEltOut));
1163
2.61k
                            }
1164
2.61k
                        }
1165
5.22k
                    }
1166
5.22k
                }
1167
5.49k
            }
1168
1169
3.09k
            XMLString::release(&xmlNamespace);
1170
3.09k
        }
1171
1.34k
    }
1172
1173
    // Find ambiguous class names
1174
673
    {
1175
673
        for (const auto &oIter : m_oSetEltsForTopClass)
1176
9.46k
        {
1177
9.46k
            CPLString osName(transcode(oIter->getName()));
1178
9.46k
            m_oMapEltNamesToInstanceCount[osName]++;
1179
9.46k
        }
1180
673
    }
1181
1182
    // Instantiate all needed typenames
1183
673
    for (const auto &poEltDecl : oVectorEltsForTopClass)
1184
9.46k
    {
1185
9.46k
        const CPLString osXPath(MakeXPath(transcode(poEltDecl->getNamespace()),
1186
9.46k
                                          transcode(poEltDecl->getName())));
1187
1188
9.46k
        bool bError = false;
1189
9.46k
        bool bResolvedType =
1190
9.46k
            InstantiateClassFromEltDeclaration(poEltDecl, poModel, bError);
1191
9.46k
        if (bError)
1192
0
        {
1193
0
            return false;
1194
0
        }
1195
9.46k
        if (!bResolvedType)
1196
0
        {
1197
0
            CPLError(
1198
0
                CE_Failure, CPLE_AppDefined, "Couldn't resolve %s (%s)",
1199
0
                osXPath.c_str(),
1200
0
                transcode(poEltDecl->getTypeDefinition()->getName()).c_str());
1201
0
            return false;
1202
0
        }
1203
9.46k
    }
1204
1205
673
    LaunderClassNames();
1206
1207
673
    return true;
1208
673
}
1209
1210
/************************************************************************/
1211
/*                            GetAnnotationDoc()                        */
1212
/************************************************************************/
1213
1214
static CPLString GetAnnotationDoc(const XSAnnotation *annotation)
1215
225k
{
1216
225k
    if (!annotation)
1217
225k
        return CPLString();
1218
0
    CPLString osAnnot(transcode(annotation->getAnnotationString()));
1219
0
    CPLXMLNode *psRoot = CPLParseXMLString(osAnnot);
1220
0
    CPLStripXMLNamespace(psRoot, nullptr, TRUE);
1221
0
    CPLString osDoc(CPLGetXMLValue(psRoot, "=annotation.documentation", ""));
1222
0
    CPLDestroyXMLNode(psRoot);
1223
0
    return osDoc.Trim();
1224
225k
}
1225
1226
/************************************************************************/
1227
/*                            GetAnnotationDoc()                        */
1228
/************************************************************************/
1229
1230
static CPLString GetAnnotationDoc(const XSAnnotationList *annotationList)
1231
106k
{
1232
106k
    if (!annotationList)
1233
106k
        return CPLString();
1234
0
    CPLString osRet;
1235
0
    for (size_t i = 0; i < annotationList->size(); ++i)
1236
0
    {
1237
0
        CPLString osDoc(GetAnnotationDoc(annotationList->elementAt(i)));
1238
0
        if (!osDoc.empty())
1239
0
        {
1240
0
            if (!osRet.empty())
1241
0
                osRet += "\n";
1242
0
            osRet += osDoc;
1243
0
        }
1244
0
    }
1245
0
    return osRet;
1246
106k
}
1247
1248
/************************************************************************/
1249
/*                            GetAnnotationDoc()                        */
1250
/************************************************************************/
1251
1252
static CPLString GetAnnotationDoc(const XSElementDeclaration *poEltDecl)
1253
106k
{
1254
106k
    XSTypeDefinition *poTypeDef = poEltDecl->getTypeDefinition();
1255
106k
    CPLString osDoc = GetAnnotationDoc(poEltDecl->getAnnotation());
1256
106k
    XSAnnotationList *list = nullptr;
1257
168k
    while (poTypeDef != nullptr)
1258
168k
    {
1259
168k
        if (poTypeDef->getTypeCategory() == XSTypeDefinition::COMPLEX_TYPE)
1260
168k
        {
1261
168k
            XSComplexTypeDefinition *poCT =
1262
168k
                reinterpret_cast<XSComplexTypeDefinition *>(poTypeDef);
1263
168k
            list = poCT->getAnnotations();
1264
168k
        }
1265
320
        else if (poTypeDef->getTypeCategory() == XSTypeDefinition::SIMPLE_TYPE)
1266
320
        {
1267
320
            XSSimpleTypeDefinition *poST =
1268
320
                reinterpret_cast<XSSimpleTypeDefinition *>(poTypeDef);
1269
320
            list = poST->getAnnotations();
1270
320
        }
1271
168k
        if (list != nullptr)
1272
0
            break;
1273
168k
        XSTypeDefinition *poNewTypeDef = poTypeDef->getBaseType();
1274
168k
        if (poNewTypeDef == poTypeDef)
1275
106k
            break;
1276
61.7k
        poTypeDef = poNewTypeDef;
1277
61.7k
    }
1278
106k
    CPLString osDoc2 = GetAnnotationDoc(list);
1279
106k
    if (!osDoc.empty() && !osDoc2.empty())
1280
0
    {
1281
0
        osDoc += "\n";
1282
0
        osDoc += osDoc2;
1283
0
    }
1284
106k
    else if (!osDoc2.empty())
1285
0
        osDoc = std::move(osDoc2);
1286
106k
    return osDoc;
1287
106k
}
1288
1289
/************************************************************************/
1290
/*                  InstantiateClassFromEltDeclaration()                */
1291
/************************************************************************/
1292
1293
bool GMLASSchemaAnalyzer::InstantiateClassFromEltDeclaration(
1294
    XSElementDeclaration *poEltDecl, XSModel *poModel, bool &bError)
1295
9.46k
{
1296
9.46k
    bError = false;
1297
9.46k
    XSComplexTypeDefinition *poCT = IsEltCompatibleOfFC(poEltDecl);
1298
9.46k
    if (!poEltDecl->getAbstract() && poCT != nullptr)
1299
9.46k
    {
1300
9.46k
        GMLASFeatureClass oClass;
1301
9.46k
        const CPLString osEltName(transcode(poEltDecl->getName()));
1302
9.46k
        const CPLString osXPath(
1303
9.46k
            MakeXPath(transcode(poEltDecl->getNamespace()), osEltName));
1304
1305
9.46k
        if (IsIgnoredXPath(osXPath))
1306
0
        {
1307
#ifdef DEBUG_VERBOSE
1308
            CPLDebug("GMLAS", "%s is in ignored xpaths", osXPath.c_str());
1309
#endif
1310
0
            return false;
1311
0
        }
1312
1313
9.46k
        if (m_oMapEltNamesToInstanceCount[osEltName] > 1)
1314
10
        {
1315
10
            CPLString osLaunderedXPath(osXPath);
1316
10
            osLaunderedXPath.replaceAll(':', '_');
1317
10
            oClass.SetName(osLaunderedXPath);
1318
10
        }
1319
9.45k
        else
1320
9.45k
            oClass.SetName(osEltName);
1321
1322
#ifdef DEBUG_VERBOSE
1323
        CPLDebug("GMLAS", "Instantiating element %s", osXPath.c_str());
1324
#endif
1325
9.46k
        oClass.SetXPath(osXPath);
1326
9.46k
        oClass.SetIsTopLevelElt(
1327
9.46k
            GetTopElementDeclarationFromXPath(osXPath, poModel) != nullptr);
1328
1329
9.46k
        std::set<XSModelGroup *> oSetVisitedModelGroups;
1330
1331
9.46k
        oClass.SetDocumentation(GetAnnotationDoc(poEltDecl));
1332
1333
        // might be NULL on swe:values for example
1334
9.46k
        if (poCT->getParticle() != nullptr)
1335
9.46k
        {
1336
9.46k
            std::map<CPLString, int> oMapCountOccurrencesOfSameName;
1337
9.46k
            BuildMapCountOccurrencesOfSameName(
1338
9.46k
                poCT->getParticle()->getModelGroupTerm(),
1339
9.46k
                oMapCountOccurrencesOfSameName);
1340
1341
9.46k
            OGRwkbGeometryType eGeomType = wkbUnknown;
1342
9.46k
            if (IsGMLNamespace(transcode(poCT->getNamespace())) &&
1343
9.46k
                (eGeomType = GetOGRGeometryType(poCT)) != wkbNone)
1344
0
            {
1345
0
                GMLASField oField;
1346
0
                oField.SetName("geometry");
1347
0
                oField.SetMinOccurs(1);
1348
0
                oField.SetMaxOccurs(1);
1349
0
                oField.SetType(GMLAS_FT_GEOMETRY, szFAKEXS_GEOMETRY);
1350
0
                oField.SetGeomType(eGeomType);
1351
0
                oField.SetXPath(osXPath + szMATCH_ALL);
1352
0
                oField.SetIncludeThisEltInBlob(true);
1353
1354
0
                oClass.AddField(oField);
1355
0
            }
1356
9.46k
            else if (!ExploreModelGroup(
1357
9.46k
                         poCT->getParticle()->getModelGroupTerm(),
1358
9.46k
                         poCT->getAttributeUses(), oClass, 0,
1359
9.46k
                         oSetVisitedModelGroups, poModel,
1360
9.46k
                         oMapCountOccurrencesOfSameName))
1361
0
            {
1362
0
                bError = true;
1363
0
                return false;
1364
0
            }
1365
9.46k
        }
1366
0
        else
1367
0
        {
1368
            // TODO ?
1369
0
        }
1370
1371
9.46k
        if (!LaunderFieldNames(oClass))
1372
0
            return false;
1373
1374
9.46k
        m_aoClasses.push_back(std::move(oClass));
1375
9.46k
        return true;
1376
9.46k
    }
1377
0
    return false;
1378
9.46k
}
1379
1380
/************************************************************************/
1381
/*                 SetFieldTypeAndWidthFromDefinition()                 */
1382
/************************************************************************/
1383
1384
void GMLASSchemaAnalyzer::SetFieldTypeAndWidthFromDefinition(
1385
    XSSimpleTypeDefinition *poST, GMLASField &oField)
1386
75.2k
{
1387
75.2k
    int nMaxLength = 0;
1388
75.2k
    while (
1389
75.2k
        poST->getBaseType() != poST &&
1390
75.2k
        poST->getBaseType()->getTypeCategory() ==
1391
75.2k
            XSTypeDefinition::SIMPLE_TYPE &&
1392
75.2k
        !XMLString::equals(poST->getNamespace(), PSVIUni::fgNamespaceXmlSchema))
1393
0
    {
1394
0
        const XMLCh *maxLength =
1395
0
            poST->getLexicalFacetValue(XSSimpleTypeDefinition::FACET_LENGTH);
1396
0
        if (maxLength == nullptr)
1397
0
        {
1398
0
            maxLength = poST->getLexicalFacetValue(
1399
0
                XSSimpleTypeDefinition::FACET_MAXLENGTH);
1400
0
        }
1401
0
        if (maxLength != nullptr)
1402
0
            nMaxLength = MAX(nMaxLength, atoi(transcode(maxLength)));
1403
0
        poST = reinterpret_cast<XSSimpleTypeDefinition *>(poST->getBaseType());
1404
0
    }
1405
1406
75.2k
    if (XMLString::equals(poST->getNamespace(), PSVIUni::fgNamespaceXmlSchema))
1407
75.2k
    {
1408
75.2k
        CPLString osType(transcode(poST->getName()));
1409
75.2k
        oField.SetType(GMLASField::GetTypeFromString(osType), osType);
1410
75.2k
    }
1411
0
    else
1412
0
    {
1413
0
        CPLError(CE_Warning, CPLE_AppDefined, "Base type is not a xs: one ???");
1414
0
    }
1415
1416
75.2k
    oField.SetWidth(nMaxLength);
1417
75.2k
}
1418
1419
/************************************************************************/
1420
/*                              IsSame()                                */
1421
/*                                                                      */
1422
/* The objects returned by different PSVI API are not always the same   */
1423
/* so do content inspection to figure out if they are equivalent.       */
1424
/************************************************************************/
1425
1426
bool GMLASSchemaAnalyzer::IsSame(const XSModelGroup *poModelGroup1,
1427
                                 const XSModelGroup *poModelGroup2)
1428
0
{
1429
0
    if (poModelGroup1->getCompositor() != poModelGroup2->getCompositor())
1430
0
        return false;
1431
1432
0
    const XSParticleList *poParticleList1 = poModelGroup1->getParticles();
1433
0
    const XSParticleList *poParticleList2 = poModelGroup2->getParticles();
1434
0
    if (poParticleList1->size() != poParticleList2->size())
1435
0
        return false;
1436
1437
0
    for (size_t i = 0; i < poParticleList1->size(); ++i)
1438
0
    {
1439
0
        const XSParticle *poParticle1 = poParticleList1->elementAt(i);
1440
0
        const XSParticle *poParticle2 = poParticleList2->elementAt(i);
1441
0
        if (poParticle1->getTermType() != poParticle2->getTermType() ||
1442
0
            poParticle1->getMinOccurs() != poParticle2->getMinOccurs() ||
1443
0
            poParticle1->getMaxOccurs() != poParticle2->getMaxOccurs() ||
1444
0
            poParticle1->getMaxOccursUnbounded() !=
1445
0
                poParticle2->getMaxOccursUnbounded())
1446
0
        {
1447
0
            return false;
1448
0
        }
1449
0
        switch (poParticle1->getTermType())
1450
0
        {
1451
0
            case XSParticle::TERM_EMPTY:
1452
0
                break;
1453
1454
0
            case XSParticle::TERM_ELEMENT:
1455
0
            {
1456
0
                const XSElementDeclaration *poElt1 =
1457
0
                    const_cast<XSParticle *>(poParticle1)->getElementTerm();
1458
0
                const XSElementDeclaration *poElt2 =
1459
0
                    const_cast<XSParticle *>(poParticle2)->getElementTerm();
1460
                // Pointer comparison works here
1461
0
                if (poElt1 != poElt2)
1462
0
                    return false;
1463
0
                break;
1464
0
            }
1465
1466
0
            case XSParticle::TERM_MODELGROUP:
1467
0
            {
1468
0
                const XSModelGroup *psSubGroup1 =
1469
0
                    const_cast<XSParticle *>(poParticle1)->getModelGroupTerm();
1470
0
                const XSModelGroup *psSubGroup2 =
1471
0
                    const_cast<XSParticle *>(poParticle2)->getModelGroupTerm();
1472
0
                if (!IsSame(psSubGroup1, psSubGroup2))
1473
0
                    return false;
1474
0
                break;
1475
0
            }
1476
1477
0
            case XSParticle::TERM_WILDCARD:
1478
0
            {
1479
                // TODO: check that pointer comparison works
1480
0
                const XSWildcard *psWildcard1 =
1481
0
                    const_cast<XSParticle *>(poParticle1)->getWildcardTerm();
1482
0
                const XSWildcard *psWildcard2 =
1483
0
                    const_cast<XSParticle *>(poParticle2)->getWildcardTerm();
1484
0
                if (psWildcard1 != psWildcard2)
1485
0
                    return false;
1486
0
                break;
1487
0
            }
1488
1489
0
            default:
1490
0
            {
1491
0
                CPLAssert(FALSE);
1492
0
                return false;
1493
0
            }
1494
0
        }
1495
0
    }
1496
1497
0
    return true;
1498
0
}
1499
1500
/************************************************************************/
1501
/*                           GetGroupName()                             */
1502
/*                                                                      */
1503
/*  The model group object returned when exploring a high level model   */
1504
/*  group isn't the same object as the one returned by model group      */
1505
/*  definitions and has no name. So we have to investigate the content  */
1506
/*  of model groups to figure out if they are the same.                 */
1507
/************************************************************************/
1508
1509
XSModelGroupDefinition *
1510
GMLASSchemaAnalyzer::GetGroupDefinition(const XSModelGroup *poModelGroup)
1511
0
{
1512
0
    for (const auto &oIter : m_oMapModelGroupToMGD)
1513
0
    {
1514
0
        if (IsSame(poModelGroup, oIter.first))
1515
0
        {
1516
0
            return oIter.second;
1517
0
        }
1518
0
    }
1519
1520
0
    return nullptr;
1521
0
}
1522
1523
/************************************************************************/
1524
/*                              IsAnyType()                             */
1525
/************************************************************************/
1526
1527
static bool IsAnyType(XSComplexTypeDefinition *poType)
1528
95.5k
{
1529
95.5k
    if (XMLString::equals(poType->getBaseType()->getNamespace(),
1530
95.5k
                          PSVIUni::fgNamespaceXmlSchema) &&
1531
95.5k
        transcode(poType->getBaseType()->getName()) == szXS_ANY_TYPE)
1532
95.5k
    {
1533
95.5k
        XSParticle *poParticle = poType->getParticle();
1534
95.5k
        if (poParticle != nullptr)
1535
41.9k
        {
1536
41.9k
            XSModelGroup *poGroupTerm = poParticle->getModelGroupTerm();
1537
41.9k
            if (poGroupTerm != nullptr)
1538
41.9k
            {
1539
41.9k
                XSParticleList *poParticles = poGroupTerm->getParticles();
1540
41.9k
                if (poParticles != nullptr)
1541
41.9k
                {
1542
41.9k
                    return poParticles->size() == 1 &&
1543
41.9k
                           poParticles->elementAt(0)->getTermType() ==
1544
36.7k
                               XSParticle::TERM_WILDCARD;
1545
41.9k
                }
1546
41.9k
            }
1547
41.9k
        }
1548
53.5k
        else if (poType->getDerivationMethod() ==
1549
53.5k
                 XSConstants::DERIVATION_EXTENSION)
1550
0
        {
1551
            // swe:values case
1552
0
            return true;
1553
0
        }
1554
95.5k
    }
1555
53.5k
    return false;
1556
95.5k
}
1557
1558
/************************************************************************/
1559
/*                       SetFieldFromAttribute()                        */
1560
/************************************************************************/
1561
1562
bool GMLASSchemaAnalyzer::SetFieldFromAttribute(GMLASField &oField,
1563
                                                XSAttributeUse *poAttr,
1564
                                                const CPLString &osXPathPrefix,
1565
                                                const CPLString &osNamePrefix)
1566
75.0k
{
1567
75.0k
    const XSAttributeDeclaration *poAttrDecl = poAttr->getAttrDeclaration();
1568
75.0k
    const CPLString osNS(transcode(poAttrDecl->getNamespace()));
1569
75.0k
    const CPLString osName(transcode(poAttrDecl->getName()));
1570
1571
75.0k
    if (osNamePrefix.empty())
1572
22.6k
        oField.SetName(osName);
1573
52.4k
    else
1574
52.4k
        oField.SetName(osNamePrefix + "_" + osName);
1575
1576
75.0k
    XSSimpleTypeDefinition *poAttrType = poAttrDecl->getTypeDefinition();
1577
75.0k
    if (!poAttrType)
1578
0
    {
1579
0
        CPLError(CE_Failure, CPLE_AppDefined,
1580
0
                 "Cannot get type definition for attribute %s",
1581
0
                 oField.GetName().c_str());
1582
0
        return false;
1583
0
    }
1584
1585
75.0k
    SetFieldTypeAndWidthFromDefinition(poAttrType, oField);
1586
1587
75.0k
    oField.SetXPath(osXPathPrefix + "/@" + MakeXPath(osNS, osName));
1588
75.0k
    if (poAttr->getRequired())
1589
73.5k
    {
1590
73.5k
        oField.SetNotNullable(true);
1591
73.5k
    }
1592
75.0k
    oField.SetMinOccurs(oField.IsNotNullable() ? 1 : 0);
1593
75.0k
    oField.SetMaxOccurs(1);
1594
75.0k
    if (poAttr->getConstraintType() == XSConstants::VALUE_CONSTRAINT_FIXED)
1595
9
    {
1596
9
        oField.SetFixedValue(transcode(poAttr->getConstraintValue()));
1597
9
    }
1598
75.0k
    else if (poAttr->getConstraintType() ==
1599
75.0k
             XSConstants::VALUE_CONSTRAINT_DEFAULT)
1600
0
    {
1601
0
        oField.SetDefaultValue(transcode(poAttr->getConstraintValue()));
1602
0
    }
1603
1604
75.0k
    const bool bIsList =
1605
75.0k
        (poAttrType->getVariety() == XSSimpleTypeDefinition::VARIETY_LIST);
1606
75.0k
    if (bIsList)
1607
0
    {
1608
0
        SetFieldTypeAndWidthFromDefinition(poAttrType->getItemType(), oField);
1609
0
        if (m_bUseArrays && IsCompatibleOfArray(oField.GetType()))
1610
0
        {
1611
0
            oField.SetList(true);
1612
0
            oField.SetArray(true);
1613
0
        }
1614
0
        else
1615
0
        {
1616
            // We should probably create an auxiliary table here, but this
1617
            // is too corner case for now...
1618
0
            oField.SetType(GMLAS_FT_STRING, szXS_STRING);
1619
0
        }
1620
0
    }
1621
1622
75.0k
    oField.SetDocumentation(GetAnnotationDoc(poAttrDecl->getAnnotation()));
1623
1624
75.0k
    return true;
1625
75.0k
}
1626
1627
/************************************************************************/
1628
/*                      GetConcreteImplementationTypes()                */
1629
/************************************************************************/
1630
1631
void GMLASSchemaAnalyzer::GetConcreteImplementationTypes(
1632
    XSElementDeclaration *poParentElt,
1633
    std::vector<XSElementDeclaration *> &apoImplEltList)
1634
1.05M
{
1635
1.05M
    const auto oIter = m_oMapParentEltToChildElt.find(poParentElt);
1636
1.05M
    if (oIter != m_oMapParentEltToChildElt.end())
1637
0
    {
1638
0
        for (size_t j = 0; j < oIter->second.size(); j++)
1639
0
        {
1640
0
            XSElementDeclaration *poSubElt = oIter->second[j];
1641
0
            if (IsEltCompatibleOfFC(poSubElt))
1642
0
            {
1643
0
                if (!poSubElt->getAbstract())
1644
0
                {
1645
0
                    apoImplEltList.push_back(poSubElt);
1646
0
                }
1647
0
            }
1648
0
            GetConcreteImplementationTypes(poSubElt, apoImplEltList);
1649
0
        }
1650
0
    }
1651
1.05M
}
1652
1653
/************************************************************************/
1654
/*                       GetConstraintChildrenElements()                */
1655
/************************************************************************/
1656
1657
std::vector<XSElementDeclaration *>
1658
GMLASSchemaAnalyzer::GetConstraintChildrenElements(const CPLString &osFullXPath)
1659
1.05M
{
1660
1661
1.05M
    std::vector<XSElementDeclaration *> oVectorRes;
1662
1.05M
    CPLString osMatched;
1663
1.05M
    if (m_oChildrenElementsConstraintsXPathMatcher.MatchesRefXPath(osFullXPath,
1664
1.05M
                                                                   osMatched))
1665
0
    {
1666
0
        const std::vector<CPLString> &oVector =
1667
0
            m_oMapChildrenElementsConstraints[osMatched];
1668
0
        const std::map<CPLString, CPLString> &oMapPrefixToURI =
1669
0
            m_oChildrenElementsConstraintsXPathMatcher.GetMapPrefixToURI();
1670
0
        for (size_t j = 0; j < oVector.size(); ++j)
1671
0
        {
1672
0
            const CPLString &osSubElt(oVector[j]);
1673
0
            CPLString osSubEltPrefix;
1674
0
            CPLString osSubEltURI;
1675
0
            CPLString osSubEltType(osSubElt);
1676
0
            size_t nPos = osSubElt.find(":");
1677
0
            if (nPos != std::string::npos)
1678
0
            {
1679
0
                osSubEltPrefix = osSubElt.substr(0, nPos);
1680
0
                osSubEltType = osSubElt.substr(nPos + 1);
1681
1682
0
                const auto oIter2 = oMapPrefixToURI.find(osSubEltPrefix);
1683
0
                if (oIter2 != oMapPrefixToURI.end())
1684
0
                {
1685
0
                    osSubEltURI = oIter2->second;
1686
0
                }
1687
0
                else
1688
0
                {
1689
0
                    CPLError(CE_Warning, CPLE_AppDefined,
1690
0
                             "Cannot find prefix of type constraint %s",
1691
0
                             osSubElt.c_str());
1692
0
                }
1693
0
            }
1694
1695
0
            const CPLString osSubEltXPath(MakeXPath(osSubEltURI, osSubEltType));
1696
0
            const auto oIter2 = m_oMapXPathToEltDecl.find(osSubEltXPath);
1697
0
            if (oIter2 != m_oMapXPathToEltDecl.end())
1698
0
            {
1699
0
                XSElementDeclaration *poSubElt = oIter2->second;
1700
0
                if (IsEltCompatibleOfFC(poSubElt))
1701
0
                {
1702
0
                    oVectorRes.push_back(poSubElt);
1703
0
                }
1704
0
            }
1705
0
            else
1706
0
            {
1707
0
                CPLError(
1708
0
                    CE_Warning, CPLE_AppDefined,
1709
0
                    "Cannot find element declaration of type constraint %s",
1710
0
                    osSubElt.c_str());
1711
0
            }
1712
0
        }
1713
0
    }
1714
1.05M
    return oVectorRes;
1715
1.05M
}
1716
1717
/************************************************************************/
1718
/*                        GetOGRGeometryType()                          */
1719
/************************************************************************/
1720
1721
static OGRwkbGeometryType GetOGRGeometryType(XSTypeDefinition *poTypeDef)
1722
261
{
1723
261
    const struct MyStruct
1724
261
    {
1725
261
        const char *pszName;
1726
261
        OGRwkbGeometryType eType;
1727
261
    } asArray[] = {{"GeometryPropertyType", wkbUnknown},
1728
261
                   {"PointPropertyType", wkbPoint},
1729
261
                   {"BoundingShapeType", wkbPolygon},
1730
261
                   {"PolygonPropertyType", wkbPolygon},
1731
261
                   {"LineStringPropertyType", wkbLineString},
1732
261
                   {"MultiPointPropertyType", wkbMultiPoint},
1733
261
                   {"MultiPolygonPropertyType", wkbMultiPolygon},
1734
261
                   {"MultiLineStringPropertyType", wkbMultiLineString},
1735
261
                   {"MultiGeometryPropertyType", wkbGeometryCollection},
1736
261
                   {"MultiCurvePropertyType", wkbMultiCurve},
1737
261
                   {"MultiSurfacePropertyType", wkbMultiSurface},
1738
261
                   {"MultiSolidPropertyType", wkbUnknown},
1739
                   // GeometryArrayPropertyType ?
1740
261
                   {"GeometricPrimitivePropertyType", wkbUnknown},
1741
261
                   {"CurvePropertyType", wkbCurve},
1742
261
                   {"SurfacePropertyType", wkbSurface},
1743
                   // SurfaceArrayPropertyType ?
1744
                   // AbstractRingPropertyType ?
1745
                   // LinearRingPropertyType ?
1746
261
                   {"CompositeCurvePropertyType", wkbCurve},
1747
261
                   {"CompositeSurfacePropertyType", wkbSurface},
1748
261
                   {"CompositeSolidPropertyType", wkbUnknown},
1749
261
                   {"GeometricComplexPropertyType", wkbUnknown},
1750
261
                   {"SolidPropertyType", wkbPolyhedralSurface}};
1751
1752
261
    CPLString osName(transcode(poTypeDef->getName()));
1753
5.48k
    for (size_t i = 0; i < CPL_ARRAYSIZE(asArray); ++i)
1754
5.22k
    {
1755
5.22k
        if (osName == asArray[i].pszName)
1756
0
            return asArray[i].eType;
1757
5.22k
    }
1758
261
    return wkbNone;
1759
1760
#if 0
1761
  <complexType name="CurveSegmentArrayPropertyType">
1762
  <complexType name="KnotPropertyType">
1763
  <complexType name="SurfacePatchArrayPropertyType">
1764
  <complexType name="RingPropertyType">
1765
  <complexType name="PolygonPatchArrayPropertyType">
1766
  <complexType name="TrianglePatchArrayPropertyType">
1767
  <complexType name="LineStringSegmentArrayPropertyType">
1768
  <complexType name="SolidArrayPropertyType">
1769
#endif
1770
261
}
1771
1772
/************************************************************************/
1773
/*                 GetOGRGeometryTypeFromGMLEltName()                   */
1774
/************************************************************************/
1775
1776
static OGRwkbGeometryType
1777
GetOGRGeometryTypeFromGMLEltName(const CPLString &osEltName)
1778
0
{
1779
0
    const struct MyStruct
1780
0
    {
1781
0
        const char *pszName;
1782
0
        OGRwkbGeometryType eType;
1783
0
    } asArray[] = {
1784
0
        {"Point", wkbPoint},
1785
0
        {"Polygon", wkbPolygon},
1786
0
        {"Envelope", wkbPolygon},
1787
0
        {"LineString", wkbLineString},
1788
0
        {"MultiPoint", wkbMultiPoint},
1789
0
        {"MultiPolygon", wkbMultiPolygon},
1790
0
        {"MultiLineString", wkbMultiLineString},
1791
0
        {"MultiGeometry", wkbGeometryCollection},
1792
0
        {"MultiCurve", wkbMultiCurve},
1793
0
        {"MultiSurface", wkbMultiSurface},
1794
0
        {"MultiSolid", wkbUnknown},
1795
0
        {"Curve", wkbCurve},
1796
0
        {"Surface", wkbSurface},
1797
0
        {"CompositeCurve", wkbCurve},
1798
0
        {"CompositeSurface", wkbSurface},
1799
0
        {"CompositeSolid", wkbUnknown},
1800
0
        {"GeometricComplex", wkbUnknown},
1801
0
    };
1802
1803
0
    for (size_t i = 0; i < CPL_ARRAYSIZE(asArray); ++i)
1804
0
    {
1805
0
        if (osEltName == asArray[i].pszName)
1806
0
            return asArray[i].eType;
1807
0
    }
1808
0
    return wkbNone;
1809
0
}
1810
1811
/************************************************************************/
1812
/*                      CreateNonNestedRelationship()                  */
1813
/************************************************************************/
1814
1815
void GMLASSchemaAnalyzer::CreateNonNestedRelationship(
1816
    XSElementDeclaration *poElt,
1817
    std::vector<XSElementDeclaration *> &apoImplEltList,
1818
    GMLASFeatureClass &oClass, int nMaxOccurs, bool bEltNameWillNeedPrefix,
1819
    bool bForceJunctionTable, bool bCaseOfConstraintChildren)
1820
453k
{
1821
453k
    const CPLString osEltPrefix(GetPrefix(transcode(poElt->getNamespace())));
1822
453k
    const CPLString osEltName(transcode(poElt->getName()));
1823
453k
    const CPLString osOnlyElementXPath(
1824
453k
        MakeXPath(transcode(poElt->getNamespace()), osEltName));
1825
453k
    const CPLString osElementXPath(oClass.GetXPath() + "/" +
1826
453k
                                   osOnlyElementXPath);
1827
1828
453k
    if (!poElt->getAbstract() && !bCaseOfConstraintChildren)
1829
453k
    {
1830
453k
        apoImplEltList.insert(apoImplEltList.begin(), poElt);
1831
453k
    }
1832
1833
453k
    std::set<CPLString> aoSetSubEltXPath;
1834
453k
    if (nMaxOccurs == 1 && !bForceJunctionTable)
1835
384k
    {
1836
        // If the field isn't repeated, then we can link to each
1837
        // potential realization types with a field
1838
1839
769k
        for (size_t j = 0; j < apoImplEltList.size(); j++)
1840
384k
        {
1841
384k
            XSElementDeclaration *poSubElt = apoImplEltList[j];
1842
384k
            const CPLString osSubEltName(transcode(poSubElt->getName()));
1843
384k
            const CPLString osSubEltXPath(
1844
384k
                MakeXPath(transcode(poSubElt->getNamespace()), osSubEltName));
1845
1846
            // For AbstractFeature_SpatialDataSet_pkid in SpatialDataSet_member
1847
384k
            if (aoSetSubEltXPath.find(osSubEltXPath) != aoSetSubEltXPath.end())
1848
0
            {
1849
0
                continue;
1850
0
            }
1851
384k
            aoSetSubEltXPath.insert(osSubEltXPath);
1852
1853
384k
            const CPLString osRealFullXPath(oClass.GetXPath() + "/" +
1854
384k
                                            ((bCaseOfConstraintChildren)
1855
384k
                                                 ? osOnlyElementXPath + "/"
1856
384k
                                                 : CPLString("")) +
1857
384k
                                            osSubEltXPath);
1858
1859
384k
            if (IsIgnoredXPath(osRealFullXPath))
1860
0
            {
1861
#ifdef DEBUG_VERBOSE
1862
                CPLDebug("GMLAS", "%s is in ignored xpaths",
1863
                         osRealFullXPath.c_str());
1864
#endif
1865
0
                continue;
1866
0
            }
1867
1868
384k
            GMLASField oField;
1869
384k
            if (apoImplEltList.size() > 1 || bCaseOfConstraintChildren)
1870
0
            {
1871
0
                if (m_oMapEltNamesToInstanceCount[osSubEltName] > 1)
1872
0
                {
1873
0
                    CPLString osLaunderedXPath(osSubEltXPath);
1874
0
                    osLaunderedXPath.replaceAll(':', '_');
1875
0
                    oField.SetName(((bEltNameWillNeedPrefix) ? osEltPrefix + "_"
1876
0
                                                             : CPLString()) +
1877
0
                                   transcode(poElt->getName()) + "_" +
1878
0
                                   osLaunderedXPath + "_pkid");
1879
0
                }
1880
0
                else
1881
0
                {
1882
0
                    oField.SetName(((bEltNameWillNeedPrefix) ? osEltPrefix + "_"
1883
0
                                                             : CPLString()) +
1884
0
                                   transcode(poElt->getName()) + "_" +
1885
0
                                   osSubEltName + "_pkid");
1886
0
                }
1887
0
            }
1888
384k
            else
1889
384k
            {
1890
384k
                oField.SetName(transcode(poElt->getName()) + "_pkid");
1891
384k
            }
1892
384k
            oField.SetXPath(osRealFullXPath);
1893
384k
            oField.SetMinOccurs(0);
1894
384k
            oField.SetMaxOccurs(nMaxOccurs);
1895
384k
            oField.SetCategory(GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK);
1896
384k
            oField.SetRelatedClassXPath(osSubEltXPath);
1897
384k
            oField.SetType(GMLAS_FT_STRING, szXS_STRING);
1898
384k
            oClass.AddField(oField);
1899
384k
        }
1900
384k
    }
1901
69.0k
    else
1902
69.0k
    {
1903
        // If the field is repeated, we need to use junction
1904
        // tables
1905
138k
        for (size_t j = 0; j < apoImplEltList.size(); j++)
1906
69.0k
        {
1907
69.0k
            XSElementDeclaration *poSubElt = apoImplEltList[j];
1908
69.0k
            const CPLString osSubEltName(transcode(poSubElt->getName()));
1909
69.0k
            const CPLString osSubEltXPath(
1910
69.0k
                MakeXPath(transcode(poSubElt->getNamespace()), osSubEltName));
1911
1912
            // For AbstractFeature_SpatialDataSet_pkid in SpatialDataSet_member
1913
69.0k
            if (aoSetSubEltXPath.find(osSubEltXPath) != aoSetSubEltXPath.end())
1914
0
            {
1915
0
                continue;
1916
0
            }
1917
69.0k
            aoSetSubEltXPath.insert(osSubEltXPath);
1918
1919
            // Instantiate a junction table
1920
69.0k
            GMLASFeatureClass oJunctionTable;
1921
1922
69.0k
            if (m_oMapEltNamesToInstanceCount[osSubEltName] > 1)
1923
0
            {
1924
0
                CPLString osLaunderedXPath(osSubEltXPath);
1925
0
                osLaunderedXPath.replaceAll(':', '_');
1926
0
                oJunctionTable.SetName(oClass.GetName() + "_" +
1927
0
                                       transcode(poElt->getName()) + "_" +
1928
0
                                       osLaunderedXPath);
1929
0
            }
1930
69.0k
            else
1931
69.0k
            {
1932
69.0k
                oJunctionTable.SetName(oClass.GetName() + "_" +
1933
69.0k
                                       transcode(poElt->getName()) + "_" +
1934
69.0k
                                       osSubEltName);
1935
69.0k
            }
1936
            // Create a fake XPath binding the parent xpath (to an abstract
1937
            // element) to the child element
1938
69.0k
            oJunctionTable.SetXPath(
1939
69.0k
                BuildJunctionTableXPath(osElementXPath, osSubEltXPath));
1940
69.0k
            oJunctionTable.SetParentXPath(oClass.GetXPath());
1941
69.0k
            oJunctionTable.SetChildXPath(osSubEltXPath);
1942
69.0k
            m_aoClasses.push_back(std::move(oJunctionTable));
1943
1944
            // Add an abstract field
1945
69.0k
            GMLASField oField;
1946
69.0k
            oField.SetName(
1947
69.0k
                ((bEltNameWillNeedPrefix) ? osEltPrefix + "_" : CPLString()) +
1948
69.0k
                osEltName + "_" + osSubEltName);
1949
69.0k
            oField.SetXPath(oClass.GetXPath() + "/" +
1950
69.0k
                            ((bCaseOfConstraintChildren)
1951
69.0k
                                 ? osOnlyElementXPath + "/"
1952
69.0k
                                 : CPLString("")) +
1953
69.0k
                            osSubEltXPath);
1954
69.0k
            oField.SetMinOccurs(0);
1955
69.0k
            oField.SetMaxOccurs(nMaxOccurs);
1956
69.0k
            oField.SetAbstractElementXPath(osElementXPath);
1957
69.0k
            oField.SetRelatedClassXPath(osSubEltXPath);
1958
69.0k
            oField.SetCategory(
1959
69.0k
                GMLASField::PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE);
1960
69.0k
            oClass.AddField(oField);
1961
69.0k
        }
1962
69.0k
    }
1963
1964
#if 0
1965
    GMLASField oField;
1966
    oField.SetName( transcode(poElt->getName()) );
1967
    oField.SetXPath( osElementXPath );
1968
    oField.SetMinOccurs( poParticle->getMinOccurs() );
1969
    oField.SetMaxOccurs( poParticle->getMaxOccursUnbounded() ?
1970
        MAXOCCURS_UNLIMITED : poParticle->getMaxOccurs() );
1971
1972
    for( size_t j = 0; j < apoImplEltList.size(); j++ )
1973
    {
1974
        XSElementDeclaration* poSubElt = apoImplEltList[j];
1975
        XSTypeDefinition* poSubEltType =
1976
                                    poSubElt->getTypeDefinition();
1977
        XSComplexTypeDefinition* poCT =
1978
            reinterpret_cast<XSComplexTypeDefinition*>(poSubEltType);
1979
1980
        GMLASFeatureClass oNestedClass;
1981
        oNestedClass.SetName( oClass.GetName() + "_" +
1982
                    transcode(poSubElt->getName()) );
1983
        oNestedClass.SetXPath( oClass.GetXPath() + "/" +
1984
            MakeXPath(transcode(poSubElt->getNamespace()),
1985
                        transcode(poSubElt->getName())) );
1986
1987
        std::set<XSModelGroup*>
1988
            oSetNewVisitedModelGroups(oSetVisitedModelGroups);
1989
        if( !ExploreModelGroup(
1990
                poCT->getParticle()->getModelGroupTerm(),
1991
                NULL,
1992
                oNestedClass,
1993
                nRecursionCounter + 1,
1994
                oSetNewVisitedModelGroups ) )
1995
        {
1996
            return false;
1997
        }
1998
1999
        oClass.AddNestedClass( oNestedClass );
2000
    }
2001
2002
    if( !apoImplEltList.empty() )
2003
    {
2004
        oField.SetAbstract(true);
2005
    }
2006
    else
2007
    {
2008
        oField.SetType( GMLAS_FT_ANYTYPE, "anyType" );
2009
        oField.SetXPath( oClass.GetXPath() + "/" + "*" );
2010
        oField.SetIncludeThisEltInBlob( true );
2011
    }
2012
    oClass.AddField( oField );
2013
#endif
2014
453k
}
2015
2016
/************************************************************************/
2017
/*                          IsIgnoredXPath()                            */
2018
/************************************************************************/
2019
2020
bool GMLASSchemaAnalyzer::IsIgnoredXPath(const CPLString &osXPath)
2021
1.53M
{
2022
1.53M
    CPLString osIgnored;
2023
1.53M
    return m_oIgnoredXPathMatcher.MatchesRefXPath(osXPath, osIgnored);
2024
1.53M
}
2025
2026
/************************************************************************/
2027
/*                     FindElementsWithMustBeToLevel()                  */
2028
/************************************************************************/
2029
2030
bool GMLASSchemaAnalyzer::FindElementsWithMustBeToLevel(
2031
    const CPLString &osParentXPath, XSModelGroup *poModelGroup,
2032
    int nRecursionCounter, std::set<XSElementDeclaration *> &oSetVisitedEltDecl,
2033
    std::set<XSModelGroup *> &oSetVisitedModelGroups,
2034
    std::vector<XSElementDeclaration *> &oVectorEltsForTopClass,
2035
    std::set<CPLString> &aoSetXPathEltsForTopClass, XSModel *poModel,
2036
    bool &bSimpleEnoughOut, int &nCountSubEltsOut)
2037
22.6k
{
2038
22.6k
    const bool bAlreadyVisitedMG = (oSetVisitedModelGroups.find(poModelGroup) !=
2039
22.6k
                                    oSetVisitedModelGroups.end());
2040
2041
22.6k
    oSetVisitedModelGroups.insert(poModelGroup);
2042
2043
22.6k
    if (nRecursionCounter == 100)
2044
0
    {
2045
        // Presumably an hostile schema
2046
0
        CPLError(CE_Failure, CPLE_AppDefined,
2047
0
                 "Schema analysis failed due to too deeply nested model");
2048
0
        return false;
2049
0
    }
2050
2051
22.6k
    {
2052
22.6k
        CPLString osIgnored;
2053
22.6k
        if (m_oDisabledFlattenedXPathMatcher.MatchesRefXPath(osParentXPath,
2054
22.6k
                                                             osIgnored))
2055
0
        {
2056
0
            bSimpleEnoughOut = false;
2057
0
        }
2058
22.6k
    }
2059
2060
22.6k
    XSParticleList *poParticles = poModelGroup->getParticles();
2061
546k
    for (size_t i = 0; i < poParticles->size(); ++i)
2062
524k
    {
2063
524k
        XSParticle *poParticle = poParticles->elementAt(i);
2064
2065
524k
        const bool bRepeatedParticle = poParticle->getMaxOccursUnbounded() ||
2066
524k
                                       poParticle->getMaxOccurs() > 1;
2067
2068
524k
        if (poParticle->getTermType() == XSParticle::TERM_ELEMENT)
2069
506k
        {
2070
506k
            XSElementDeclaration *poElt = poParticle->getElementTerm();
2071
506k
            XSTypeDefinition *poTypeDef = poElt->getTypeDefinition();
2072
506k
            const CPLString osEltName(transcode(poElt->getName()));
2073
506k
            const CPLString osEltNS(transcode(poElt->getNamespace()));
2074
506k
            CPLString osXPath(MakeXPath(osEltNS, osEltName));
2075
506k
            const CPLString osFullXPath(osParentXPath + "/" + osXPath);
2076
2077
#ifdef DEBUG_SUPER_VERBOSE
2078
            CPLDebug("GMLAS", "FindElementsWithMustBeToLevel: %s",
2079
                     osFullXPath.c_str());
2080
#endif
2081
2082
506k
            if (IsIgnoredXPath(osFullXPath))
2083
0
            {
2084
#ifdef DEBUG_VERBOSE
2085
                CPLDebug("GMLAS", "%s is in ignored xpaths",
2086
                         osFullXPath.c_str());
2087
#endif
2088
0
                continue;
2089
0
            }
2090
2091
            // This could be refined to detect if the repeated element might not
2092
            // be simplifiable as an array
2093
506k
            if (bSimpleEnoughOut && bRepeatedParticle)
2094
432
            {
2095
#ifdef DEBUG_VERBOSE
2096
                CPLDebug("GMLAS", "%s not inlinable because %s is repeated",
2097
                         osParentXPath.c_str(), osXPath.c_str());
2098
#endif
2099
432
                bSimpleEnoughOut = false;
2100
432
            }
2101
2102
            // We don't want to inline
2103
            // sub-classes with hundereds of attributes
2104
506k
            nCountSubEltsOut++;
2105
2106
506k
            std::vector<XSElementDeclaration *> apoImplEltList;
2107
506k
            GetConcreteImplementationTypes(poElt, apoImplEltList);
2108
2109
506k
            std::vector<XSElementDeclaration *> apoChildrenElements =
2110
506k
                GetConstraintChildrenElements(osFullXPath);
2111
2112
            // Special case for a GML geometry property
2113
506k
            if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
2114
506k
                GetOGRGeometryType(poTypeDef) != wkbNone)
2115
0
            {
2116
                // Do nothing
2117
0
            }
2118
506k
            else if (IsGMLNamespace(osEltNS) &&
2119
506k
                     GetOGRGeometryTypeFromGMLEltName(osEltName) != wkbNone)
2120
0
            {
2121
                // Do nothing
2122
0
            }
2123
            // Any GML abstract type
2124
506k
            else if (poElt->getAbstract() && IsGMLNamespace(osEltNS) &&
2125
506k
                     osEltName != "_Feature" &&
2126
506k
                     osEltName != "AbstractFeature" &&
2127
506k
                     osEltName != "AbstractTimeObject")
2128
0
            {
2129
                // Do nothing
2130
0
            }
2131
            // Are there substitution groups for this element ?
2132
506k
            else if (!apoImplEltList.empty() || !apoChildrenElements.empty())
2133
0
            {
2134
0
                if (!apoChildrenElements.empty())
2135
0
                {
2136
0
                    apoImplEltList = std::move(apoChildrenElements);
2137
0
                }
2138
0
                else if (!poElt->getAbstract())
2139
0
                {
2140
0
                    apoImplEltList.insert(apoImplEltList.begin(), poElt);
2141
0
                }
2142
0
                for (size_t j = 0; j < apoImplEltList.size(); j++)
2143
0
                {
2144
0
                    XSElementDeclaration *poSubElt = apoImplEltList[j];
2145
0
                    const CPLString osSubEltXPath(
2146
0
                        MakeXPath(transcode(poSubElt->getNamespace()),
2147
0
                                  transcode(poSubElt->getName())));
2148
2149
0
                    if (IsIgnoredXPath(osParentXPath + "/" + osSubEltXPath))
2150
0
                    {
2151
#ifdef DEBUG_VERBOSE
2152
                        CPLDebug("GMLAS", "%s is in ignored xpaths",
2153
                                 (osParentXPath + "/" + osSubEltXPath).c_str());
2154
#endif
2155
0
                        continue;
2156
0
                    }
2157
2158
                    // Make sure we will instantiate the referenced element
2159
0
                    if (m_oSetEltsForTopClass.find(poSubElt) ==
2160
0
                            m_oSetEltsForTopClass.end() &&
2161
0
                        aoSetXPathEltsForTopClass.find(osSubEltXPath) ==
2162
0
                            aoSetXPathEltsForTopClass.end())
2163
0
                    {
2164
#ifdef DEBUG_VERBOSE
2165
                        CPLDebug(
2166
                            "GMLAS",
2167
                            "%s (%s) must be exposed as "
2168
                            "top-level (%s of %s)",
2169
                            osSubEltXPath.c_str(),
2170
                            transcode(poSubElt->getTypeDefinition()->getName())
2171
                                .c_str(),
2172
                            apoChildrenElements.empty() ? "derived class"
2173
                                                        : "child",
2174
                            osParentXPath.c_str());
2175
#endif
2176
2177
0
                        oSetVisitedEltDecl.insert(poSubElt);
2178
0
                        m_oSetEltsForTopClass.insert(poSubElt);
2179
0
                        oVectorEltsForTopClass.push_back(poSubElt);
2180
0
                        aoSetXPathEltsForTopClass.insert(osSubEltXPath);
2181
2182
0
                        XSComplexTypeDefinition *poSubEltCT =
2183
0
                            IsEltCompatibleOfFC(poSubElt);
2184
0
                        if (!bAlreadyVisitedMG && poSubEltCT != nullptr &&
2185
0
                            poSubEltCT->getParticle() != nullptr)
2186
0
                        {
2187
0
                            bool bSubSimpleEnoughOut = true;
2188
0
                            int nSubCountSubElt = 0;
2189
0
                            if (!FindElementsWithMustBeToLevel(
2190
0
                                    osSubEltXPath,
2191
0
                                    poSubEltCT->getParticle()
2192
0
                                        ->getModelGroupTerm(),
2193
0
                                    nRecursionCounter + 1, oSetVisitedEltDecl,
2194
0
                                    oSetVisitedModelGroups,
2195
0
                                    oVectorEltsForTopClass,
2196
0
                                    aoSetXPathEltsForTopClass, poModel,
2197
0
                                    bSubSimpleEnoughOut, nSubCountSubElt))
2198
0
                            {
2199
0
                                return false;
2200
0
                            }
2201
0
                        }
2202
0
                    }
2203
0
                }
2204
0
            }
2205
2206
506k
            else if (!poElt->getAbstract() &&
2207
506k
                     poTypeDef->getTypeCategory() ==
2208
506k
                         XSTypeDefinition::COMPLEX_TYPE)
2209
506k
            {
2210
506k
                nCountSubEltsOut--;
2211
2212
506k
                XSComplexTypeDefinition *poEltCT = IsEltCompatibleOfFC(poElt);
2213
506k
                if (poEltCT)
2214
478k
                {
2215
                    // Might be a bit extreme, but for now we don't inline
2216
                    // classes that have subclasses.
2217
478k
                    if (bSimpleEnoughOut)
2218
2.31k
                    {
2219
#ifdef DEBUG_VERBOSE
2220
                        CPLDebug("GMLAS",
2221
                                 "%s not inlinable because %s field is complex",
2222
                                 osParentXPath.c_str(), osXPath.c_str());
2223
#endif
2224
2.31k
                        bSimpleEnoughOut = false;
2225
2.31k
                    }
2226
2227
478k
                    if (oSetVisitedEltDecl.find(poElt) !=
2228
478k
                        oSetVisitedEltDecl.end())
2229
458k
                    {
2230
458k
                        if (m_oSetEltsForTopClass.find(poElt) ==
2231
458k
                                m_oSetEltsForTopClass.end() &&
2232
458k
                            m_oSetSimpleEnoughElts.find(poElt) ==
2233
25.1k
                                m_oSetSimpleEnoughElts.end() &&
2234
458k
                            aoSetXPathEltsForTopClass.find(osXPath) ==
2235
22.1k
                                aoSetXPathEltsForTopClass.end())
2236
7.09k
                        {
2237
7.09k
                            CPLString osIgnored;
2238
7.09k
                            if (!m_oForcedFlattenedXPathMatcher.MatchesRefXPath(
2239
7.09k
                                    osXPath, osIgnored))
2240
7.09k
                            {
2241
#ifdef DEBUG_VERBOSE
2242
                                CPLDebug("GMLAS",
2243
                                         "%s (%s) must be exposed as "
2244
                                         "top-level (multiple time referenced)",
2245
                                         osXPath.c_str(),
2246
                                         transcode(poTypeDef->getNamespace())
2247
                                             .c_str());
2248
#endif
2249
7.09k
                                m_oSetEltsForTopClass.insert(poElt);
2250
7.09k
                                oVectorEltsForTopClass.push_back(poElt);
2251
7.09k
                                aoSetXPathEltsForTopClass.insert(
2252
7.09k
                                    std::move(osXPath));
2253
7.09k
                            }
2254
7.09k
                        }
2255
458k
                    }
2256
20.0k
                    else
2257
20.0k
                    {
2258
20.0k
                        oSetVisitedEltDecl.insert(poElt);
2259
2260
20.0k
                        if (!bAlreadyVisitedMG &&
2261
20.0k
                            poEltCT->getParticle() != nullptr)
2262
20.0k
                        {
2263
20.0k
                            int nSubCountSubElt = 0;
2264
2265
                            // Process attributes
2266
20.0k
                            XSAttributeUseList *poAttrList =
2267
20.0k
                                poEltCT->getAttributeUses();
2268
20.0k
                            const size_t nAttrListSize =
2269
20.0k
                                (poAttrList != nullptr) ? poAttrList->size()
2270
20.0k
                                                        : 0;
2271
20.0k
                            for (size_t j = 0; j < nAttrListSize; ++j)
2272
0
                            {
2273
0
                                XSAttributeUse *poAttr =
2274
0
                                    poAttrList->elementAt(j);
2275
0
                                GMLASField oField;
2276
0
                                if (!SetFieldFromAttribute(oField, poAttr,
2277
0
                                                           osFullXPath))
2278
0
                                {
2279
0
                                    return false;
2280
0
                                }
2281
0
                                if (!IsIgnoredXPath(oField.GetXPath()) &&
2282
0
                                    oField.GetFixedValue().empty())
2283
0
                                {
2284
#ifdef DEBUG_SUPER_VERBOSE
2285
                                    CPLDebug(
2286
                                        "GMLAS",
2287
                                        "FindElementsWithMustBeToLevel: %s",
2288
                                        oField.GetXPath().c_str());
2289
#endif
2290
0
                                    nSubCountSubElt++;
2291
0
                                }
2292
0
                            }
2293
2294
20.0k
                            bool bSubSimpleEnoughOut = true;
2295
20.0k
                            if (!FindElementsWithMustBeToLevel(
2296
20.0k
                                    osFullXPath,
2297
20.0k
                                    poEltCT->getParticle()->getModelGroupTerm(),
2298
20.0k
                                    nRecursionCounter + 1, oSetVisitedEltDecl,
2299
20.0k
                                    oSetVisitedModelGroups,
2300
20.0k
                                    oVectorEltsForTopClass,
2301
20.0k
                                    aoSetXPathEltsForTopClass, poModel,
2302
20.0k
                                    bSubSimpleEnoughOut, nSubCountSubElt))
2303
0
                            {
2304
0
                                return false;
2305
0
                            }
2306
20.0k
                            if (bSubSimpleEnoughOut)
2307
1.92k
                            {
2308
#ifdef DEBUG_VERBOSE
2309
                                CPLDebug("GMLAS", "%s is inlinable: %d fields",
2310
                                         osXPath.c_str(), nSubCountSubElt);
2311
#endif
2312
1.92k
                                m_oSetSimpleEnoughElts.insert(poElt);
2313
2314
1.92k
                                nCountSubEltsOut += nSubCountSubElt;
2315
1.92k
                            }
2316
18.0k
                            else if (bSimpleEnoughOut)
2317
0
                            {
2318
#ifdef DEBUG_VERBOSE
2319
                                CPLDebug("GMLAS",
2320
                                         "%s not inlinable because %s is not "
2321
                                         "inlinable",
2322
                                         osParentXPath.c_str(),
2323
                                         osXPath.c_str());
2324
#endif
2325
0
                                bSimpleEnoughOut = false;
2326
0
                            }
2327
20.0k
                        }
2328
20.0k
                    }
2329
478k
                }
2330
27.0k
                else
2331
27.0k
                {
2332
27.0k
                    if (transcode(poElt->getName()) != szFEATURE_COLLECTION)
2333
27.0k
                    {
2334
27.0k
                        poEltCT = reinterpret_cast<XSComplexTypeDefinition *>(
2335
27.0k
                            poTypeDef);
2336
                        // Process attributes
2337
27.0k
                        XSAttributeUseList *poAttrList =
2338
27.0k
                            poEltCT->getAttributeUses();
2339
27.0k
                        const size_t nAttrListSize =
2340
27.0k
                            (poAttrList != nullptr) ? poAttrList->size() : 0;
2341
27.0k
                        for (size_t j = 0;
2342
49.6k
                             bSimpleEnoughOut && j < nAttrListSize; ++j)
2343
22.6k
                        {
2344
22.6k
                            XSAttributeUse *poAttr = poAttrList->elementAt(j);
2345
22.6k
                            GMLASField oField;
2346
22.6k
                            if (!SetFieldFromAttribute(oField, poAttr,
2347
22.6k
                                                       osFullXPath))
2348
0
                            {
2349
0
                                return false;
2350
0
                            }
2351
22.6k
                            if (!IsIgnoredXPath(oField.GetXPath()) &&
2352
22.6k
                                oField.GetFixedValue().empty())
2353
22.6k
                            {
2354
#ifdef DEBUG_SUPER_VERBOSE
2355
                                CPLDebug("GMLAS",
2356
                                         "FindElementsWithMustBeToLevel: %s",
2357
                                         oField.GetXPath().c_str());
2358
#endif
2359
22.6k
                                nCountSubEltsOut++;
2360
22.6k
                            }
2361
22.6k
                        }
2362
27.0k
                    }
2363
27.0k
                }
2364
2365
506k
                CPLString osTargetElement;
2366
506k
                if (poElt->getAnnotation() != nullptr)
2367
0
                {
2368
0
                    CPLString osAnnot(transcode(
2369
0
                        poElt->getAnnotation()->getAnnotationString()));
2370
2371
#ifdef DEBUG_SUPER_VERBOSE
2372
                    CPLDebug("GMLAS", "Annot: %s", osAnnot.c_str());
2373
#endif
2374
0
                    CPLXMLNode *psRoot = CPLParseXMLString(osAnnot);
2375
0
                    CPLStripXMLNamespace(psRoot, nullptr, TRUE);
2376
0
                    osTargetElement = CPLGetXMLValue(
2377
0
                        psRoot, "=annotation.appinfo.targetElement", "");
2378
0
                    CPLDestroyXMLNode(psRoot);
2379
#ifdef DEBUG_VERBOSE
2380
                    if (!osTargetElement.empty())
2381
                        CPLDebug("GMLAS", "targetElement: %s",
2382
                                 osTargetElement.c_str());
2383
#endif
2384
0
                }
2385
2386
                // If we have a element of type gml:ReferenceType that has
2387
                // a targetElement in its annotation.appinfo, then create
2388
                // a dedicated field to have cross-layer relationships.
2389
506k
                if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
2390
506k
                    transcode(poTypeDef->getName()) == "ReferenceType" &&
2391
506k
                    !osTargetElement.empty())
2392
0
                {
2393
0
                    XSElementDeclaration *poTargetElt =
2394
0
                        GetTopElementDeclarationFromXPath(osTargetElement,
2395
0
                                                          poModel);
2396
                    // TODO: even for non abstract we should probably
2397
                    // handle substitutions
2398
0
                    if (poTargetElt != nullptr && !poTargetElt->getAbstract())
2399
0
                    {
2400
0
                        const CPLString osTargetEltXPath(
2401
0
                            MakeXPath(transcode(poTargetElt->getNamespace()),
2402
0
                                      transcode(poTargetElt->getName())));
2403
2404
0
                        if (IsIgnoredXPath(osTargetEltXPath))
2405
0
                        {
2406
#ifdef DEBUG_VERBOSE
2407
                            CPLDebug("GMLAS", "%s is in ignored xpaths",
2408
                                     osTargetEltXPath.c_str());
2409
#endif
2410
0
                            continue;
2411
0
                        }
2412
2413
                        // Make sure we will instantiate the referenced
2414
                        // element
2415
0
                        if (m_oSetEltsForTopClass.find(poTargetElt) ==
2416
0
                                m_oSetEltsForTopClass.end() &&
2417
0
                            aoSetXPathEltsForTopClass.find(osTargetEltXPath) ==
2418
0
                                aoSetXPathEltsForTopClass.end())
2419
0
                        {
2420
#ifdef DEBUG_VERBOSE
2421
                            CPLDebug(
2422
                                "GMLAS", "%d: Adding %s as (%s) needed type",
2423
                                __LINE__, osTargetElement.c_str(),
2424
                                transcode(
2425
                                    poTargetElt->getTypeDefinition()->getName())
2426
                                    .c_str());
2427
#endif
2428
0
                            oSetVisitedEltDecl.insert(poTargetElt);
2429
0
                            m_oSetEltsForTopClass.insert(poTargetElt);
2430
0
                            oVectorEltsForTopClass.push_back(poTargetElt);
2431
0
                            aoSetXPathEltsForTopClass.insert(osTargetEltXPath);
2432
0
                        }
2433
2434
0
                        XSComplexTypeDefinition *poTargetEltCT =
2435
0
                            IsEltCompatibleOfFC(poTargetElt);
2436
0
                        if (!bAlreadyVisitedMG && poTargetEltCT &&
2437
0
                            poTargetEltCT->getParticle() != nullptr)
2438
0
                        {
2439
0
                            bool bSubSimpleEnoughOut = true;
2440
0
                            int nSubCountSubElt = 0;
2441
0
                            if (!FindElementsWithMustBeToLevel(
2442
0
                                    osTargetEltXPath,
2443
0
                                    poTargetEltCT->getParticle()
2444
0
                                        ->getModelGroupTerm(),
2445
0
                                    nRecursionCounter + 1, oSetVisitedEltDecl,
2446
0
                                    oSetVisitedModelGroups,
2447
0
                                    oVectorEltsForTopClass,
2448
0
                                    aoSetXPathEltsForTopClass, poModel,
2449
0
                                    bSubSimpleEnoughOut, nSubCountSubElt))
2450
0
                            {
2451
0
                                return false;
2452
0
                            }
2453
0
                        }
2454
0
                    }
2455
0
                }
2456
506k
            }
2457
506k
        }
2458
17.6k
        else if (!bAlreadyVisitedMG &&
2459
17.6k
                 poParticle->getTermType() == XSParticle::TERM_MODELGROUP)
2460
20
        {
2461
            // This could be refined to detect if the repeated element might not
2462
            // be simplifiable as an array
2463
20
            if (bSimpleEnoughOut && bRepeatedParticle)
2464
0
            {
2465
#ifdef DEBUG_VERBOSE
2466
                CPLDebug(
2467
                    "GMLAS",
2468
                    "%s not inlinable because there is a repeated particle",
2469
                    osParentXPath.c_str());
2470
#endif
2471
0
                bSimpleEnoughOut = false;
2472
0
            }
2473
2474
20
            XSModelGroup *psSubModelGroup = poParticle->getModelGroupTerm();
2475
20
            if (!FindElementsWithMustBeToLevel(
2476
20
                    osParentXPath, psSubModelGroup, nRecursionCounter + 1,
2477
20
                    oSetVisitedEltDecl, oSetVisitedModelGroups,
2478
20
                    oVectorEltsForTopClass, aoSetXPathEltsForTopClass, poModel,
2479
20
                    bSimpleEnoughOut, nCountSubEltsOut))
2480
0
            {
2481
0
                return false;
2482
0
            }
2483
20
        }
2484
17.6k
        else
2485
17.6k
        {
2486
            // This could be refined to detect if the repeated element might not
2487
            // be simplifiable as an array
2488
17.6k
            if (bSimpleEnoughOut && bRepeatedParticle)
2489
17.6k
            {
2490
#ifdef DEBUG_VERBOSE
2491
                CPLDebug(
2492
                    "GMLAS",
2493
                    "%s not inlinable because there is a repeated particle",
2494
                    osParentXPath.c_str());
2495
#endif
2496
17.6k
                bSimpleEnoughOut = false;
2497
17.6k
            }
2498
17.6k
        }
2499
524k
    }
2500
2501
22.6k
    if (bSimpleEnoughOut && nCountSubEltsOut > m_nMaximumFieldsForFlattening)
2502
186
    {
2503
186
        CPLString osIgnored;
2504
186
        if (!m_oForcedFlattenedXPathMatcher.MatchesRefXPath(osParentXPath,
2505
186
                                                            osIgnored))
2506
186
        {
2507
#ifdef DEBUG_VERBOSE
2508
            CPLDebug("GMLAS",
2509
                     "%s not inlinable because it has more than %d fields",
2510
                     osParentXPath.c_str(), m_nMaximumFieldsForFlattening);
2511
#endif
2512
186
            bSimpleEnoughOut = false;
2513
186
        }
2514
186
    }
2515
2516
22.6k
    return true;
2517
22.6k
}
2518
2519
/************************************************************************/
2520
/*                           IsGMLNamespace()                           */
2521
/************************************************************************/
2522
2523
bool GMLASSchemaAnalyzer::IsGMLNamespace(const CPLString &osURI)
2524
2.68M
{
2525
2.68M
    if (osURI.find(szGML_URI) == 0)
2526
509
        return true;
2527
    // Below is mostly for unit tests were we use xmlns:gml="http://fake_gml"
2528
2.68M
    const auto oIter = m_oMapURIToPrefix.find(osURI);
2529
2.68M
    return oIter != m_oMapURIToPrefix.end() && oIter->second == szGML_PREFIX;
2530
2.68M
}
2531
2532
/************************************************************************/
2533
/*                    BuildMapCountOccurrencesOfSameName()               */
2534
/************************************************************************/
2535
2536
void GMLASSchemaAnalyzer::BuildMapCountOccurrencesOfSameName(
2537
    XSModelGroup *poModelGroup,
2538
    std::map<CPLString, int> &oMapCountOccurrencesOfSameName)
2539
14.8k
{
2540
14.8k
    XSParticleList *poParticles = poModelGroup->getParticles();
2541
571k
    for (size_t i = 0; i < poParticles->size(); ++i)
2542
556k
    {
2543
556k
        XSParticle *poParticle = poParticles->elementAt(i);
2544
556k
        if (poParticle->getTermType() == XSParticle::TERM_ELEMENT)
2545
549k
        {
2546
549k
            XSElementDeclaration *poElt = poParticle->getElementTerm();
2547
549k
            const CPLString osEltName(transcode(poElt->getName()));
2548
549k
            oMapCountOccurrencesOfSameName[osEltName]++;
2549
549k
        }
2550
7.02k
        else if (poParticle->getTermType() == XSParticle::TERM_MODELGROUP)
2551
20
        {
2552
20
            XSModelGroup *psSubModelGroup = poParticle->getModelGroupTerm();
2553
20
            BuildMapCountOccurrencesOfSameName(psSubModelGroup,
2554
20
                                               oMapCountOccurrencesOfSameName);
2555
20
        }
2556
556k
    }
2557
14.8k
}
2558
2559
/************************************************************************/
2560
/*                         ComposeMinOccurs()                           */
2561
/************************************************************************/
2562
2563
static int ComposeMinOccurs(int nVal1, int nVal2)
2564
697
{
2565
697
    return nVal1 * nVal2;
2566
697
}
2567
2568
/************************************************************************/
2569
/*                         ComposeMaxOccurs()                           */
2570
/************************************************************************/
2571
2572
static int ComposeMaxOccurs(int nVal1, int nVal2)
2573
697
{
2574
697
    if (nVal1 == MAXOCCURS_UNLIMITED || nVal2 == MAXOCCURS_UNLIMITED)
2575
697
        return MAXOCCURS_UNLIMITED;
2576
0
    return nVal1 * nVal2;
2577
697
}
2578
2579
/************************************************************************/
2580
/*                         ExploreModelGroup()                          */
2581
/************************************************************************/
2582
2583
bool GMLASSchemaAnalyzer::ExploreModelGroup(
2584
    XSModelGroup *poModelGroup, XSAttributeUseList *poMainAttrList,
2585
    GMLASFeatureClass &oClass, int nRecursionCounter,
2586
    std::set<XSModelGroup *> &oSetVisitedModelGroups, XSModel *poModel,
2587
    const std::map<CPLString, int> &oMapCountOccurrencesOfSameName)
2588
14.8k
{
2589
14.8k
    if (oSetVisitedModelGroups.find(poModelGroup) !=
2590
14.8k
        oSetVisitedModelGroups.end())
2591
0
    {
2592
0
        CPLError(CE_Failure, CPLE_AppDefined, "%s already visited",
2593
0
                 oClass.GetXPath().c_str());
2594
0
        return false;
2595
0
    }
2596
14.8k
    oSetVisitedModelGroups.insert(poModelGroup);
2597
2598
14.8k
    if (nRecursionCounter == 100)
2599
0
    {
2600
        // Presumably an hostile schema
2601
0
        CPLError(CE_Failure, CPLE_AppDefined,
2602
0
                 "Schema analysis failed due to too deeply nested model");
2603
0
        return false;
2604
0
    }
2605
2606
14.8k
    if (poMainAttrList != nullptr)
2607
20
    {
2608
20
        const size_t nMainAttrListSize = poMainAttrList->size();
2609
40
        for (size_t j = 0; j < nMainAttrListSize; ++j)
2610
20
        {
2611
20
            GMLASField oField;
2612
20
            XSAttributeUse *poAttr = poMainAttrList->elementAt(j);
2613
20
            if (!SetFieldFromAttribute(oField, poAttr, oClass.GetXPath()))
2614
0
            {
2615
0
                return false;
2616
0
            }
2617
2618
20
            if (IsIgnoredXPath(oField.GetXPath()))
2619
0
            {
2620
#ifdef DEBUG_VERBOSE
2621
                CPLDebug("GMLAS", "%s is in ignored xpaths",
2622
                         oField.GetXPath().c_str());
2623
#endif
2624
0
                if (!oField.GetFixedValue().empty() ||
2625
0
                    !oField.GetDefaultValue().empty())
2626
0
                {
2627
0
                    oField.SetIgnored();
2628
0
                }
2629
0
                else
2630
0
                {
2631
0
                    continue;
2632
0
                }
2633
0
            }
2634
2635
20
            oClass.AddField(oField);
2636
20
        }
2637
20
    }
2638
2639
14.8k
    XSParticleList *poParticles = poModelGroup->getParticles();
2640
2641
    // Special case for GML 3.1.1 where gml:metaDataProperty should be
2642
    // a sequence of gml:_Metadata but for some reason they have used
2643
    // a sequence of any.
2644
14.8k
    if (oClass.GetXPath() == "gml:metaDataProperty" &&
2645
14.8k
        poModelGroup->getCompositor() == XSModelGroup::COMPOSITOR_SEQUENCE &&
2646
14.8k
        poParticles->size() == 1 &&
2647
14.8k
        poParticles->elementAt(0)->getTermType() == XSParticle::TERM_WILDCARD)
2648
0
    {
2649
0
        XSElementDeclaration *poGMLMetadata =
2650
0
            GetTopElementDeclarationFromXPath("gml:_MetaData", poModel);
2651
0
        if (poGMLMetadata != nullptr)
2652
0
        {
2653
0
            std::vector<XSElementDeclaration *> apoImplEltList;
2654
0
            GetConcreteImplementationTypes(poGMLMetadata, apoImplEltList);
2655
0
            CreateNonNestedRelationship(poGMLMetadata, apoImplEltList, oClass,
2656
0
                                        1,
2657
0
                                        false,  // doesn't need prefix
2658
0
                                        true,   // force junction table
2659
0
                                        false   // regular case
2660
0
            );
2661
2662
0
            return true;
2663
0
        }
2664
0
    }
2665
2666
14.8k
    const bool bIsChoice =
2667
14.8k
        (poModelGroup->getCompositor() == XSModelGroup::COMPOSITOR_CHOICE);
2668
14.8k
    int nGroup = 0;
2669
2670
571k
    for (size_t i = 0; i < poParticles->size(); ++i)
2671
556k
    {
2672
556k
        XSParticle *poParticle = poParticles->elementAt(i);
2673
556k
        const bool bRepeatedParticle = poParticle->getMaxOccursUnbounded() ||
2674
556k
                                       poParticle->getMaxOccurs() > 1;
2675
556k
        const int nMinOccurs = static_cast<int>(poParticle->getMinOccurs());
2676
556k
        const int nMaxOccurs =
2677
556k
            poParticle->getMaxOccursUnbounded()
2678
556k
                ? MAXOCCURS_UNLIMITED
2679
556k
                : static_cast<int>(poParticle->getMaxOccurs());
2680
2681
556k
        if (poParticle->getTermType() == XSParticle::TERM_ELEMENT)
2682
549k
        {
2683
549k
            XSElementDeclaration *poElt = poParticle->getElementTerm();
2684
549k
            const CPLString osEltName(transcode(poElt->getName()));
2685
2686
549k
            const auto oIter = oMapCountOccurrencesOfSameName.find(osEltName);
2687
549k
            const bool bEltNameWillNeedPrefix =
2688
549k
                oIter != oMapCountOccurrencesOfSameName.end() &&
2689
549k
                oIter->second > 1;
2690
549k
            const CPLString osEltNS(transcode(poElt->getNamespace()));
2691
549k
            const CPLString osPrefixedEltName((bEltNameWillNeedPrefix
2692
549k
                                                   ? GetPrefix(osEltNS) + "_"
2693
549k
                                                   : CPLString()) +
2694
549k
                                              osEltName);
2695
549k
            const CPLString osOnlyElementXPath(MakeXPath(osEltNS, osEltName));
2696
549k
            const CPLString osElementXPath(oClass.GetXPath() + "/" +
2697
549k
                                           osOnlyElementXPath);
2698
#ifdef DEBUG_VERBOSE
2699
            CPLDebug("GMLAS", "Iterating through %s", osElementXPath.c_str());
2700
#endif
2701
2702
549k
            if (IsIgnoredXPath(osElementXPath))
2703
0
            {
2704
#ifdef DEBUG_VERBOSE
2705
                CPLDebug("GMLAS", "%s is in ignored xpaths",
2706
                         osElementXPath.c_str());
2707
#endif
2708
0
                continue;
2709
0
            }
2710
2711
549k
            CPLString osTargetElement;
2712
549k
            if (poElt->getAnnotation() != nullptr)
2713
0
            {
2714
0
                CPLString osAnnot(
2715
0
                    transcode(poElt->getAnnotation()->getAnnotationString()));
2716
2717
#ifdef DEBUG_SUPER_VERBOSE
2718
                CPLDebug("GMLAS", "Annot: %s", osAnnot.c_str());
2719
#endif
2720
0
                CPLXMLNode *psRoot = CPLParseXMLString(osAnnot);
2721
0
                CPLStripXMLNamespace(psRoot, nullptr, TRUE);
2722
0
                osTargetElement = CPLGetXMLValue(
2723
0
                    psRoot, "=annotation.appinfo.targetElement", "");
2724
0
                CPLDestroyXMLNode(psRoot);
2725
#ifdef DEBUG_VERBOSE
2726
                if (!osTargetElement.empty())
2727
                    CPLDebug("GMLAS", "targetElement: %s",
2728
                             osTargetElement.c_str());
2729
#endif
2730
0
            }
2731
2732
549k
            XSTypeDefinition *poTypeDef = poElt->getTypeDefinition();
2733
2734
549k
            std::vector<XSElementDeclaration *> apoImplEltList;
2735
549k
            GetConcreteImplementationTypes(poElt, apoImplEltList);
2736
2737
549k
            std::vector<XSElementDeclaration *> apoChildrenElements =
2738
549k
                GetConstraintChildrenElements(osElementXPath);
2739
2740
            // Special case for a GML geometry property
2741
549k
            OGRwkbGeometryType eGeomType =
2742
549k
                wkbNone;                    // to make Visual Studio happy
2743
549k
            CPL_IGNORE_RET_VAL(eGeomType);  // to make cppcheck happy
2744
2745
549k
            if (!apoChildrenElements.empty())
2746
0
            {
2747
0
                CreateNonNestedRelationship(
2748
0
                    poElt, apoChildrenElements, oClass, nMaxOccurs,
2749
0
                    bEltNameWillNeedPrefix,
2750
0
                    false,  // do not force junction table
2751
0
                    true    // special case for children elements
2752
0
                );
2753
0
            }
2754
2755
549k
            else if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
2756
549k
                     (eGeomType = GetOGRGeometryType(poTypeDef)) != wkbNone)
2757
0
            {
2758
0
                GMLASField oField;
2759
0
                oField.SetName(osPrefixedEltName);
2760
0
                oField.SetMinOccurs(nMinOccurs);
2761
0
                oField.SetMaxOccurs(nMaxOccurs);
2762
0
                oField.SetType(GMLAS_FT_GEOMETRY, szFAKEXS_GEOMETRY);
2763
0
                if (nMaxOccurs > 1 || nMaxOccurs == MAXOCCURS_UNLIMITED)
2764
0
                {
2765
                    // Repeated geometry property can happen in some schemas
2766
                    // like
2767
                    // inspire.ec.europa.eu/schemas/ge_gp/4.0/GeophysicsCore.xsd
2768
                    // or
2769
                    // http://ngwd-bdnes.cits.nrcan.gc.ca/service/gwml/schemas/2.1/gwml2-flow.xsd
2770
0
                    oField.SetGeomType(wkbUnknown);
2771
0
                    oField.SetArray(true);
2772
0
                }
2773
0
                else
2774
0
                    oField.SetGeomType(eGeomType);
2775
0
                oField.SetXPath(osElementXPath);
2776
0
                oField.SetDocumentation(GetAnnotationDoc(poElt));
2777
2778
0
                oClass.AddField(oField);
2779
0
            }
2780
2781
549k
            else if (IsGMLNamespace(osEltNS) &&
2782
549k
                     (eGeomType = GetOGRGeometryTypeFromGMLEltName(
2783
0
                          osEltName)) != wkbNone)
2784
0
            {
2785
0
                GMLASField oField;
2786
0
                oField.SetName(osPrefixedEltName);
2787
0
                oField.SetMinOccurs(nMinOccurs);
2788
0
                oField.SetMaxOccurs(nMaxOccurs);
2789
2790
0
                oField.SetType(GMLAS_FT_GEOMETRY, szFAKEXS_GEOMETRY);
2791
0
                oField.SetGeomType(eGeomType);
2792
0
                oField.SetArray(nMaxOccurs > 1 ||
2793
0
                                nMaxOccurs == MAXOCCURS_UNLIMITED);
2794
2795
0
                oField.SetXPath(osElementXPath);
2796
0
                oField.SetIncludeThisEltInBlob(true);
2797
0
                oField.SetDocumentation(GetAnnotationDoc(poElt));
2798
2799
0
                oClass.AddField(oField);
2800
0
            }
2801
2802
            // Any GML abstract type
2803
549k
            else if (poElt->getAbstract() && IsGMLNamespace(osEltNS) &&
2804
549k
                     osEltName != "_Feature" &&
2805
549k
                     osEltName != "AbstractFeature" &&
2806
549k
                     osEltName != "AbstractTimeObject")
2807
0
            {
2808
0
                GMLASField oField;
2809
0
                oField.SetName(osPrefixedEltName);
2810
0
                oField.SetMinOccurs(nMinOccurs);
2811
0
                oField.SetMaxOccurs(nMaxOccurs);
2812
0
                if (osEltName == "AbstractGeometry")
2813
0
                {
2814
0
                    oField.SetType(GMLAS_FT_GEOMETRY, szFAKEXS_GEOMETRY);
2815
0
                    oField.SetGeomType(wkbUnknown);
2816
0
                    oField.SetArray(nMaxOccurs > 1 ||
2817
0
                                    nMaxOccurs == MAXOCCURS_UNLIMITED);
2818
0
                }
2819
0
                else
2820
0
                {
2821
0
                    oField.SetType(GMLAS_FT_ANYTYPE, szXS_ANY_TYPE);
2822
0
                }
2823
0
                oField.SetIncludeThisEltInBlob(true);
2824
0
                oField.SetDocumentation(GetAnnotationDoc(poElt));
2825
2826
0
                for (size_t j = 0; j < apoImplEltList.size(); j++)
2827
0
                {
2828
0
                    XSElementDeclaration *poSubElt = apoImplEltList[j];
2829
0
                    oField.AddAlternateXPath(
2830
0
                        oClass.GetXPath() + "/" +
2831
0
                        MakeXPath(transcode(poSubElt->getNamespace()),
2832
0
                                  transcode(poSubElt->getName())));
2833
0
                }
2834
2835
0
                oClass.AddField(oField);
2836
0
            }
2837
2838
            // Are there substitution groups for this element ?
2839
            // or is this element already identified as being a top-level one ?
2840
549k
            else if (!apoImplEltList.empty() ||
2841
549k
                     (m_oSetEltsForTopClass.find(poElt) !=
2842
549k
                          m_oSetEltsForTopClass.end() &&
2843
549k
                      m_oSetSimpleEnoughElts.find(poElt) ==
2844
453k
                          m_oSetSimpleEnoughElts.end()))
2845
453k
            {
2846
453k
                CreateNonNestedRelationship(
2847
453k
                    poElt, apoImplEltList, oClass, nMaxOccurs,
2848
453k
                    bEltNameWillNeedPrefix,
2849
453k
                    false,  // do not force junction table
2850
453k
                    false   // regular case
2851
453k
                );
2852
453k
            }
2853
2854
            // Abstract element without realizations !
2855
95.6k
            else if (poElt->getAbstract())
2856
0
            {
2857
                // Do nothing with it since it cannot be instantiated
2858
                // in a valid way.
2859
0
                CPLDebug("GMLAS",
2860
0
                         "Ignoring %s that is abstract without realizations",
2861
0
                         osElementXPath.c_str());
2862
0
            }
2863
2864
            // Simple type like string, int, etc...
2865
95.6k
            else if (poTypeDef->getTypeCategory() ==
2866
95.6k
                     XSTypeDefinition::SIMPLE_TYPE)
2867
160
            {
2868
160
                XSSimpleTypeDefinition *poST =
2869
160
                    reinterpret_cast<XSSimpleTypeDefinition *>(poTypeDef);
2870
160
                GMLASField oField;
2871
160
                SetFieldTypeAndWidthFromDefinition(poST, oField);
2872
160
                oField.SetMinOccurs((bIsChoice) ? 0 : nMinOccurs);
2873
160
                oField.SetMaxOccurs(nMaxOccurs);
2874
160
                oField.SetDocumentation(GetAnnotationDoc(poElt));
2875
2876
160
                bool bNeedAuxTable = false;
2877
160
                const bool bIsList = (poST->getVariety() ==
2878
160
                                      XSSimpleTypeDefinition::VARIETY_LIST);
2879
160
                if (bIsList)
2880
0
                {
2881
0
                    SetFieldTypeAndWidthFromDefinition(poST->getItemType(),
2882
0
                                                       oField);
2883
0
                    if (bRepeatedParticle || !m_bUseArrays ||
2884
0
                        !IsCompatibleOfArray(oField.GetType()))
2885
0
                    {
2886
                        // Really particular case. This is a workaround
2887
0
                        oField.SetType(GMLAS_FT_STRING, szXS_STRING);
2888
0
                    }
2889
0
                    else
2890
0
                    {
2891
0
                        oField.SetList(true);
2892
0
                        oField.SetArray(true);
2893
0
                    }
2894
0
                }
2895
2896
160
                if (m_bUseArrays && bRepeatedParticle &&
2897
160
                    IsCompatibleOfArray(oField.GetType()))
2898
5
                {
2899
5
                    oField.SetArray(true);
2900
5
                }
2901
155
                else if (bRepeatedParticle)
2902
0
                {
2903
0
                    bNeedAuxTable = true;
2904
0
                }
2905
160
                if (bNeedAuxTable)
2906
0
                {
2907
0
                    GMLASFeatureClass oNestedClass;
2908
0
                    oNestedClass.SetName(oClass.GetName() + "_" +
2909
0
                                         osPrefixedEltName);
2910
0
                    oNestedClass.SetXPath(osElementXPath);
2911
0
                    GMLASField oUniqueField;
2912
0
                    oUniqueField.SetName("value");
2913
0
                    oUniqueField.SetMinOccurs(1);
2914
0
                    oUniqueField.SetMaxOccurs(1);
2915
0
                    oUniqueField.SetXPath(osElementXPath);
2916
0
                    oUniqueField.SetType(oField.GetType(),
2917
0
                                         oField.GetTypeName());
2918
0
                    oNestedClass.AddField(oUniqueField);
2919
0
                    oNestedClass.SetDocumentation(GetAnnotationDoc(poElt));
2920
2921
0
                    oClass.AddNestedClass(oNestedClass);
2922
2923
0
                    oField.SetType(GMLAS_FT_STRING, "");
2924
0
                    oField.SetName(osPrefixedEltName);
2925
0
                    oField.SetXPath(osElementXPath);
2926
0
                    oField.SetCategory(
2927
0
                        GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
2928
0
                    oField.SetRelatedClassXPath(oField.GetXPath());
2929
0
                    oClass.AddField(oField);
2930
0
                }
2931
160
                else
2932
160
                {
2933
160
                    oField.SetName(osPrefixedEltName);
2934
160
                    oField.SetXPath(osElementXPath);
2935
160
                    if (!bIsChoice && nMinOccurs > 0 && !poElt->getNillable())
2936
124
                    {
2937
124
                        oField.SetNotNullable(true);
2938
124
                    }
2939
160
                    oClass.AddField(oField);
2940
2941
                    // If the element has minOccurs=0 and is nillable, then we
2942
                    // need an extra field to be able to distinguish between the
2943
                    // case of the missing element or the element with
2944
                    // xsi:nil="true"
2945
160
                    if (nMinOccurs == 0 && poElt->getNillable() &&
2946
160
                        !m_bUseNullState)
2947
0
                    {
2948
0
                        GMLASField oFieldNil;
2949
0
                        oFieldNil.SetName(osPrefixedEltName + "_" + szNIL);
2950
0
                        oFieldNil.SetXPath(osElementXPath + "/" + szAT_XSI_NIL);
2951
0
                        oFieldNil.SetType(GMLAS_FT_BOOLEAN, "boolean");
2952
0
                        oFieldNil.SetMinOccurs(0);
2953
0
                        oFieldNil.SetMaxOccurs(1);
2954
0
                        oClass.AddField(oFieldNil);
2955
0
                    }
2956
160
                }
2957
160
            }
2958
2959
            // Complex type (element with attributes, composed element, etc...)
2960
95.5k
            else if (poTypeDef->getTypeCategory() ==
2961
95.5k
                     XSTypeDefinition::COMPLEX_TYPE)
2962
95.5k
            {
2963
95.5k
                XSComplexTypeDefinition *poEltCT =
2964
95.5k
                    reinterpret_cast<XSComplexTypeDefinition *>(poTypeDef);
2965
95.5k
                std::vector<GMLASField> aoFields;
2966
95.5k
                bool bNothingMoreToDo = false;
2967
95.5k
                std::vector<GMLASFeatureClass> aoNestedClasses;
2968
2969
95.5k
                const int nMinOccursEltParticle =
2970
95.5k
                    poEltCT->getParticle()
2971
95.5k
                        ? static_cast<int>(
2972
41.9k
                              poEltCT->getParticle()->getMinOccurs())
2973
95.5k
                        : -1;
2974
95.5k
                const int nMaxOccursEltParticle =
2975
95.5k
                    poEltCT->getParticle()
2976
95.5k
                        ? (poEltCT->getParticle()->getMaxOccursUnbounded()
2977
41.9k
                               ? MAXOCCURS_UNLIMITED
2978
41.9k
                               : static_cast<int>(
2979
41.9k
                                     poEltCT->getParticle()->getMaxOccurs()))
2980
95.5k
                        : -1;
2981
2982
95.5k
                const bool bEltRepeatedParticle =
2983
95.5k
                    nMaxOccursEltParticle > 1 ||
2984
95.5k
                    nMaxOccursEltParticle == MAXOCCURS_UNLIMITED;
2985
95.5k
                const bool bMoveNestedClassToTop =
2986
95.5k
                    !bRepeatedParticle && !bEltRepeatedParticle;
2987
2988
                // Process attributes
2989
95.5k
                XSAttributeUseList *poAttrList = poEltCT->getAttributeUses();
2990
95.5k
                const size_t nAttrListSize =
2991
95.5k
                    (poAttrList != nullptr) ? poAttrList->size() : 0;
2992
147k
                for (size_t j = 0; j < nAttrListSize; ++j)
2993
52.4k
                {
2994
52.4k
                    XSAttributeUse *poAttr = poAttrList->elementAt(j);
2995
52.4k
                    GMLASField oField;
2996
52.4k
                    CPLString osNamePrefix(bMoveNestedClassToTop
2997
52.4k
                                               ? osPrefixedEltName
2998
52.4k
                                               : CPLString());
2999
52.4k
                    if (!SetFieldFromAttribute(oField, poAttr, osElementXPath,
3000
52.4k
                                               osNamePrefix))
3001
0
                    {
3002
0
                        return false;
3003
0
                    }
3004
52.4k
                    if (nMinOccurs == 0 || bIsChoice)
3005
0
                    {
3006
0
                        oField.SetMinOccurs(0);
3007
0
                        oField.SetNotNullable(false);
3008
0
                    }
3009
3010
52.4k
                    if (IsIgnoredXPath(oField.GetXPath()))
3011
0
                    {
3012
#ifdef DEBUG_VERBOSE
3013
                        CPLDebug("GMLAS", "%s is in ignored xpaths",
3014
                                 oField.GetXPath().c_str());
3015
#endif
3016
0
                        if (!oField.GetFixedValue().empty() ||
3017
0
                            !oField.GetDefaultValue().empty())
3018
0
                        {
3019
0
                            oField.SetIgnored();
3020
0
                        }
3021
0
                        else
3022
0
                        {
3023
0
                            continue;
3024
0
                        }
3025
0
                    }
3026
3027
52.4k
                    aoFields.push_back(std::move(oField));
3028
52.4k
                }
3029
3030
                // Deal with anyAttributes (or any element that also imply it)
3031
95.5k
                XSWildcard *poAttrWildcard = poEltCT->getAttributeWildcard();
3032
95.5k
                if (poAttrWildcard != nullptr)
3033
36.5k
                {
3034
36.5k
                    GMLASField oField;
3035
36.5k
                    oField.SetType(GMLASField::GetTypeFromString(szXS_STRING),
3036
36.5k
                                   szFAKEXS_JSON_DICT);
3037
36.5k
                    if (!bMoveNestedClassToTop)
3038
1.56k
                    {
3039
1.56k
                        oField.SetName("anyAttributes");
3040
1.56k
                    }
3041
34.9k
                    else
3042
34.9k
                    {
3043
34.9k
                        oField.SetName(osPrefixedEltName + "_anyAttributes");
3044
34.9k
                    }
3045
36.5k
                    oField.SetXPath(osElementXPath + "/" + szAT_ANY_ATTR);
3046
36.5k
                    oField.SetDocumentation(
3047
36.5k
                        GetAnnotationDoc(poAttrWildcard->getAnnotation()));
3048
3049
36.5k
                    aoFields.push_back(std::move(oField));
3050
36.5k
                }
3051
3052
95.5k
                XSSimpleTypeDefinition *poST = poEltCT->getSimpleType();
3053
95.5k
                if (poST != nullptr)
3054
0
                {
3055
                    /* Case of an element, generally with attributes */
3056
3057
0
                    GMLASField oField;
3058
0
                    SetFieldTypeAndWidthFromDefinition(poST, oField);
3059
0
                    if (bRepeatedParticle && nAttrListSize == 0 &&
3060
0
                        m_bUseArrays && IsCompatibleOfArray(oField.GetType()) &&
3061
0
                        oField.GetCategory() !=
3062
0
                            GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK)
3063
0
                    {
3064
                        /* We have a complex type, but no attributes, and */
3065
                        /* compatible of arrays, so move it to top level! */
3066
0
                        oField.SetName(osPrefixedEltName);
3067
0
                        oField.SetArray(true);
3068
0
                        oField.SetMinOccurs(nMinOccurs);
3069
0
                        oField.SetMaxOccurs(nMaxOccurs);
3070
0
                    }
3071
0
                    else if (bRepeatedParticle)
3072
0
                    {
3073
0
                        oField.SetName("value");
3074
0
                        oField.SetMinOccurs(1);
3075
0
                        oField.SetMaxOccurs(1);
3076
0
                        oField.SetNotNullable(true);
3077
0
                    }
3078
0
                    else
3079
0
                    {
3080
0
                        if (nMinOccurs == 0)
3081
0
                        {
3082
0
                            for (size_t j = 0; j < aoFields.size(); j++)
3083
0
                            {
3084
0
                                aoFields[j].SetMinOccurs(0);
3085
0
                                aoFields[j].SetNotNullable(false);
3086
0
                            }
3087
0
                        }
3088
3089
0
                        oField.SetName(osPrefixedEltName);
3090
0
                        oField.SetMinOccurs((bIsChoice) ? 0 : nMinOccurs);
3091
0
                        oField.SetMaxOccurs(nMaxOccurs);
3092
3093
                        // If the element has minOccurs=0 and is nillable, then
3094
                        // we need an extra field to be able to distinguish
3095
                        // between the case of the missing element or the
3096
                        // element with xsi:nil="true"
3097
0
                        if (nMinOccurs == 0 && poElt->getNillable() &&
3098
0
                            !m_bUseNullState)
3099
0
                        {
3100
0
                            GMLASField oFieldNil;
3101
0
                            oFieldNil.SetName(osPrefixedEltName + "_" + szNIL);
3102
0
                            oFieldNil.SetXPath(osElementXPath + "/" +
3103
0
                                               szAT_XSI_NIL);
3104
0
                            oFieldNil.SetType(GMLAS_FT_BOOLEAN, "boolean");
3105
0
                            oFieldNil.SetMinOccurs(0);
3106
0
                            oFieldNil.SetMaxOccurs(1);
3107
0
                            aoFields.push_back(std::move(oFieldNil));
3108
0
                        }
3109
0
                    }
3110
0
                    oField.SetXPath(osElementXPath);
3111
0
                    oField.SetDocumentation(GetAnnotationDoc(poElt));
3112
3113
0
                    aoFields.push_back(oField);
3114
0
                    if (oField.IsArray())
3115
0
                    {
3116
0
                        oClass.AddField(oField);
3117
0
                        bNothingMoreToDo = true;
3118
0
                    }
3119
0
                }
3120
95.5k
                else if (IsAnyType(poEltCT))
3121
36.5k
                {
3122
36.5k
                    GMLASField oField;
3123
36.5k
                    oField.SetType(GMLAS_FT_ANYTYPE, szXS_ANY_TYPE);
3124
36.5k
                    if (bRepeatedParticle)
3125
1.56k
                    {
3126
1.56k
                        oField.SetName("value");
3127
1.56k
                        oField.SetMinOccurs(1);
3128
1.56k
                        oField.SetMaxOccurs(1);
3129
1.56k
                        oField.SetNotNullable(true);
3130
1.56k
                    }
3131
34.9k
                    else
3132
34.9k
                    {
3133
34.9k
                        if (nMinOccurs == 0)
3134
287
                        {
3135
574
                            for (size_t j = 0; j < aoFields.size(); j++)
3136
287
                            {
3137
287
                                aoFields[j].SetMinOccurs(0);
3138
287
                                aoFields[j].SetNotNullable(false);
3139
287
                            }
3140
287
                        }
3141
3142
34.9k
                        oField.SetName(osPrefixedEltName);
3143
34.9k
                        oField.SetMinOccurs(nMinOccurs);
3144
34.9k
                        oField.SetMaxOccurs(nMaxOccurs);
3145
34.9k
                    }
3146
36.5k
                    oField.SetXPath(osElementXPath);
3147
36.5k
                    oField.SetDocumentation(GetAnnotationDoc(poElt));
3148
3149
36.5k
                    aoFields.push_back(std::move(oField));
3150
36.5k
                }
3151
3152
                // Is it an element that we already visited ? (cycle)
3153
58.9k
                else if (poEltCT->getParticle() != nullptr &&
3154
58.9k
                         oSetVisitedModelGroups.find(
3155
5.38k
                             poEltCT->getParticle()->getModelGroupTerm()) !=
3156
5.38k
                             oSetVisitedModelGroups.end())
3157
0
                {
3158
0
                    CreateNonNestedRelationship(
3159
0
                        poElt, apoImplEltList, oClass,
3160
0
                        bMoveNestedClassToTop ? 1 : MAXOCCURS_UNLIMITED,
3161
0
                        bEltNameWillNeedPrefix,
3162
0
                        true,  // force junction table
3163
0
                        false  // regular case
3164
0
                    );
3165
3166
0
                    bNothingMoreToDo = true;
3167
0
                }
3168
3169
58.9k
                else
3170
58.9k
                {
3171
58.9k
                    GMLASFeatureClass oNestedClass;
3172
58.9k
                    oNestedClass.SetName(oClass.GetName() + "_" +
3173
58.9k
                                         osPrefixedEltName);
3174
58.9k
                    oNestedClass.SetXPath(osElementXPath);
3175
58.9k
                    oNestedClass.SetDocumentation(GetAnnotationDoc(poElt));
3176
3177
                    // NULL can happen, for example for gml:ReferenceType
3178
                    // that is an empty sequence with just attributes
3179
58.9k
                    if (poEltCT->getParticle() != nullptr)
3180
5.38k
                    {
3181
#ifdef DEBUG_VERBOSE
3182
                        CPLDebug("GMLAS", "Exploring %s",
3183
                                 osElementXPath.c_str());
3184
#endif
3185
5.38k
                        std::set<XSModelGroup *> oSetNewVisitedModelGroups(
3186
5.38k
                            oSetVisitedModelGroups);
3187
3188
5.38k
                        std::map<CPLString, int>
3189
5.38k
                            oMapCountOccurrencesOfSameNameSub;
3190
5.38k
                        BuildMapCountOccurrencesOfSameName(
3191
5.38k
                            poEltCT->getParticle()->getModelGroupTerm(),
3192
5.38k
                            oMapCountOccurrencesOfSameNameSub);
3193
3194
5.38k
                        if (!ExploreModelGroup(
3195
5.38k
                                poEltCT->getParticle()->getModelGroupTerm(),
3196
5.38k
                                nullptr, oNestedClass, nRecursionCounter + 1,
3197
5.38k
                                oSetNewVisitedModelGroups, poModel,
3198
5.38k
                                oMapCountOccurrencesOfSameNameSub))
3199
0
                        {
3200
0
                            return false;
3201
0
                        }
3202
5.38k
                    }
3203
3204
                    // If we have a element of type gml:ReferenceType that has
3205
                    // a targetElement in its annotation.appinfo, then create
3206
                    // a dedicated field to have cross-layer relationships.
3207
58.9k
                    if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
3208
58.9k
                        transcode(poTypeDef->getName()) == "ReferenceType" &&
3209
58.9k
                        !osTargetElement.empty())
3210
0
                    {
3211
0
                        XSElementDeclaration *poTargetElt =
3212
0
                            GetTopElementDeclarationFromXPath(osTargetElement,
3213
0
                                                              poModel);
3214
                        // TODO: even for non abstract we should probably
3215
                        // handle substitutions
3216
0
                        if (poTargetElt != nullptr &&
3217
0
                            !poTargetElt->getAbstract())
3218
0
                        {
3219
0
                            bool bHasRequiredId = false;
3220
0
                            XSComplexTypeDefinition *poTargetEltCT =
3221
0
                                IsEltCompatibleOfFC(poTargetElt);
3222
0
                            if (poTargetEltCT)
3223
0
                            {
3224
0
                                XSAttributeUseList *poTargetEltAttrList =
3225
0
                                    poTargetEltCT->getAttributeUses();
3226
0
                                const size_t nTEAttrListSize =
3227
0
                                    (poTargetEltAttrList != nullptr)
3228
0
                                        ? poTargetEltAttrList->size()
3229
0
                                        : 0;
3230
0
                                for (size_t j = 0; j < nTEAttrListSize; ++j)
3231
0
                                {
3232
0
                                    XSAttributeUse *poTEAttr =
3233
0
                                        poTargetEltAttrList->elementAt(j);
3234
0
                                    XSAttributeDeclaration *poTEAttrDecl =
3235
0
                                        poTEAttr->getAttrDeclaration();
3236
0
                                    XSSimpleTypeDefinition *poTEAttrType =
3237
0
                                        poTEAttrDecl->getTypeDefinition();
3238
0
                                    if (transcode(poTEAttrType->getName()) ==
3239
0
                                            szXS_ID &&
3240
0
                                        poTEAttr->getRequired())
3241
0
                                    {
3242
0
                                        bHasRequiredId = true;
3243
0
                                        break;
3244
0
                                    }
3245
0
                                }
3246
0
                            }
3247
0
                            if (bHasRequiredId && !m_bAlwaysGenerateOGRId)
3248
0
                            {
3249
                                // If the element is nillable, then we
3250
                                // need an extra field to be able to distinguish
3251
                                // between the case of the missing element or
3252
                                // the element with xsi:nil="true"
3253
0
                                if (poElt->getNillable() && !m_bUseNullState)
3254
0
                                {
3255
0
                                    GMLASField oFieldNil;
3256
0
                                    oFieldNil.SetName(osPrefixedEltName + "_" +
3257
0
                                                      szNIL);
3258
0
                                    oFieldNil.SetXPath(osElementXPath + "/" +
3259
0
                                                       szAT_XSI_NIL);
3260
0
                                    oFieldNil.SetType(GMLAS_FT_BOOLEAN,
3261
0
                                                      "boolean");
3262
0
                                    oFieldNil.SetMinOccurs(0);
3263
0
                                    oFieldNil.SetMaxOccurs(1);
3264
0
                                    aoFields.push_back(std::move(oFieldNil));
3265
0
                                }
3266
3267
0
                                GMLASField oField;
3268
                                // Fake xpath
3269
0
                                oField.SetXPath(
3270
0
                                    GMLASField::
3271
0
                                        MakePKIDFieldXPathFromXLinkHrefXPath(
3272
0
                                            osElementXPath + "/" +
3273
0
                                            szAT_XLINK_HREF));
3274
0
                                oField.SetName(osPrefixedEltName +
3275
0
                                               szPKID_SUFFIX);
3276
0
                                oField.SetMinOccurs(0);
3277
0
                                oField.SetMaxOccurs(1);
3278
0
                                oField.SetType(GMLAS_FT_STRING, szXS_STRING);
3279
0
                                oField.SetCategory(
3280
0
                                    GMLASField::
3281
0
                                        PATH_TO_CHILD_ELEMENT_WITH_LINK);
3282
0
                                oField.SetRelatedClassXPath(osTargetElement);
3283
0
                                aoFields.push_back(std::move(oField));
3284
0
                            }
3285
0
                        }
3286
0
                        else if (poTargetElt != nullptr &&
3287
0
                                 poTargetElt->getAbstract())
3288
0
                        {
3289
                            // If the element is nillable, then we
3290
                            // need an extra field to be able to distinguish
3291
                            // between the case of the missing element or the
3292
                            // element with xsi:nil="true"
3293
0
                            if (poElt->getNillable() && !m_bUseNullState)
3294
0
                            {
3295
0
                                GMLASField oFieldNil;
3296
0
                                oFieldNil.SetName(osPrefixedEltName + "_" +
3297
0
                                                  szNIL);
3298
0
                                oFieldNil.SetXPath(osElementXPath + "/" +
3299
0
                                                   szAT_XSI_NIL);
3300
0
                                oFieldNil.SetType(GMLAS_FT_BOOLEAN, "boolean");
3301
0
                                oFieldNil.SetMinOccurs(0);
3302
0
                                oFieldNil.SetMaxOccurs(1);
3303
0
                                aoFields.push_back(std::move(oFieldNil));
3304
0
                            }
3305
3306
                            // e.g importing
3307
                            // http://inspire.ec.europa.eu/schemas/ad/4.0
3308
                            // references bu-base:AbstractConstruction, but
3309
                            // sometimes there are no realization available for
3310
                            // it, so no need to be verbose about that.
3311
0
                            std::vector<XSElementDeclaration *>
3312
0
                                apoImplTargetEltList;
3313
0
                            GetConcreteImplementationTypes(
3314
0
                                poTargetElt, apoImplTargetEltList);
3315
0
                            if (!apoImplTargetEltList.empty())
3316
0
                            {
3317
0
                                CPLDebug("GMLAS",
3318
0
                                         "Not handled: targetElement %s of %s "
3319
0
                                         "is abstract but has substitutions",
3320
0
                                         osTargetElement.c_str(),
3321
0
                                         osElementXPath.c_str());
3322
0
                            }
3323
0
                        }
3324
0
                        else
3325
0
                        {
3326
                            // This shouldn't happen with consistent schemas
3327
                            // but as targetElement is in <annotation>, no
3328
                            // general-purpose XSD validator can ensure this
3329
0
                            CPLDebug("GMLAS",
3330
0
                                     "%s is a targetElement of %s, "
3331
0
                                     "but cannot be found",
3332
0
                                     osTargetElement.c_str(),
3333
0
                                     osElementXPath.c_str());
3334
0
                        }
3335
0
                    }
3336
3337
                    // Can we move the nested class(es) one level up ?
3338
58.9k
                    if (bMoveNestedClassToTop)
3339
57.9k
                    {
3340
                        // Case of an element like
3341
                        //   <xs:element name="foo">
3342
                        //      <xs:complexType>
3343
                        //          <xs:sequence>
3344
3345
57.9k
                        const std::vector<GMLASField> &osNestedClassFields =
3346
57.9k
                            oNestedClass.GetFields();
3347
248k
                        for (size_t j = 0; j < osNestedClassFields.size(); j++)
3348
190k
                        {
3349
190k
                            GMLASField oField(osNestedClassFields[j]);
3350
190k
                            oField.SetName(osPrefixedEltName + "_" +
3351
190k
                                           oField.GetName());
3352
190k
                            if (nMinOccurs == 0 ||
3353
190k
                                (poEltCT->getParticle() != nullptr &&
3354
180k
                                 poEltCT->getParticle()->getMinOccurs() == 0))
3355
10.2k
                            {
3356
10.2k
                                oField.SetMinOccurs(0);
3357
10.2k
                                oField.SetNotNullable(false);
3358
10.2k
                            }
3359
190k
                            aoFields.push_back(std::move(oField));
3360
190k
                        }
3361
3362
57.9k
                        aoNestedClasses = oNestedClass.GetNestedClasses();
3363
57.9k
                    }
3364
1.04k
                    else
3365
1.04k
                    {
3366
                        // Case of an element like
3367
                        //   <xs:element name="foo">
3368
                        //      <xs:complexType>
3369
                        //          <xs:sequence maxOccurs="unbounded">
3370
                        // or
3371
                        //   <xs:element name="foo" maxOccurs="unbounded">
3372
                        //      <xs:complexType>
3373
                        //          <xs:sequence>
3374
                        // or even
3375
                        //   <xs:element name="foo" maxOccurs="unbounded">
3376
                        //      <xs:complexType>
3377
                        //          <xs:sequence maxOccurs="unbounded">
3378
1.04k
                        if (m_bUseArrays && nAttrListSize == 0 &&
3379
1.04k
                            oNestedClass.GetNestedClasses().empty() &&
3380
1.04k
                            oNestedClass.GetFields().size() == 1 &&
3381
1.04k
                            IsCompatibleOfArray(
3382
148
                                oNestedClass.GetFields()[0].GetType()) &&
3383
1.04k
                            oNestedClass.GetFields()[0].GetCategory() !=
3384
20
                                GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK)
3385
0
                        {
3386
                            // In the case the sequence has a single element,
3387
                            // compatible of array type, and no attribute and
3388
                            // no nested classes, then add an array attribute
3389
                            // at the top-level
3390
0
                            GMLASField oField(oNestedClass.GetFields()[0]);
3391
0
                            oField.SetName(osPrefixedEltName + "_" +
3392
0
                                           oField.GetName());
3393
0
                            if (oField.GetMaxOccurs() == 1 &&
3394
0
                                bEltRepeatedParticle &&
3395
0
                                poEltCT->getParticle() != nullptr)
3396
0
                            {
3397
0
                                oField.SetMaxOccurs(nMaxOccursEltParticle);
3398
0
                            }
3399
0
                            oField.SetArray(true);
3400
0
                            oClass.AddField(oField);
3401
0
                        }
3402
1.04k
                        else
3403
1.04k
                        {
3404
1.04k
                            if (!aoFields.empty() && bEltRepeatedParticle)
3405
0
                            {
3406
                                // We have attributes and the sequence is
3407
                                // repeated
3408
                                //   <xs:element name="foo"
3409
                                //   maxOccurs="unbounded">
3410
                                //      <xs:complexType>
3411
                                //          <xs:sequence maxOccurs="unbounded">
3412
                                //              ...
3413
                                //          </xs:sequence>
3414
                                //          <xs:attribute .../>
3415
                                //      </xs:complexType>
3416
                                //   </xs:element>
3417
                                // So we need to create an
3418
                                // intermediate class to store them
3419
0
                                GMLASFeatureClass oIntermediateNestedClass;
3420
0
                                oIntermediateNestedClass.SetName(
3421
0
                                    oClass.GetName() + "_" + osPrefixedEltName);
3422
0
                                oIntermediateNestedClass.SetXPath(
3423
0
                                    osElementXPath);
3424
3425
0
                                oIntermediateNestedClass.PrependFields(
3426
0
                                    aoFields);
3427
3428
0
                                oNestedClass.SetName(oClass.GetName() + "_" +
3429
0
                                                     osPrefixedEltName +
3430
0
                                                     "_sequence");
3431
0
                                oNestedClass.SetXPath(oNestedClass.GetXPath() +
3432
0
                                                      szEXTRA_SUFFIX +
3433
0
                                                      "sequence");
3434
0
                                oNestedClass.SetIsRepeatedSequence(true);
3435
3436
0
                                GMLASField oField;
3437
0
                                oField.SetXPath(osElementXPath);
3438
0
                                oField.SetCategory(
3439
0
                                    GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
3440
0
                                if (nMaxOccursEltParticle != 1)
3441
0
                                    oField.SetRepetitionOnSequence(true);
3442
0
                                oField.SetMinOccurs(nMinOccursEltParticle);
3443
0
                                oField.SetMaxOccurs(nMaxOccursEltParticle);
3444
0
                                oField.SetRelatedClassXPath(
3445
0
                                    oNestedClass.GetXPath());
3446
0
                                oIntermediateNestedClass.AddField(oField);
3447
3448
0
                                oIntermediateNestedClass.AddNestedClass(
3449
0
                                    oNestedClass);
3450
3451
0
                                oClass.AddNestedClass(oIntermediateNestedClass);
3452
0
                            }
3453
1.04k
                            else
3454
1.04k
                            {
3455
1.04k
                                oNestedClass.SetIsRepeatedSequence(
3456
1.04k
                                    bEltRepeatedParticle);
3457
1.04k
                                oNestedClass.PrependFields(aoFields);
3458
3459
1.04k
                                oClass.AddNestedClass(oNestedClass);
3460
1.04k
                            }
3461
3462
1.04k
                            GMLASField oField;
3463
1.04k
                            oField.SetName(osPrefixedEltName);
3464
1.04k
                            oField.SetXPath(osElementXPath);
3465
1.04k
                            if (bRepeatedParticle)
3466
1.04k
                            {
3467
1.04k
                                if (poEltCT->getParticle() != nullptr)
3468
697
                                {
3469
697
                                    oField.SetMinOccurs(ComposeMinOccurs(
3470
697
                                        nMinOccurs, nMinOccursEltParticle));
3471
697
                                    oField.SetMaxOccurs(ComposeMaxOccurs(
3472
697
                                        nMaxOccurs, nMaxOccursEltParticle));
3473
697
                                }
3474
345
                                else
3475
345
                                {
3476
345
                                    oField.SetMinOccurs(nMinOccurs);
3477
345
                                    oField.SetMaxOccurs(nMaxOccurs);
3478
345
                                }
3479
1.04k
                            }
3480
0
                            else if (poEltCT->getParticle() != nullptr)
3481
0
                            {
3482
0
                                if (nMaxOccursEltParticle != 1)
3483
0
                                    oField.SetRepetitionOnSequence(true);
3484
0
                                oField.SetMinOccurs(nMinOccursEltParticle);
3485
0
                                oField.SetMaxOccurs(nMaxOccursEltParticle);
3486
0
                            }
3487
1.04k
                            oField.SetCategory(
3488
1.04k
                                GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
3489
1.04k
                            oField.SetRelatedClassXPath(oField.GetXPath());
3490
1.04k
                            oClass.AddField(oField);
3491
1.04k
                        }
3492
3493
1.04k
                        bNothingMoreToDo = true;
3494
1.04k
                    }
3495
58.9k
                }
3496
3497
95.5k
                if (bNothingMoreToDo)
3498
1.04k
                {
3499
                    // Nothing to do
3500
1.04k
                }
3501
94.4k
                else if (bRepeatedParticle)
3502
1.56k
                {
3503
1.56k
                    GMLASFeatureClass oNestedClass;
3504
1.56k
                    oNestedClass.SetName(oClass.GetName() + "_" +
3505
1.56k
                                         osPrefixedEltName);
3506
1.56k
                    oNestedClass.SetXPath(osElementXPath);
3507
1.56k
                    oNestedClass.AppendFields(aoFields);
3508
1.56k
                    oNestedClass.SetDocumentation(GetAnnotationDoc(poElt));
3509
1.56k
                    oClass.AddNestedClass(oNestedClass);
3510
3511
1.56k
                    GMLASField oField;
3512
1.56k
                    oField.SetName(osPrefixedEltName);
3513
1.56k
                    oField.SetXPath(osElementXPath);
3514
1.56k
                    oField.SetMinOccurs((bIsChoice) ? 0 : nMinOccurs);
3515
1.56k
                    oField.SetMaxOccurs(nMaxOccurs);
3516
1.56k
                    oField.SetCategory(
3517
1.56k
                        GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
3518
1.56k
                    oField.SetRelatedClassXPath(oField.GetXPath());
3519
1.56k
                    oClass.AddField(oField);
3520
1.56k
                }
3521
92.9k
                else
3522
92.9k
                {
3523
92.9k
                    oClass.AppendFields(aoFields);
3524
94.7k
                    for (size_t j = 0; j < aoNestedClasses.size(); j++)
3525
1.86k
                    {
3526
1.86k
                        oClass.AddNestedClass(aoNestedClasses[j]);
3527
1.86k
                    }
3528
92.9k
                }
3529
95.5k
            }
3530
549k
        }
3531
7.02k
        else if (poParticle->getTermType() == XSParticle::TERM_MODELGROUP)
3532
20
        {
3533
20
            XSModelGroup *psSubModelGroup = poParticle->getModelGroupTerm();
3534
20
            if (bRepeatedParticle)
3535
0
            {
3536
0
                GMLASFeatureClass oNestedClass;
3537
0
                CPLString osGroupName;
3538
0
                XSModelGroupDefinition *psGroupDefinition =
3539
0
                    GetGroupDefinition(psSubModelGroup);
3540
0
                if (psGroupDefinition != nullptr)
3541
0
                {
3542
0
                    osGroupName = transcode(psGroupDefinition->getName());
3543
0
                    oNestedClass.SetDocumentation(
3544
0
                        GetAnnotationDoc(psGroupDefinition->getAnnotation()));
3545
0
                }
3546
0
                else
3547
0
                {
3548
                    // Is it a <xs:choice maxOccurs=">1|unbounded"
3549
0
                    if (psSubModelGroup->getCompositor() ==
3550
0
                        XSModelGroup::COMPOSITOR_CHOICE)
3551
0
                    {
3552
0
                        std::set<XSModelGroup *> oSetNewVisitedModelGroups(
3553
0
                            oSetVisitedModelGroups);
3554
0
                        GMLASFeatureClass oTmpClass;
3555
0
                        oTmpClass.SetName(oClass.GetName());
3556
0
                        oTmpClass.SetXPath(oClass.GetXPath());
3557
0
                        if (!ExploreModelGroup(psSubModelGroup, nullptr,
3558
0
                                               oTmpClass, nRecursionCounter + 1,
3559
0
                                               oSetNewVisitedModelGroups,
3560
0
                                               poModel,
3561
0
                                               oMapCountOccurrencesOfSameName))
3562
0
                        {
3563
0
                            return false;
3564
0
                        }
3565
0
                        bool bHasArray = false;
3566
0
                        std::vector<GMLASField> &oTmpFields =
3567
0
                            oTmpClass.GetFields();
3568
0
                        for (size_t j = 0; j < oTmpFields.size(); ++j)
3569
0
                        {
3570
0
                            if (oTmpFields[j].IsArray())
3571
0
                            {
3572
0
                                bHasArray = true;
3573
0
                                break;
3574
0
                            }
3575
0
                        }
3576
0
                        if (!bHasArray)
3577
0
                        {
3578
0
                            for (size_t j = 0; j < oTmpFields.size(); ++j)
3579
0
                            {
3580
0
                                oTmpFields[j].SetMayAppearOutOfOrder(true);
3581
0
                                oClass.AddField(oTmpFields[j]);
3582
0
                            }
3583
0
                            return true;
3584
0
                        }
3585
0
                    }
3586
3587
0
                    nGroup++;
3588
0
                    osGroupName = CPLSPrintf("_group%d", nGroup);
3589
0
                }
3590
0
                oNestedClass.SetName(oClass.GetName() + "_" + osGroupName);
3591
0
                oNestedClass.SetIsGroup(true);
3592
0
                oNestedClass.SetIsRepeatedSequence(true);
3593
                // Caution: we will change it afterwards !
3594
0
                oNestedClass.SetXPath(oClass.GetXPath());
3595
0
                std::set<XSModelGroup *> oSetNewVisitedModelGroups(
3596
0
                    oSetVisitedModelGroups);
3597
0
                if (!ExploreModelGroup(psSubModelGroup, nullptr, oNestedClass,
3598
0
                                       nRecursionCounter + 1,
3599
0
                                       oSetNewVisitedModelGroups, poModel,
3600
0
                                       oMapCountOccurrencesOfSameName))
3601
0
                {
3602
0
                    return false;
3603
0
                }
3604
                // This is a nasty hack. We set a unique fake xpath *AFTER*
3605
                // processing the group, so that we can add a fake GROUP field
3606
                // pointing to the nested class
3607
0
                oNestedClass.SetXPath(oClass.GetXPath() + szEXTRA_SUFFIX +
3608
0
                                      osGroupName);
3609
3610
0
                if (m_bUseArrays && oNestedClass.GetFields().size() == 1 &&
3611
0
                    IsCompatibleOfArray(oNestedClass.GetFields()[0].GetType()))
3612
0
                {
3613
0
                    GMLASField oField(oNestedClass.GetFields()[0]);
3614
0
                    oField.SetMinOccurs(
3615
0
                        ComposeMinOccurs(oField.GetMinOccurs(), nMinOccurs));
3616
0
                    oField.SetMaxOccurs(
3617
0
                        ComposeMaxOccurs(oField.GetMaxOccurs(), nMaxOccurs));
3618
0
                    oField.SetArray(true);
3619
0
                    oClass.AddField(oField);
3620
0
                }
3621
0
                else
3622
0
                {
3623
0
                    oClass.AddNestedClass(oNestedClass);
3624
3625
0
                    GMLASField oField;
3626
0
                    oField.SetCategory(GMLASField::GROUP);
3627
0
                    oField.SetMinOccurs(nMinOccurs);
3628
0
                    oField.SetMaxOccurs(nMaxOccurs);
3629
0
                    oField.SetRelatedClassXPath(oNestedClass.GetXPath());
3630
0
                    oClass.AddField(oField);
3631
0
                }
3632
0
            }
3633
20
            else
3634
20
            {
3635
20
                std::set<XSModelGroup *> oSetNewVisitedModelGroups(
3636
20
                    oSetVisitedModelGroups);
3637
20
                if (!ExploreModelGroup(psSubModelGroup, nullptr, oClass,
3638
20
                                       nRecursionCounter + 1,
3639
20
                                       oSetNewVisitedModelGroups, poModel,
3640
20
                                       oMapCountOccurrencesOfSameName))
3641
0
                {
3642
0
                    return false;
3643
0
                }
3644
20
            }
3645
20
        }
3646
7.00k
        else if (poParticle->getTermType() == XSParticle::TERM_WILDCARD)
3647
7.00k
        {
3648
            /* Special case for a layer that matches everything, as found */
3649
            /* in swe:extension */
3650
7.00k
            XSWildcard *poWildcard = poParticle->getWildcardTerm();
3651
7.00k
            GMLASField oField;
3652
7.00k
            oField.SetXPath(oClass.GetXPath() + szMATCH_ALL);
3653
7.00k
            oField.SetName("value");
3654
7.00k
            oField.SetType(GMLAS_FT_ANYTYPE, szXS_ANY_TYPE);
3655
7.00k
            oField.SetIncludeThisEltInBlob(true);
3656
7.00k
            oField.SetMinOccurs(nMinOccurs);
3657
7.00k
            oField.SetMaxOccurs(1);
3658
7.00k
            oField.SetDocumentation(
3659
7.00k
                GetAnnotationDoc(poWildcard->getAnnotation()));
3660
7.00k
            oClass.AddField(oField);
3661
7.00k
        }
3662
556k
    }
3663
3664
14.8k
    return true;
3665
14.8k
}