Coverage Report

Created: 2025-11-15 08:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/gml/gmlhandler.cpp
Line
Count
Source
1
/**********************************************************************
2
 *
3
 * Project:  GML Reader
4
 * Purpose:  Implementation of GMLHandler class.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 **********************************************************************
8
 * Copyright (c) 2002, Frank Warmerdam
9
 * Copyright (c) 2008-2014, Even Rouault <even dot rouault at spatialys.com>
10
 *
11
 * SPDX-License-Identifier: MIT
12
 ****************************************************************************/
13
14
#include "cpl_port.h"
15
#include "gmlreader.h"
16
#include "gmlreaderp.h"
17
18
#include <algorithm>
19
#include <climits>
20
#include <cstddef>
21
#include <cstdlib>
22
#include <cstring>
23
#include <memory>
24
#include <string>
25
#include <vector>
26
27
#include "cpl_conv.h"
28
#include "cpl_error.h"
29
#include "cpl_hash_set.h"
30
#include "cpl_minixml.h"
31
#include "cpl_string.h"
32
#include "cpl_vsi.h"
33
#ifdef HAVE_EXPAT
34
#include "expat.h"
35
#include "expat_external.h"
36
#endif
37
#include "ogr_core.h"
38
#ifdef HAVE_XERCES
39
#include "ogr_xerces.h"
40
#endif
41
42
#ifdef HAVE_XERCES
43
44
/************************************************************************/
45
/*                        GMLXercesHandler()                            */
46
/************************************************************************/
47
48
GMLXercesHandler::GMLXercesHandler(GMLReader *poReader)
49
4.14k
    : GMLHandler(poReader), m_nEntityCounter(0)
50
4.14k
{
51
4.14k
}
52
53
/************************************************************************/
54
/*                    GMLXercesHandlerDealWithError()                   */
55
/************************************************************************/
56
57
static void GMLXercesHandlerDealWithError(OGRErr eErr)
58
910k
{
59
910k
    if (eErr == OGRERR_NOT_ENOUGH_MEMORY)
60
0
    {
61
0
        throw SAXNotSupportedException("Out of memory");
62
0
    }
63
910k
    else if (eErr != OGRERR_NONE)
64
60
    {
65
60
        throw SAXNotSupportedException("Other error during parsing");
66
60
    }
67
910k
}
68
69
/************************************************************************/
70
/*                            startElement()                            */
71
/************************************************************************/
72
73
void GMLXercesHandler::startElement(const XMLCh *const /*uri*/,
74
                                    const XMLCh *const localname,
75
                                    const XMLCh *const /*qname*/,
76
                                    const Attributes &attrs)
77
391k
{
78
391k
    m_nEntityCounter = 0;
79
80
391k
    transcode(localname, m_osElement);
81
82
391k
    GMLXercesHandlerDealWithError(GMLHandler::startElement(
83
391k
        m_osElement.c_str(), static_cast<int>(m_osElement.size()),
84
391k
        const_cast<Attributes *>(&attrs)));
85
391k
}
86
87
/************************************************************************/
88
/*                             endElement()                             */
89
/************************************************************************/
90
void GMLXercesHandler::endElement(const XMLCh *const /*uri*/,
91
                                  const XMLCh *const /*localname*/,
92
                                  const XMLCh *const /*qname */)
93
347k
{
94
347k
    m_nEntityCounter = 0;
95
96
347k
    GMLXercesHandlerDealWithError(GMLHandler::endElement());
97
347k
}
98
99
/************************************************************************/
100
/*                             characters()                             */
101
/************************************************************************/
102
103
void GMLXercesHandler::characters(const XMLCh *const chars_in,
104
                                  const XMLSize_t length)
105
106
171k
{
107
171k
    transcode(chars_in, m_osCharacters, static_cast<int>(length));
108
171k
    GMLXercesHandlerDealWithError(GMLHandler::dataHandler(
109
171k
        m_osCharacters.c_str(), static_cast<int>(m_osCharacters.size())));
110
171k
}
111
112
/************************************************************************/
113
/*                             fatalError()                             */
114
/************************************************************************/
115
116
void GMLXercesHandler::fatalError(const SAXParseException &exception)
117
118
3.96k
{
119
3.96k
    CPLString osMsg;
120
3.96k
    transcode(exception.getMessage(), osMsg);
121
3.96k
    CPLError(CE_Failure, CPLE_AppDefined,
122
3.96k
             "XML Parsing Error: %s at line %d, column %d\n", osMsg.c_str(),
123
3.96k
             static_cast<int>(exception.getLineNumber()),
124
3.96k
             static_cast<int>(exception.getColumnNumber()));
125
3.96k
}
126
127
/************************************************************************/
128
/*                             startEntity()                            */
129
/************************************************************************/
130
131
void GMLXercesHandler::startEntity(const XMLCh *const /* name */)
132
0
{
133
0
    m_nEntityCounter++;
134
0
    if (m_nEntityCounter > 1000 && !m_poReader->HasStoppedParsing())
135
0
    {
136
0
        throw SAXNotSupportedException(
137
0
            "File probably corrupted (million laugh pattern)");
138
0
    }
139
0
}
140
141
/************************************************************************/
142
/*                               GetFID()                               */
143
/************************************************************************/
144
145
const char *GMLXercesHandler::GetFID(void *attr)
146
33.9k
{
147
33.9k
    const Attributes *attrs = static_cast<const Attributes *>(attr);
148
33.9k
    const XMLCh achFID[] = {'f', 'i', 'd', '\0'};
149
33.9k
    int nFIDIndex = attrs->getIndex(achFID);
150
33.9k
    if (nFIDIndex != -1)
151
2.60k
    {
152
2.60k
        transcode(attrs->getValue(nFIDIndex), m_osFID);
153
2.60k
        return m_osFID.c_str();
154
2.60k
    }
155
31.3k
    else
156
31.3k
    {
157
31.3k
        const XMLCh achGMLID[] = {'g', 'm', 'l', ':', 'i', 'd', '\0'};
158
31.3k
        nFIDIndex = attrs->getIndex(achGMLID);
159
31.3k
        if (nFIDIndex != -1)
160
2
        {
161
2
            transcode(attrs->getValue(nFIDIndex), m_osFID);
162
2
            return m_osFID.c_str();
163
2
        }
164
31.3k
    }
165
166
31.3k
    m_osFID.resize(0);
167
31.3k
    return nullptr;
168
33.9k
}
169
170
/************************************************************************/
171
/*                        AddAttributes()                               */
172
/************************************************************************/
173
174
CPLXMLNode *GMLXercesHandler::AddAttributes(CPLXMLNode *psNode, void *attr)
175
31.5k
{
176
31.5k
    const Attributes *attrs = static_cast<const Attributes *>(attr);
177
178
31.5k
    CPLXMLNode *psLastChild = nullptr;
179
180
32.6k
    for (unsigned int i = 0; i < attrs->getLength(); i++)
181
1.07k
    {
182
1.07k
        transcode(attrs->getQName(i), m_osAttrName);
183
1.07k
        transcode(attrs->getValue(i), m_osAttrValue);
184
185
1.07k
        CPLXMLNode *psChild =
186
1.07k
            CPLCreateXMLNode(nullptr, CXT_Attribute, m_osAttrName.c_str());
187
1.07k
        CPLCreateXMLNode(psChild, CXT_Text, m_osAttrValue.c_str());
188
189
1.07k
        if (psLastChild == nullptr)
190
885
            psNode->psChild = psChild;
191
193
        else
192
193
            psLastChild->psNext = psChild;
193
1.07k
        psLastChild = psChild;
194
1.07k
    }
195
196
31.5k
    return psLastChild;
197
31.5k
}
198
199
/************************************************************************/
200
/*                    GetAttributeValue()                               */
201
/************************************************************************/
202
203
char *GMLXercesHandler::GetAttributeValue(void *attr,
204
                                          const char *pszAttributeName)
205
117k
{
206
117k
    const Attributes *attrs = static_cast<const Attributes *>(attr);
207
120k
    for (unsigned int i = 0; i < attrs->getLength(); i++)
208
3.24k
    {
209
3.24k
        transcode(attrs->getQName(i), m_osAttrName);
210
3.24k
        if (m_osAttrName == pszAttributeName)
211
75
        {
212
75
            transcode(attrs->getValue(i), m_osAttrValue);
213
75
            return CPLStrdup(m_osAttrValue);
214
75
        }
215
3.24k
    }
216
117k
    return nullptr;
217
117k
}
218
219
/************************************************************************/
220
/*                    GetAttributeByIdx()                               */
221
/************************************************************************/
222
223
char *GMLXercesHandler::GetAttributeByIdx(void *attr, unsigned int idx,
224
                                          char **ppszKey)
225
117k
{
226
117k
    const Attributes *attrs = static_cast<const Attributes *>(attr);
227
117k
    if (idx >= attrs->getLength())
228
114k
    {
229
114k
        *ppszKey = nullptr;
230
114k
        return nullptr;
231
114k
    }
232
2.98k
    transcode(attrs->getQName(idx), m_osAttrName);
233
2.98k
    transcode(attrs->getValue(idx), m_osAttrValue);
234
235
2.98k
    *ppszKey = CPLStrdup(m_osAttrName);
236
2.98k
    return CPLStrdup(m_osAttrValue);
237
117k
}
238
239
#endif
240
241
#ifdef HAVE_EXPAT
242
243
/************************************************************************/
244
/*                            GMLExpatHandler()                         */
245
/************************************************************************/
246
247
GMLExpatHandler::GMLExpatHandler(GMLReader *poReader, XML_Parser oParser)
248
3.27k
    : GMLHandler(poReader), m_oParser(oParser), m_bStopParsing(false),
249
3.27k
      m_nDataHandlerCounter(0)
250
3.27k
{
251
3.27k
}
252
253
/************************************************************************/
254
/*                  GMLExpatHandler::DealWithError()                    */
255
/************************************************************************/
256
257
void GMLExpatHandler::DealWithError(OGRErr eErr)
258
3.20M
{
259
3.20M
    if (eErr != OGRERR_NONE)
260
31
    {
261
31
        m_bStopParsing = true;
262
31
        XML_StopParser(m_oParser, static_cast<XML_Bool>(false));
263
31
        if (eErr == OGRERR_NOT_ENOUGH_MEMORY)
264
0
            CPLError(CE_Failure, CPLE_OutOfMemory, "Out of memory");
265
31
    }
266
3.20M
}
267
268
/************************************************************************/
269
/*                           startElementCbk()                          */
270
/************************************************************************/
271
272
void XMLCALL GMLExpatHandler::startElementCbk(void *pUserData,
273
                                              const char *pszName,
274
                                              const char **ppszAttr)
275
276
613k
{
277
613k
    GMLExpatHandler *pThis = static_cast<GMLExpatHandler *>(pUserData);
278
613k
    if (pThis->m_bStopParsing)
279
0
        return;
280
281
613k
    const char *pszIter = pszName;
282
613k
    char ch = '\0';
283
10.3M
    while ((ch = *pszIter) != '\0')
284
9.72M
    {
285
9.72M
        if (ch == ':')
286
57.8k
            pszName = pszIter + 1;
287
9.72M
        pszIter++;
288
9.72M
    }
289
290
613k
    pThis->DealWithError(pThis->GMLHandler::startElement(
291
613k
        pszName, static_cast<int>(pszIter - pszName), ppszAttr));
292
613k
}
293
294
/************************************************************************/
295
/*                            endElementCbk()                           */
296
/************************************************************************/
297
void XMLCALL GMLExpatHandler::endElementCbk(void *pUserData,
298
                                            const char * /* pszName */)
299
587k
{
300
587k
    GMLExpatHandler *pThis = static_cast<GMLExpatHandler *>(pUserData);
301
587k
    if (pThis->m_bStopParsing)
302
15
        return;
303
304
587k
    pThis->DealWithError(pThis->GMLHandler::endElement());
305
587k
}
306
307
/************************************************************************/
308
/*                            dataHandlerCbk()                          */
309
/************************************************************************/
310
311
void XMLCALL GMLExpatHandler::dataHandlerCbk(void *pUserData, const char *data,
312
                                             int nLen)
313
314
2.00M
{
315
2.00M
    GMLExpatHandler *pThis = static_cast<GMLExpatHandler *>(pUserData);
316
2.00M
    if (pThis->m_bStopParsing)
317
0
        return;
318
319
2.00M
    pThis->m_nDataHandlerCounter++;
320
    // The size of the buffer that is fetched and that Expat parses is
321
    // PARSER_BUF_SIZE bytes. If the dataHandlerCbk() callback is called
322
    // more than PARSER_BUF_SIZE times, this means that one byte in the
323
    // file expands to more XML text fragments, which is the sign of a
324
    // likely abuse of <!ENTITY>
325
    // Note: the counter is zeroed by ResetDataHandlerCounter() before each
326
    // new XML parsing.
327
2.00M
    if (pThis->m_nDataHandlerCounter >= PARSER_BUF_SIZE)
328
1
    {
329
1
        CPLError(CE_Failure, CPLE_AppDefined,
330
1
                 "File probably corrupted (million laugh pattern)");
331
1
        pThis->m_bStopParsing = true;
332
1
        XML_StopParser(pThis->m_oParser, static_cast<XML_Bool>(false));
333
1
        return;
334
1
    }
335
336
2.00M
    pThis->DealWithError(pThis->GMLHandler::dataHandler(data, nLen));
337
2.00M
}
338
339
/************************************************************************/
340
/*                               GetFID()                               */
341
/************************************************************************/
342
343
const char *GMLExpatHandler::GetFID(void *attr)
344
52.9k
{
345
52.9k
    const char *const *papszIter = static_cast<const char *const *>(attr);
346
53.0k
    while (*papszIter)
347
892
    {
348
892
        if (strcmp(*papszIter, "fid") == 0 || strcmp(*papszIter, "gml:id") == 0)
349
705
        {
350
705
            return papszIter[1];
351
705
        }
352
187
        papszIter += 2;
353
187
    }
354
52.1k
    return nullptr;
355
52.9k
}
356
357
/************************************************************************/
358
/*                        AddAttributes()                               */
359
/************************************************************************/
360
361
CPLXMLNode *GMLExpatHandler::AddAttributes(CPLXMLNode *psNode, void *attr)
362
46.2k
{
363
46.2k
    const char **papszIter = static_cast<const char **>(attr);
364
365
46.2k
    CPLXMLNode *psLastChild = nullptr;
366
367
60.3k
    while (*papszIter)
368
14.0k
    {
369
14.0k
        CPLXMLNode *psChild =
370
14.0k
            CPLCreateXMLNode(nullptr, CXT_Attribute, papszIter[0]);
371
14.0k
        CPLCreateXMLNode(psChild, CXT_Text, papszIter[1]);
372
373
14.0k
        if (psLastChild == nullptr)
374
12.1k
            psNode->psChild = psChild;
375
1.97k
        else
376
1.97k
            psLastChild->psNext = psChild;
377
14.0k
        psLastChild = psChild;
378
379
14.0k
        papszIter += 2;
380
14.0k
    }
381
382
46.2k
    return psLastChild;
383
46.2k
}
384
385
/************************************************************************/
386
/*                    GetAttributeValue()                               */
387
/************************************************************************/
388
389
char *GMLExpatHandler::GetAttributeValue(void *attr,
390
                                         const char *pszAttributeName)
391
171k
{
392
171k
    const char *const *papszIter = static_cast<const char *const *>(attr);
393
173k
    while (*papszIter)
394
4.32k
    {
395
4.32k
        if (strcmp(*papszIter, pszAttributeName) == 0)
396
1.64k
        {
397
1.64k
            return CPLStrdup(papszIter[1]);
398
1.64k
        }
399
2.67k
        papszIter += 2;
400
2.67k
    }
401
169k
    return nullptr;
402
171k
}
403
404
/************************************************************************/
405
/*                    GetAttributeByIdx()                               */
406
/************************************************************************/
407
408
// CAUTION: should be called with increasing idx starting from 0 and
409
// no attempt to read beyond end of list.
410
char *GMLExpatHandler::GetAttributeByIdx(void *attr, unsigned int idx,
411
                                         char **ppszKey)
412
171k
{
413
171k
    const char *const *papszIter = static_cast<const char *const *>(attr);
414
171k
    if (papszIter[2 * idx] == nullptr)
415
168k
    {
416
168k
        *ppszKey = nullptr;
417
168k
        return nullptr;
418
168k
    }
419
2.52k
    *ppszKey = CPLStrdup(papszIter[2 * idx]);
420
2.52k
    return CPLStrdup(papszIter[2 * idx + 1]);
421
171k
}
422
423
#endif
424
425
static const char *const apszGMLGeometryElements[] = {
426
    "BoundingBox", /* ows:BoundingBox */
427
    "CompositeCurve",
428
    "CompositeSurface",
429
    "Shell", /* CityGML 3 */
430
    "Curve",
431
    "GeometryCollection", /* OGR < 1.8.0 bug... */
432
    "LineString",
433
    "MultiCurve",
434
    "MultiGeometry",
435
    "MultiLineString",
436
    "MultiPoint",
437
    "MultiPolygon",
438
    "MultiSurface",
439
    "Point",
440
    "Polygon",
441
    "PolygonPatch",
442
    "PolyhedralSurface",
443
    "SimplePolygon",    /* GML 3.3 compact encoding */
444
    "SimpleRectangle",  /* GML 3.3 compact encoding */
445
    "SimpleTriangle",   /* GML 3.3 compact encoding */
446
    "SimpleMultiPoint", /* GML 3.3 compact encoding */
447
    "Solid",
448
    "Surface",
449
    "Tin",
450
    "TopoCurve",
451
    "TopoSurface",
452
    "Triangle",
453
    "TriangulatedSurface"};
454
455
#define GML_GEOMETRY_TYPE_COUNT                                                \
456
546k
    static_cast<int>(sizeof(apszGMLGeometryElements) /                         \
457
546k
                     sizeof(apszGMLGeometryElements[0]))
458
459
bool OGRGMLIsGeometryElement(const char *pszElement)
460
2.81k
{
461
2.81k
    for (const auto &pszGMLElement : apszGMLGeometryElements)
462
78.6k
    {
463
78.6k
        if (strcmp(pszElement, pszGMLElement) == 0)
464
4
            return true;
465
78.6k
    }
466
2.80k
    return false;
467
2.81k
}
468
469
struct _GeometryNamesStruct
470
{
471
    unsigned long nHash;
472
    const char *pszName;
473
};
474
475
/************************************************************************/
476
/*                            GMLHandler()                              */
477
/************************************************************************/
478
479
GMLHandler::GMLHandler(GMLReader *poReader)
480
7.41k
    : pasGeometryNames(static_cast<GeometryNamesStruct *>(
481
7.41k
          CPLMalloc(GML_GEOMETRY_TYPE_COUNT * sizeof(GeometryNamesStruct)))),
482
      m_nSRSDimensionIfMissing(
483
7.41k
          atoi(CPLGetConfigOption("GML_SRS_DIMENSION_IF_MISSING", "0"))),
484
7.41k
      m_poReader(poReader), eAppSchemaType(APPSCHEMA_GENERIC), nStackDepth(0)
485
7.41k
{
486
215k
    for (int i = 0; i < GML_GEOMETRY_TYPE_COUNT; i++)
487
207k
    {
488
207k
        pasGeometryNames[i].pszName = apszGMLGeometryElements[i];
489
207k
        pasGeometryNames[i].nHash =
490
207k
            CPLHashSetHashStr(pasGeometryNames[i].pszName);
491
207k
    }
492
7.41k
    std::sort(pasGeometryNames, pasGeometryNames + GML_GEOMETRY_TYPE_COUNT,
493
7.41k
              [](const GeometryNamesStruct &a, const GeometryNamesStruct &b)
494
1.03M
              { return a.nHash < b.nHash; });
495
496
7.41k
    stateStack[0] = STATE_TOP;
497
7.41k
}
498
499
/************************************************************************/
500
/*                            ~GMLHandler()                             */
501
/************************************************************************/
502
503
GMLHandler::~GMLHandler()
504
505
7.41k
{
506
7.41k
    if (apsXMLNode.size() >= 2 && apsXMLNode[1].psNode != nullptr)
507
583
        CPLDestroyXMLNode(apsXMLNode[1].psNode);
508
509
7.41k
    CPLFree(m_pszCurField);
510
7.41k
    CPLFree(m_pszGeometry);
511
7.41k
    CPLFree(m_pszCityGMLGenericAttrName);
512
7.41k
    CPLFree(m_pszHref);
513
7.41k
    CPLFree(m_pszUom);
514
7.41k
    CPLFree(m_pszValue);
515
7.41k
    CPLFree(m_pszKieli);
516
7.41k
    CPLFree(pasGeometryNames);
517
7.41k
}
518
519
/************************************************************************/
520
/*                             startElement()                           */
521
/************************************************************************/
522
523
OGRErr GMLHandler::startElement(const char *pszName, int nLenName, void *attr)
524
1.00M
{
525
1.00M
    OGRErr eRet;
526
1.00M
    switch (stateStack[nStackDepth])
527
1.00M
    {
528
5.73k
        case STATE_TOP:
529
5.73k
            eRet = startElementTop(pszName, nLenName, attr);
530
5.73k
            break;
531
576k
        case STATE_DEFAULT:
532
576k
            eRet = startElementDefault(pszName, nLenName, attr);
533
576k
            break;
534
83.9k
        case STATE_FEATURE:
535
83.9k
            eRet = startElementFeatureAttribute(pszName, nLenName, attr);
536
83.9k
            break;
537
232k
        case STATE_PROPERTY:
538
232k
            eRet = startElementFeatureAttribute(pszName, nLenName, attr);
539
232k
            break;
540
0
        case STATE_FEATUREPROPERTY:
541
0
            eRet = startElementFeatureProperty(pszName, nLenName, attr);
542
0
            break;
543
44.2k
        case STATE_GEOMETRY:
544
44.2k
            eRet = startElementGeometry(pszName, nLenName, attr);
545
44.2k
            break;
546
56.3k
        case STATE_IGNORED_FEATURE:
547
56.3k
            eRet = OGRERR_NONE;
548
56.3k
            break;
549
1.18k
        case STATE_BOUNDED_BY:
550
1.18k
            eRet = startElementBoundedBy(pszName, nLenName, attr);
551
1.18k
            break;
552
4.04k
        case STATE_BOUNDED_BY_IN_FEATURE:
553
4.04k
            eRet = startElementGeometry(pszName, nLenName, attr);
554
4.04k
            break;
555
941
        case STATE_CITYGML_ATTRIBUTE:
556
941
            eRet = startElementCityGMLGenericAttr(pszName, nLenName, attr);
557
941
            break;
558
0
        default:
559
0
            eRet = OGRERR_NONE;
560
0
            break;
561
1.00M
    }
562
1.00M
    m_nDepth++;
563
1.00M
    if (m_nDepth == 64)
564
90
    {
565
        // Avoid performance issues on files like
566
        // https://bugs.chromium.org/p/oss-fuzz/issues/detail?id=21737
567
90
        if (m_nUnlimitedDepth < 0)
568
90
        {
569
90
            m_nUnlimitedDepth = EQUAL(
570
90
                CPLGetConfigOption("OGR_GML_NESTING_LEVEL", ""), "UNLIMITED");
571
90
        }
572
90
        if (!m_nUnlimitedDepth)
573
90
        {
574
90
            CPLError(CE_Failure, CPLE_NotSupported,
575
90
                     "Too deep XML nesting level (%d). "
576
90
                     "Set the OGR_GML_NESTING_LEVEL configuration option to "
577
90
                     "UNLIMITED to remove that limitation.",
578
90
                     m_nDepth);
579
90
            eRet = OGRERR_FAILURE;
580
90
        }
581
90
    }
582
1.00M
    return eRet;
583
1.00M
}
584
585
/************************************************************************/
586
/*                              endElement()                            */
587
/************************************************************************/
588
589
OGRErr GMLHandler::endElement()
590
934k
{
591
934k
    m_nDepth--;
592
934k
    switch (stateStack[nStackDepth])
593
934k
    {
594
0
        case STATE_TOP:
595
0
            break;
596
362k
        case STATE_DEFAULT:
597
362k
            return endElementDefault();
598
87.1k
        case STATE_FEATURE:
599
87.1k
            return endElementFeature();
600
249k
        case STATE_PROPERTY:
601
249k
            return endElementAttribute();
602
0
        case STATE_FEATUREPROPERTY:
603
0
            return endElementFeatureProperty();
604
66.6k
        case STATE_GEOMETRY:
605
66.6k
            return endElementGeometry();
606
160k
        case STATE_IGNORED_FEATURE:
607
160k
            return endElementIgnoredFeature();
608
2.44k
        case STATE_BOUNDED_BY:
609
2.44k
            return endElementBoundedBy();
610
4.15k
        case STATE_BOUNDED_BY_IN_FEATURE:
611
4.15k
            return endElementBoundedByInFeature();
612
1.47k
        case STATE_CITYGML_ATTRIBUTE:
613
1.47k
            return endElementCityGMLGenericAttr();
614
0
        default:
615
0
            break;
616
934k
    }
617
0
    return OGRERR_NONE;
618
934k
}
619
620
/************************************************************************/
621
/*                              dataHandler()                           */
622
/************************************************************************/
623
624
OGRErr GMLHandler::dataHandler(const char *data, int nLen)
625
2.17M
{
626
2.17M
    switch (stateStack[nStackDepth])
627
2.17M
    {
628
0
        case STATE_TOP:
629
82.7k
        case STATE_DEFAULT:
630
120k
        case STATE_FEATURE:
631
120k
            break;
632
1.96M
        case STATE_PROPERTY:
633
1.96M
            return dataHandlerAttribute(data, nLen);
634
0
        case STATE_FEATUREPROPERTY:
635
0
            break;
636
51.9k
        case STATE_GEOMETRY:
637
51.9k
            return dataHandlerGeometry(data, nLen);
638
22.6k
        case STATE_IGNORED_FEATURE:
639
25.9k
        case STATE_BOUNDED_BY:
640
25.9k
            break;
641
8.19k
        case STATE_BOUNDED_BY_IN_FEATURE:
642
8.19k
            return dataHandlerGeometry(data, nLen);
643
2.44k
        case STATE_CITYGML_ATTRIBUTE:
644
2.44k
            return dataHandlerAttribute(data, nLen);
645
0
        default:
646
0
            break;
647
2.17M
    }
648
146k
    return OGRERR_NONE;
649
2.17M
}
650
651
#define PUSH_STATE(val)                                                        \
652
296k
    do                                                                         \
653
296k
    {                                                                          \
654
296k
        nStackDepth++;                                                         \
655
296k
        CPLAssert(nStackDepth < STACK_SIZE);                                   \
656
296k
        stateStack[nStackDepth] = val;                                         \
657
296k
    } while (false)
658
288k
#define POP_STATE() nStackDepth--
659
660
/************************************************************************/
661
/*                       startElementBoundedBy()                        */
662
/************************************************************************/
663
664
OGRErr GMLHandler::startElementBoundedBy(const char *pszName, int /*nLenName*/,
665
                                         void *attr)
666
1.18k
{
667
1.18k
    if (m_nDepth == 2 && strcmp(pszName, "Envelope") == 0)
668
327
    {
669
327
        char *pszGlobalSRSName = GetAttributeValue(attr, "srsName");
670
327
        m_poReader->SetGlobalSRSName(pszGlobalSRSName);
671
327
        CPLFree(pszGlobalSRSName);
672
673
327
        if (m_nSRSDimensionIfMissing == 0)
674
226
        {
675
226
            char *pszGlobalSRSDimension =
676
226
                GetAttributeValue(attr, "srsDimension");
677
226
            if (pszGlobalSRSDimension)
678
13
                m_nSRSDimensionIfMissing = atoi(pszGlobalSRSDimension);
679
226
            CPLFree(pszGlobalSRSDimension);
680
226
        }
681
327
    }
682
683
1.18k
    return OGRERR_NONE;
684
1.18k
}
685
686
/************************************************************************/
687
/*                       startElementGeometry()                         */
688
/************************************************************************/
689
690
OGRErr GMLHandler::startElementGeometry(const char *pszName, int nLenName,
691
                                        void *attr)
692
77.8k
{
693
77.8k
    if (stateStack[nStackDepth] == STATE_BOUNDED_BY_IN_FEATURE &&
694
4.04k
        apsXMLNode.empty())
695
1
    {
696
1
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid <boundedBy> construct");
697
1
        return OGRERR_FAILURE;
698
1
    }
699
700
    /* Create new XML Element */
701
77.8k
    CPLXMLNode *psCurNode =
702
77.8k
        static_cast<CPLXMLNode *>(CPLCalloc(sizeof(CPLXMLNode), 1));
703
77.8k
    psCurNode->eType = CXT_Element;
704
77.8k
    psCurNode->pszValue = static_cast<char *>(CPLMalloc(nLenName + 1));
705
77.8k
    memcpy(psCurNode->pszValue, pszName, nLenName + 1);
706
707
    /* Attach element as the last child of its parent */
708
77.8k
    NodeLastChild &sNodeLastChild = apsXMLNode.back();
709
77.8k
    CPLXMLNode *psLastChildParent = sNodeLastChild.psLastChild;
710
711
77.8k
    if (psLastChildParent == nullptr)
712
45.8k
    {
713
45.8k
        CPLXMLNode *psParent = sNodeLastChild.psNode;
714
45.8k
        if (psParent)
715
16.0k
            psParent->psChild = psCurNode;
716
45.8k
    }
717
32.0k
    else
718
32.0k
    {
719
32.0k
        psLastChildParent->psNext = psCurNode;
720
32.0k
    }
721
77.8k
    sNodeLastChild.psLastChild = psCurNode;
722
723
    /* Add attributes to the element */
724
77.8k
    CPLXMLNode *psLastChildCurNode = AddAttributes(psCurNode, attr);
725
93.0k
    for (CPLXMLNode *psIter = psCurNode->psChild; psIter;
726
77.8k
         psIter = psIter->psNext)
727
15.1k
    {
728
15.1k
        if (psIter->eType == CXT_Attribute &&
729
15.1k
            strcmp(psIter->pszValue, "xlink:href") == 0 &&
730
2.87k
            psIter->psChild->pszValue && psIter->psChild->pszValue[0] == '#')
731
2.85k
        {
732
2.85k
            m_oMapElementToSubstitute[psIter->psChild->pszValue + 1] =
733
2.85k
                psCurNode;
734
2.85k
        }
735
15.1k
    }
736
737
    /* Some CityGML lack a srsDimension="3" in posList, such as in */
738
    /* http://www.citygml.org/fileadmin/count.php?f=fileadmin%2Fcitygml%2Fdocs%2FFrankfurt_Street_Setting_LOD3.zip
739
     */
740
    /* So we have to add it manually */
741
77.8k
    if (strcmp(pszName, "posList") == 0 &&
742
2.28k
        CPLGetXMLValue(psCurNode, "srsDimension", nullptr) == nullptr &&
743
821
        m_nSRSDimensionIfMissing != 0)
744
243
    {
745
243
        CPLXMLNode *psChild =
746
243
            CPLCreateXMLNode(nullptr, CXT_Attribute, "srsDimension");
747
243
        CPLCreateXMLNode(psChild, CXT_Text,
748
243
                         (m_nSRSDimensionIfMissing == 3) ? "3" : "2");
749
750
243
        if (psLastChildCurNode == nullptr)
751
243
            psCurNode->psChild = psChild;
752
0
        else
753
0
            psLastChildCurNode->psNext = psChild;
754
243
        psLastChildCurNode = psChild;
755
243
    }
756
757
    /* Push the element on the stack */
758
77.8k
    NodeLastChild sNewNodeLastChild;
759
77.8k
    sNewNodeLastChild.psNode = psCurNode;
760
77.8k
    sNewNodeLastChild.psLastChild = psLastChildCurNode;
761
77.8k
    apsXMLNode.push_back(sNewNodeLastChild);
762
763
77.8k
    if (m_pszGeometry)
764
9.67k
    {
765
9.67k
        CPLFree(m_pszGeometry);
766
9.67k
        m_pszGeometry = nullptr;
767
9.67k
        m_nGeomAlloc = 0;
768
9.67k
        m_nGeomLen = 0;
769
9.67k
    }
770
771
77.8k
    return OGRERR_NONE;
772
77.8k
}
773
774
/************************************************************************/
775
/*                    startElementCityGMLGenericAttr()                  */
776
/************************************************************************/
777
778
OGRErr GMLHandler::startElementCityGMLGenericAttr(const char *pszName,
779
                                                  int /*nLenName*/,
780
                                                  void * /*attr*/)
781
941
{
782
941
    if (strcmp(pszName, "value") == 0)
783
674
    {
784
674
        if (m_pszCurField)
785
61
        {
786
61
            CPLFree(m_pszCurField);
787
61
            m_pszCurField = nullptr;
788
61
            m_nCurFieldLen = 0;
789
61
            m_nCurFieldAlloc = 0;
790
61
        }
791
674
        m_bInCurField = true;
792
674
    }
793
794
941
    return OGRERR_NONE;
795
941
}
796
797
/************************************************************************/
798
/*                       DealWithAttributes()                           */
799
/************************************************************************/
800
801
void GMLHandler::DealWithAttributes(const char *pszName, int nLenName,
802
                                    void *attr)
803
283k
{
804
283k
    GMLReadState *poState = m_poReader->GetState();
805
283k
    GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
806
807
288k
    for (unsigned int idx = 0; true; idx++)
808
288k
    {
809
288k
        char *pszAttrKey = nullptr;
810
811
288k
        char *pszAttrVal = GetAttributeByIdx(attr, idx, &pszAttrKey);
812
288k
        if (pszAttrVal == nullptr)
813
283k
            break;
814
815
5.50k
        int nAttrIndex = 0;
816
5.50k
        const char *pszAttrKeyNoNS = strchr(pszAttrKey, ':');
817
5.50k
        if (pszAttrKeyNoNS != nullptr)
818
910
            pszAttrKeyNoNS++;
819
820
        /* If attribute is referenced by the .gfs */
821
5.50k
        if (poClass->IsSchemaLocked() &&
822
84
            ((pszAttrKeyNoNS != nullptr &&
823
32
              (nAttrIndex = m_poReader->GetAttributeElementIndex(
824
32
                   pszName, nLenName, pszAttrKeyNoNS)) != -1) ||
825
83
             ((nAttrIndex = m_poReader->GetAttributeElementIndex(
826
83
                   pszName, nLenName, pszAttrKey)) != -1)))
827
24
        {
828
24
            nAttrIndex = FindRealPropertyByCheckingConditions(nAttrIndex, attr);
829
24
            if (nAttrIndex >= 0)
830
24
            {
831
24
                m_poReader->SetFeaturePropertyDirectly(nullptr, pszAttrVal,
832
24
                                                       nAttrIndex);
833
24
                pszAttrVal = nullptr;
834
24
            }
835
24
        }
836
837
        /* Hard-coded historic cases */
838
5.48k
        else if (strcmp(pszAttrKey, "xlink:href") == 0)
839
246
        {
840
246
            if ((m_bReportHref || m_poReader->ReportAllAttributes()) &&
841
8
                m_bInCurField)
842
8
            {
843
8
                CPLFree(m_pszHref);
844
8
                m_pszHref = pszAttrVal;
845
8
                pszAttrVal = nullptr;
846
8
            }
847
238
            else if ((!poClass->IsSchemaLocked() &&
848
236
                      (m_bReportHref || m_poReader->ReportAllAttributes())) ||
849
238
                     (poClass->IsSchemaLocked() &&
850
2
                      (nAttrIndex = m_poReader->GetAttributeElementIndex(
851
2
                           (std::string(pszName) + "_href").c_str(),
852
2
                           nLenName + 5)) != -1))
853
0
            {
854
0
                poState->PushPath(pszName, nLenName);
855
0
                CPLString osPropNameHref = poState->osPath + "_href";
856
0
                poState->PopPath();
857
0
                m_poReader->SetFeaturePropertyDirectly(osPropNameHref,
858
0
                                                       pszAttrVal, nAttrIndex);
859
0
                pszAttrVal = nullptr;
860
0
            }
861
246
        }
862
5.23k
        else if (strcmp(pszAttrKey, "uom") == 0)
863
232
        {
864
232
            CPLFree(m_pszUom);
865
232
            m_pszUom = pszAttrVal;
866
232
            pszAttrVal = nullptr;
867
232
        }
868
5.00k
        else if (strcmp(pszAttrKey, "value") == 0)
869
130
        {
870
130
            CPLFree(m_pszValue);
871
130
            m_pszValue = pszAttrVal;
872
130
            pszAttrVal = nullptr;
873
130
        }
874
4.87k
        else /* Get language in 'kieli' attribute of 'teksti' element */
875
4.87k
            if (eAppSchemaType == APPSCHEMA_MTKGML && nLenName == 6 &&
876
31
                strcmp(pszName, "teksti") == 0 &&
877
7
                strcmp(pszAttrKey, "kieli") == 0)
878
7
            {
879
7
                CPLFree(m_pszKieli);
880
7
                m_pszKieli = pszAttrVal;
881
7
                pszAttrVal = nullptr;
882
7
            }
883
884
            /* Should we report all attributes ? */
885
4.86k
            else if (m_poReader->ReportAllAttributes() &&
886
0
                     !poClass->IsSchemaLocked())
887
0
            {
888
0
                poState->PushPath(pszName, nLenName);
889
0
                CPLString osPropName = poState->osPath;
890
0
                poState->PopPath();
891
892
0
                m_poReader->SetFeaturePropertyDirectly(
893
0
                    CPLSPrintf("%s@%s", osPropName.c_str(),
894
0
                               pszAttrKeyNoNS ? pszAttrKeyNoNS : pszAttrKey),
895
0
                    pszAttrVal, -1);
896
0
                pszAttrVal = nullptr;
897
0
            }
898
899
5.50k
        CPLFree(pszAttrKey);
900
5.50k
        CPLFree(pszAttrVal);
901
5.50k
    }
902
903
#if 0
904
    if( poClass->IsSchemaLocked() )
905
    {
906
        poState->PushPath( pszName, nLenName );
907
        CPLString osPath = poState->osPath;
908
        poState->PopPath();
909
        /* Find fields that match an attribute that is missing */
910
        for(int i=0; i < poClass->GetPropertyCount(); i++ )
911
        {
912
            GMLPropertyDefn* poProp = poClass->GetProperty(i);
913
            const char* pszSrcElement = poProp->GetSrcElement();
914
            if( poProp->GetType() == OFTStringList &&
915
                poProp->GetSrcElementLen() > osPath.size() &&
916
                strncmp(pszSrcElement, osPath, osPath.size()) == 0 &&
917
                pszSrcElement[osPath.size()] == '@' )
918
            {
919
                char* pszAttrVal = GetAttributeValue(attr, pszSrcElement + osPath.size() + 1);
920
                if( pszAttrVal == NULL )
921
                {
922
                    const char* pszCond = poProp->GetCondition();
923
                    if( pszCond == NULL || IsConditionMatched(pszCond, attr) )
924
                    {
925
                        m_poReader->SetFeaturePropertyDirectly( NULL, CPLStrdup(""), i );
926
                    }
927
                }
928
                else
929
                    CPLFree(pszAttrVal);
930
            }
931
        }
932
    }
933
#endif
934
283k
}
935
936
/************************************************************************/
937
/*                        IsConditionMatched()                          */
938
/************************************************************************/
939
940
/* FIXME! 'and' / 'or' operators are evaluated left to right, without */
941
/* and precedence rules between them ! */
942
943
bool GMLHandler::IsConditionMatched(const char *pszCondition, void *attr)
944
0
{
945
0
    if (pszCondition == nullptr)
946
0
        return true;
947
948
0
    bool bSyntaxError = false;
949
0
    CPLString osCondAttr, osCondVal;
950
0
    const char *pszIter = pszCondition;
951
0
    bool bOpEqual = true;
952
0
    while (*pszIter == ' ')
953
0
        pszIter++;
954
0
    if (*pszIter != '@')
955
0
        bSyntaxError = true;
956
0
    else
957
0
    {
958
0
        pszIter++;
959
0
        while (*pszIter != '\0' && *pszIter != ' ' && *pszIter != '!' &&
960
0
               *pszIter != '=')
961
0
        {
962
0
            osCondAttr += *pszIter;
963
0
            pszIter++;
964
0
        }
965
0
        while (*pszIter == ' ')
966
0
            pszIter++;
967
968
0
        if (*pszIter == '!')
969
0
        {
970
0
            bOpEqual = false;
971
0
            pszIter++;
972
0
        }
973
974
0
        if (*pszIter != '=')
975
0
            bSyntaxError = true;
976
0
        else
977
0
        {
978
0
            pszIter++;
979
0
            while (*pszIter == ' ')
980
0
                pszIter++;
981
0
            if (*pszIter != '\'')
982
0
                bSyntaxError = true;
983
0
            else
984
0
            {
985
0
                pszIter++;
986
0
                while (*pszIter != '\0' && *pszIter != '\'')
987
0
                {
988
0
                    osCondVal += *pszIter;
989
0
                    pszIter++;
990
0
                }
991
0
                if (*pszIter != '\'')
992
0
                    bSyntaxError = true;
993
0
                else
994
0
                {
995
0
                    pszIter++;
996
0
                    while (*pszIter == ' ')
997
0
                        pszIter++;
998
0
                }
999
0
            }
1000
0
        }
1001
0
    }
1002
1003
0
    if (bSyntaxError)
1004
0
    {
1005
0
        CPLError(CE_Failure, CPLE_NotSupported,
1006
0
                 "Invalid condition : %s. Must be of the form "
1007
0
                 "@attrname[!]='attrvalue' [and|or other_cond]*. "
1008
0
                 "'and' and 'or' operators cannot be mixed",
1009
0
                 pszCondition);
1010
0
        return false;
1011
0
    }
1012
1013
0
    char *pszVal = GetAttributeValue(attr, osCondAttr);
1014
0
    if (pszVal == nullptr)
1015
0
        pszVal = CPLStrdup("");
1016
0
    const bool bCondMet = (bOpEqual && strcmp(pszVal, osCondVal) == 0) ||
1017
0
                          (!bOpEqual && strcmp(pszVal, osCondVal) != 0);
1018
0
    CPLFree(pszVal);
1019
0
    if (*pszIter == '\0')
1020
0
        return bCondMet;
1021
1022
0
    if (STARTS_WITH(pszIter, "and"))
1023
0
    {
1024
0
        pszIter += 3;
1025
0
        if (!bCondMet)
1026
0
            return false;
1027
0
        return IsConditionMatched(pszIter, attr);
1028
0
    }
1029
1030
0
    if (STARTS_WITH(pszIter, "or"))
1031
0
    {
1032
0
        pszIter += 2;
1033
0
        if (bCondMet)
1034
0
            return true;
1035
0
        return IsConditionMatched(pszIter, attr);
1036
0
    }
1037
1038
0
    CPLError(
1039
0
        CE_Failure, CPLE_NotSupported,
1040
0
        "Invalid condition : %s. Must be of the form @attrname[!]='attrvalue' "
1041
0
        "[and|or other_cond]*. 'and' and 'or' operators cannot be mixed",
1042
0
        pszCondition);
1043
0
    return false;
1044
0
}
1045
1046
/************************************************************************/
1047
/*                FindRealPropertyByCheckingConditions()                */
1048
/************************************************************************/
1049
1050
int GMLHandler::FindRealPropertyByCheckingConditions(int nIdx, void *attr)
1051
101
{
1052
101
    GMLReadState *poState = m_poReader->GetState();
1053
101
    GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
1054
1055
101
    GMLPropertyDefn *poProp = poClass->GetProperty(nIdx);
1056
101
    const char *pszCond = poProp->GetCondition();
1057
101
    if (pszCond != nullptr && !IsConditionMatched(pszCond, attr))
1058
0
    {
1059
        /* try other attributes with same source element, but with different */
1060
        /* condition */
1061
0
        const char *pszSrcElement = poProp->GetSrcElement();
1062
0
        nIdx = -1;
1063
0
        for (int i = m_nAttributeIndex + 1; i < poClass->GetPropertyCount();
1064
0
             i++)
1065
0
        {
1066
0
            poProp = poClass->GetProperty(i);
1067
0
            if (strcmp(poProp->GetSrcElement(), pszSrcElement) == 0)
1068
0
            {
1069
0
                pszCond = poProp->GetCondition();
1070
0
                if (IsConditionMatched(pszCond, attr))
1071
0
                {
1072
0
                    nIdx = i;
1073
0
                    break;
1074
0
                }
1075
0
            }
1076
0
        }
1077
0
    }
1078
101
    return nIdx;
1079
101
}
1080
1081
/************************************************************************/
1082
/*                      startElementFeatureAttribute()                  */
1083
/************************************************************************/
1084
1085
OGRErr GMLHandler::startElementFeatureAttribute(const char *pszName,
1086
                                                int nLenName, void *attr)
1087
316k
{
1088
1089
316k
    GMLReadState *poState = m_poReader->GetState();
1090
1091
316k
    if (m_bInCurField && m_nAttributeIndex >= 0 &&
1092
62.1k
        std::string_view(pszName, nLenName) == "timePosition")
1093
0
    {
1094
0
        poState->PushPath(pszName, nLenName);
1095
0
        return OGRERR_NONE;
1096
0
    }
1097
1098
    /* Reset flag */
1099
316k
    m_bInCurField = false;
1100
1101
    /* -------------------------------------------------------------------- */
1102
    /*      If we are collecting geometry, or if we determine this is a     */
1103
    /*      geometry element then append to the geometry info.              */
1104
    /* -------------------------------------------------------------------- */
1105
316k
    if (IsGeometryElement(pszName))
1106
29.6k
    {
1107
29.6k
        bool bReadGeometry;
1108
1109
        /* If the <GeometryElementPath> is defined in the .gfs, use it */
1110
        /* to read the appropriate geometry element */
1111
29.6k
        GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
1112
29.6k
        m_nGeometryPropertyIndex = 0;
1113
29.6k
        if (poClass->IsSchemaLocked() &&
1114
30
            poClass->GetGeometryPropertyCount() == 0)
1115
0
        {
1116
0
            bReadGeometry = false;
1117
0
        }
1118
29.6k
        else if (poClass->IsSchemaLocked() &&
1119
30
                 poClass->GetGeometryPropertyCount() == 1 &&
1120
0
                 poClass->GetGeometryProperty(0)->GetSrcElement()[0] == '\0')
1121
0
        {
1122
0
            bReadGeometry = true;
1123
0
        }
1124
29.6k
        else if (poClass->IsSchemaLocked() &&
1125
30
                 poClass->GetGeometryPropertyCount() > 0)
1126
30
        {
1127
30
            m_nGeometryPropertyIndex =
1128
30
                poClass->GetGeometryPropertyIndexBySrcElement(
1129
30
                    poState->osPath.c_str());
1130
30
            bReadGeometry = (m_nGeometryPropertyIndex >= 0);
1131
30
        }
1132
29.5k
        else if (m_poReader->FetchAllGeometries())
1133
0
        {
1134
0
            bReadGeometry = true;
1135
0
        }
1136
29.5k
        else if (!poClass->IsSchemaLocked() && m_poReader->IsWFSJointLayer())
1137
2.20k
        {
1138
2.20k
            m_nGeometryPropertyIndex =
1139
2.20k
                poClass->GetGeometryPropertyIndexBySrcElement(
1140
2.20k
                    poState->osPath.c_str());
1141
2.20k
            if (m_nGeometryPropertyIndex < 0)
1142
1.38k
            {
1143
1.38k
                const char *pszElement = poState->osPath.c_str();
1144
1.38k
                CPLString osFieldName;
1145
                /* Strip member| prefix. Should always be true normally */
1146
1.38k
                if (STARTS_WITH(pszElement, "member|"))
1147
264
                    osFieldName = pszElement + strlen("member|");
1148
1149
                /* Replace layer|property by layer_property */
1150
1.38k
                size_t iPos = osFieldName.find('|');
1151
1.38k
                if (iPos != std::string::npos)
1152
254
                    osFieldName[iPos] = '.';
1153
1154
1.38k
                poClass->AddGeometryProperty(new GMLGeometryPropertyDefn(
1155
1.38k
                    osFieldName, poState->osPath.c_str(), wkbUnknown, -1,
1156
1.38k
                    true));
1157
1.38k
                m_nGeometryPropertyIndex = poClass->GetGeometryPropertyCount();
1158
1.38k
            }
1159
2.20k
            bReadGeometry = true;
1160
2.20k
        }
1161
27.3k
        else
1162
27.3k
        {
1163
            /* AIXM special case: for RouteSegment, we only want to read Curve
1164
             * geometries */
1165
            /* not 'start' and 'end' geometries */
1166
27.3k
            if (eAppSchemaType == APPSCHEMA_AIXM &&
1167
22
                strcmp(poState->m_poFeature->GetClass()->GetName(),
1168
22
                       "RouteSegment") == 0)
1169
0
                bReadGeometry = strcmp(pszName, "Curve") == 0;
1170
1171
            // AIXM special case: we want to read both horizontalProjection_location and
1172
            // horizontalProjection_linearExtent
1173
27.3k
            else if (eAppSchemaType == APPSCHEMA_AIXM &&
1174
22
                     STARTS_WITH(poState->m_poFeature->GetClass()->GetName(),
1175
22
                                 "horizontalProjection_") == 0)
1176
22
                bReadGeometry =
1177
22
                    STARTS_WITH(pszName, "horizontalProjection_") == 0;
1178
1179
            /* For Inspire objects : the "main" geometry is in a <geometry>
1180
             * element */
1181
27.3k
            else if (m_bAlreadyFoundGeometry)
1182
7
                bReadGeometry = false;
1183
27.3k
            else if (strcmp(poState->osPath.c_str(), "geometry") == 0)
1184
24
            {
1185
24
                m_bAlreadyFoundGeometry = true;
1186
24
                bReadGeometry = true;
1187
24
                m_nGeometryPropertyIndex =
1188
24
                    poClass->GetGeometryPropertyIndexBySrcElement(
1189
24
                        poState->osPath.c_str());
1190
24
                if (m_nGeometryPropertyIndex < 0)
1191
18
                {
1192
18
                    poClass->AddGeometryProperty(new GMLGeometryPropertyDefn(
1193
18
                        "geometry", poState->osPath.c_str(), wkbUnknown, -1,
1194
18
                        true));
1195
18
                    m_nGeometryPropertyIndex =
1196
18
                        poClass->GetGeometryPropertyCount();
1197
18
                }
1198
24
            }
1199
27.3k
            else
1200
27.3k
            {
1201
27.3k
                if (!poClass->IsSchemaLocked() &&
1202
27.3k
                    poClass->IsConsistentSingleGeomElemPath())
1203
15.3k
                {
1204
15.3k
                    const std::string &osGeomElemPath =
1205
15.3k
                        poClass->GetSingleGeomElemPath();
1206
15.3k
                    if (osGeomElemPath.empty())
1207
9.95k
                    {
1208
9.95k
                        poClass->SetSingleGeomElemPath(poState->osPath);
1209
9.95k
                    }
1210
5.38k
                    else if (poState->osPath != osGeomElemPath)
1211
531
                    {
1212
531
                        poClass->SetConsistentSingleGeomElemPath(false);
1213
531
                        poClass->SetSingleGeomElemPath(std::string());
1214
531
                    }
1215
15.3k
                }
1216
27.3k
                bReadGeometry = true;
1217
27.3k
            }
1218
27.3k
        }
1219
29.6k
        if (bReadGeometry)
1220
29.5k
        {
1221
29.5k
            m_nGeometryDepth = m_nDepth;
1222
1223
29.5k
            CPLAssert(apsXMLNode.empty());
1224
1225
29.5k
            NodeLastChild sNodeLastChild;
1226
29.5k
            sNodeLastChild.psNode = nullptr;
1227
29.5k
            sNodeLastChild.psLastChild = nullptr;
1228
29.5k
            apsXMLNode.push_back(sNodeLastChild);
1229
1230
29.5k
            PUSH_STATE(STATE_GEOMETRY);
1231
1232
29.5k
            return startElementGeometry(pszName, nLenName, attr);
1233
29.5k
        }
1234
29.6k
    }
1235
286k
    else if (
1236
286k
        (nLenName == 9 || nLenName == 8) &&
1237
9.56k
        (strcmp(pszName, "boundedBy") == 0 ||
1238
8.84k
         strcmp(pszName, "boundary") == 0) &&
1239
        // We ignore the UseBBOX() flag for CityGML, since CityGML
1240
        // has elements like bldg:boundedBy or 'boundary', which are not a simple
1241
        // rectangular bbox. This is needed to read properly
1242
        // autotest/ogr/data/gml/citygml_lod2_713_5322.xml
1243
        // (this is a workaround of not being namespace aware)
1244
718
        (eAppSchemaType == APPSCHEMA_CITYGML || m_poReader->UseBBOX()))
1245
222
    {
1246
222
        m_inBoundedByDepth = m_nDepth;
1247
1248
222
        CPLAssert(apsXMLNode.empty());
1249
1250
222
        NodeLastChild sNodeLastChild;
1251
222
        sNodeLastChild.psNode = nullptr;
1252
222
        sNodeLastChild.psLastChild = nullptr;
1253
222
        apsXMLNode.push_back(sNodeLastChild);
1254
1255
222
        PUSH_STATE(STATE_BOUNDED_BY_IN_FEATURE);
1256
1257
222
        return OGRERR_NONE;
1258
222
    }
1259
1260
    /* -------------------------------------------------------------------- */
1261
    /*      Is it a CityGML generic attribute ?                             */
1262
    /* -------------------------------------------------------------------- */
1263
286k
    else if (eAppSchemaType == APPSCHEMA_CITYGML &&
1264
1.64k
             m_poReader->IsCityGMLGenericAttributeElement(pszName, attr))
1265
631
    {
1266
631
        CPLFree(m_pszCityGMLGenericAttrName);
1267
631
        m_pszCityGMLGenericAttrName = GetAttributeValue(attr, "name");
1268
631
        m_inCityGMLGenericAttrDepth = m_nDepth;
1269
1270
631
        PUSH_STATE(STATE_CITYGML_ATTRIBUTE);
1271
1272
631
        return OGRERR_NONE;
1273
631
    }
1274
1275
285k
    else if (m_poReader->IsWFSJointLayer() && m_nDepth == m_nDepthFeature + 1)
1276
895
    {
1277
895
    }
1278
1279
284k
    else if (m_poReader->IsWFSJointLayer() && m_nDepth == m_nDepthFeature + 2)
1280
1.31k
    {
1281
1.31k
        const char *pszFID = GetFID(attr);
1282
1.31k
        if (pszFID)
1283
4
        {
1284
4
            poState->PushPath(pszName, nLenName);
1285
4
            CPLString osPropPath = poState->osPath + "@id";
1286
4
            poState->PopPath();
1287
4
            m_poReader->SetFeaturePropertyDirectly(osPropPath,
1288
4
                                                   CPLStrdup(pszFID), -1);
1289
4
        }
1290
1.31k
    }
1291
1292
    /* -------------------------------------------------------------------- */
1293
    /*      If it is (or at least potentially is) a simple attribute,       */
1294
    /*      then start collecting it.                                       */
1295
    /* -------------------------------------------------------------------- */
1296
283k
    else if ((m_nAttributeIndex = m_poReader->GetAttributeElementIndex(
1297
283k
                  pszName, nLenName)) != -1)
1298
283k
    {
1299
283k
        GMLFeatureClass *poClass = poState->m_poFeature->GetClass();
1300
283k
        if (poClass->IsSchemaLocked() &&
1301
77
            (poClass->GetProperty(m_nAttributeIndex)->GetType() ==
1302
77
                 GMLPT_FeatureProperty ||
1303
77
             poClass->GetProperty(m_nAttributeIndex)->GetType() ==
1304
77
                 GMLPT_FeaturePropertyList))
1305
0
        {
1306
0
            m_nAttributeDepth = m_nDepth;
1307
0
            PUSH_STATE(STATE_FEATUREPROPERTY);
1308
0
        }
1309
283k
        else
1310
283k
        {
1311
            /* Is this a property with a condition on an attribute value ? */
1312
283k
            if (poClass->IsSchemaLocked())
1313
77
            {
1314
77
                m_nAttributeIndex = FindRealPropertyByCheckingConditions(
1315
77
                    m_nAttributeIndex, attr);
1316
77
            }
1317
1318
283k
            if (m_nAttributeIndex >= 0)
1319
283k
            {
1320
283k
                if (m_pszCurField)
1321
36.4k
                {
1322
36.4k
                    CPLFree(m_pszCurField);
1323
36.4k
                    m_pszCurField = nullptr;
1324
36.4k
                    m_nCurFieldLen = 0;
1325
36.4k
                    m_nCurFieldAlloc = 0;
1326
36.4k
                }
1327
283k
                m_bInCurField = true;
1328
1329
283k
                char *pszXSINil = GetAttributeValue(attr, "xsi:nil");
1330
283k
                if (pszXSINil)
1331
109
                {
1332
109
                    if (EQUAL(pszXSINil, "true"))
1333
109
                        m_poReader->SetFeaturePropertyDirectly(
1334
109
                            pszName, CPLStrdup(OGR_GML_NULL), -1);
1335
109
                    CPLFree(pszXSINil);
1336
109
                }
1337
283k
                else
1338
283k
                {
1339
283k
                    DealWithAttributes(pszName, nLenName, attr);
1340
283k
                }
1341
1342
283k
                if (stateStack[nStackDepth] != STATE_PROPERTY)
1343
68.4k
                {
1344
68.4k
                    m_nAttributeDepth = m_nDepth;
1345
68.4k
                    PUSH_STATE(STATE_PROPERTY);
1346
68.4k
                }
1347
283k
            }
1348
            /*else
1349
            {
1350
                DealWithAttributes(pszName, nLenName, attr);
1351
            }*/
1352
283k
        }
1353
283k
    }
1354
233
    else
1355
233
    {
1356
233
        DealWithAttributes(pszName, nLenName, attr);
1357
233
    }
1358
1359
285k
    poState->PushPath(pszName, nLenName);
1360
1361
285k
    return OGRERR_NONE;
1362
316k
}
1363
1364
/************************************************************************/
1365
/*                         startElementTop()                            */
1366
/************************************************************************/
1367
1368
OGRErr GMLHandler::startElementTop(const char *pszName, int /*nLenName*/,
1369
                                   void *attr)
1370
5.73k
{
1371
5.73k
    if (strcmp(pszName, "CityModel") == 0)
1372
103
    {
1373
103
        eAppSchemaType = APPSCHEMA_CITYGML;
1374
        // Default to 3D geometries for CityGML (#6989)
1375
103
        if (m_nSRSDimensionIfMissing == 0)
1376
103
            m_nSRSDimensionIfMissing = 3;
1377
103
    }
1378
5.63k
    else if (strcmp(pszName, "AIXMBasicMessage") == 0)
1379
11
    {
1380
11
        eAppSchemaType = APPSCHEMA_AIXM;
1381
11
        m_bReportHref = true;
1382
11
    }
1383
5.62k
    else if (strcmp(pszName, "Maastotiedot") == 0)
1384
635
    {
1385
635
        eAppSchemaType = APPSCHEMA_MTKGML;
1386
1387
635
        char *pszSRSName = GetAttributeValue(attr, "srsName");
1388
635
        m_poReader->SetGlobalSRSName(pszSRSName);
1389
635
        CPLFree(pszSRSName);
1390
1391
635
        m_bReportHref = true;
1392
1393
        /* the schemas of MTKGML don't have (string) width, so don't set it */
1394
635
        m_poReader->SetWidthFlag(false);
1395
635
    }
1396
1397
5.73k
    stateStack[0] = STATE_DEFAULT;
1398
1399
5.73k
    return OGRERR_NONE;
1400
5.73k
}
1401
1402
/************************************************************************/
1403
/*                        startElementDefault()                         */
1404
/************************************************************************/
1405
1406
OGRErr GMLHandler::startElementDefault(const char *pszName, int nLenName,
1407
                                       void *attr)
1408
1409
576k
{
1410
    /* -------------------------------------------------------------------- */
1411
    /*      Is it a feature?  If so push a whole new state, and return.     */
1412
    /* -------------------------------------------------------------------- */
1413
576k
    int nClassIndex;
1414
576k
    const char *pszFilteredClassName = nullptr;
1415
1416
576k
    if (nLenName == 9 && strcmp(pszName, "boundedBy") == 0)
1417
1.37k
    {
1418
1.37k
        m_inBoundedByDepth = m_nDepth;
1419
1420
1.37k
        PUSH_STATE(STATE_BOUNDED_BY);
1421
1422
1.37k
        return OGRERR_NONE;
1423
1.37k
    }
1424
1425
575k
    else if (m_poReader->ShouldLookForClassAtAnyLevel() &&
1426
0
             (pszFilteredClassName = m_poReader->GetFilteredClassName()) !=
1427
0
                 nullptr)
1428
0
    {
1429
0
        if (strcmp(pszName, pszFilteredClassName) == 0)
1430
0
        {
1431
0
            m_poReader->PushFeature(pszName, GetFID(attr),
1432
0
                                    m_poReader->GetFilteredClassIndex());
1433
1434
0
            m_nDepthFeature = m_nDepth;
1435
1436
0
            PUSH_STATE(STATE_FEATURE);
1437
1438
0
            return OGRERR_NONE;
1439
0
        }
1440
0
    }
1441
1442
    /* WFS 2.0 GetFeature documents have a wfs:FeatureCollection */
1443
    /* as a wfs:member of the top wfs:FeatureCollection. We don't want this */
1444
    /* wfs:FeatureCollection to be recognized as a feature */
1445
575k
    else if ((!(nLenName == static_cast<int>(strlen("FeatureCollection")) &&
1446
70.3k
                strcmp(pszName, "FeatureCollection") == 0)) &&
1447
570k
             (nClassIndex = m_poReader->GetFeatureElementIndex(
1448
570k
                  pszName, nLenName, eAppSchemaType)) != -1)
1449
195k
    {
1450
195k
        m_bAlreadyFoundGeometry = false;
1451
1452
195k
        pszFilteredClassName = m_poReader->GetFilteredClassName();
1453
195k
        if (pszFilteredClassName != nullptr &&
1454
118k
            strcmp(pszName, pszFilteredClassName) != 0)
1455
107k
        {
1456
107k
            m_nDepthFeature = m_nDepth;
1457
1458
107k
            PUSH_STATE(STATE_IGNORED_FEATURE);
1459
1460
107k
            return OGRERR_NONE;
1461
107k
        }
1462
88.8k
        else
1463
88.8k
        {
1464
88.8k
            if (eAppSchemaType == APPSCHEMA_MTKGML)
1465
3.28k
            {
1466
3.28k
                m_poReader->PushFeature(pszName, nullptr, nClassIndex);
1467
1468
3.28k
                char *pszGID = GetAttributeValue(attr, "gid");
1469
3.28k
                if (pszGID)
1470
4
                    m_poReader->SetFeaturePropertyDirectly("gid", pszGID, -1,
1471
4
                                                           GMLPT_String);
1472
3.28k
            }
1473
85.5k
            else
1474
85.5k
                m_poReader->PushFeature(pszName, GetFID(attr), nClassIndex);
1475
1476
88.8k
            m_nDepthFeature = m_nDepth;
1477
1478
88.8k
            PUSH_STATE(STATE_FEATURE);
1479
1480
88.8k
            return OGRERR_NONE;
1481
88.8k
        }
1482
195k
    }
1483
1484
    /* -------------------------------------------------------------------- */
1485
    /*      Push the element onto the current state's path.                 */
1486
    /* -------------------------------------------------------------------- */
1487
379k
    m_poReader->GetState()->PushPath(pszName, nLenName);
1488
1489
379k
    return OGRERR_NONE;
1490
576k
}
1491
1492
/************************************************************************/
1493
/*                      endElementIgnoredFeature()                      */
1494
/************************************************************************/
1495
1496
OGRErr GMLHandler::endElementIgnoredFeature()
1497
1498
160k
{
1499
160k
    if (m_nDepth == m_nDepthFeature)
1500
106k
    {
1501
106k
        POP_STATE();
1502
106k
    }
1503
160k
    return OGRERR_NONE;
1504
160k
}
1505
1506
/************************************************************************/
1507
/*                         endElementBoundedBy()                        */
1508
/************************************************************************/
1509
OGRErr GMLHandler::endElementBoundedBy()
1510
1511
2.44k
{
1512
2.44k
    if (m_inBoundedByDepth == m_nDepth)
1513
1.34k
    {
1514
1.34k
        POP_STATE();
1515
1.34k
    }
1516
1517
2.44k
    return OGRERR_NONE;
1518
2.44k
}
1519
1520
/************************************************************************/
1521
/*                     endElementBoundedByInFeature()                   */
1522
/************************************************************************/
1523
OGRErr GMLHandler::endElementBoundedByInFeature()
1524
1525
4.15k
{
1526
4.15k
    if (m_nDepth > m_inBoundedByDepth)
1527
3.96k
    {
1528
3.96k
        if (m_nDepth == m_inBoundedByDepth + 1)
1529
192
        {
1530
192
            m_nGeometryDepth = m_nDepth;
1531
192
        }
1532
3.96k
        return endElementGeometry();
1533
3.96k
    }
1534
189
    else
1535
189
    {
1536
189
        POP_STATE();
1537
189
        if (apsXMLNode.size() >= 2 && apsXMLNode[1].psNode != nullptr)
1538
0
            CPLDestroyXMLNode(apsXMLNode[1].psNode);
1539
189
        apsXMLNode.clear();
1540
189
        return OGRERR_NONE;
1541
189
    }
1542
4.15k
}
1543
1544
/************************************************************************/
1545
/*                      ParseAIXMElevationProperties()                  */
1546
/************************************************************************/
1547
1548
void GMLHandler::ParseAIXMElevationProperties(const CPLXMLNode *psGML)
1549
19
{
1550
19
    if (const char *pszElevation = CPLGetXMLValue(psGML, "elevation", nullptr))
1551
10
    {
1552
10
        m_poReader->SetFeaturePropertyDirectly("elevation",
1553
10
                                               CPLStrdup(pszElevation), -1);
1554
10
        const char *pszElevationUnit =
1555
10
            CPLGetXMLValue(psGML, "elevation.uom", nullptr);
1556
10
        if (pszElevationUnit)
1557
7
        {
1558
7
            m_poReader->SetFeaturePropertyDirectly(
1559
7
                "elevation_uom", CPLStrdup(pszElevationUnit), -1);
1560
7
        }
1561
10
    }
1562
1563
19
    if (const char *pszGeoidUndulation =
1564
19
            CPLGetXMLValue(psGML, "geoidUndulation", nullptr))
1565
0
    {
1566
0
        m_poReader->SetFeaturePropertyDirectly(
1567
0
            "geoidUndulation", CPLStrdup(pszGeoidUndulation), -1);
1568
0
        const char *pszGeoidUndulationUnit =
1569
0
            CPLGetXMLValue(psGML, "geoidUndulation.uom", nullptr);
1570
0
        if (pszGeoidUndulationUnit)
1571
0
        {
1572
0
            m_poReader->SetFeaturePropertyDirectly(
1573
0
                "geoidUndulation_uom", CPLStrdup(pszGeoidUndulationUnit), -1);
1574
0
        }
1575
0
    }
1576
1577
19
    if (const char *pszVerticalDatum =
1578
19
            CPLGetXMLValue(psGML, "verticalDatum", nullptr))
1579
3
    {
1580
3
        m_poReader->SetFeaturePropertyDirectly("verticalDatum",
1581
3
                                               CPLStrdup(pszVerticalDatum), -1);
1582
3
    }
1583
1584
19
    if (const char *pszVerticalAccuracy =
1585
19
            CPLGetXMLValue(psGML, "verticalAccuracy", nullptr))
1586
7
    {
1587
7
        m_poReader->SetFeaturePropertyDirectly(
1588
7
            "verticalAccuracy", CPLStrdup(pszVerticalAccuracy), -1);
1589
7
        const char *pszVerticalAccuracyUnit =
1590
7
            CPLGetXMLValue(psGML, "verticalAccuracy.uom", nullptr);
1591
7
        if (pszVerticalAccuracyUnit)
1592
3
        {
1593
3
            m_poReader->SetFeaturePropertyDirectly(
1594
3
                "verticalAccuracy_uom", CPLStrdup(pszVerticalAccuracyUnit), -1);
1595
3
        }
1596
7
    }
1597
19
}
1598
1599
/************************************************************************/
1600
/*                       ParseAIXMElevationPoint()                      */
1601
/************************************************************************/
1602
1603
CPLXMLNode *GMLHandler::ParseAIXMElevationPoint(CPLXMLNode *psGML)
1604
4
{
1605
4
    ParseAIXMElevationProperties(psGML);
1606
1607
4
    const char *pszPos = CPLGetXMLValue(psGML, "pos", nullptr);
1608
4
    const char *pszCoordinates = CPLGetXMLValue(psGML, "coordinates", nullptr);
1609
4
    if (pszPos != nullptr || pszCoordinates != nullptr)
1610
4
    {
1611
4
        CPLFree(psGML->pszValue);
1612
4
        psGML->pszValue = CPLStrdup("gml:Point");
1613
4
    }
1614
0
    else
1615
0
    {
1616
0
        CPLDestroyXMLNode(psGML);
1617
0
        psGML = nullptr;
1618
0
    }
1619
1620
4
    return psGML;
1621
4
}
1622
1623
/************************************************************************/
1624
/*                         endElementGeometry()                         */
1625
/************************************************************************/
1626
OGRErr GMLHandler::endElementGeometry()
1627
1628
70.5k
{
1629
70.5k
    if (m_nGeomLen)
1630
9.26k
    {
1631
9.26k
        CPLXMLNode *psNode =
1632
9.26k
            static_cast<CPLXMLNode *>(CPLCalloc(sizeof(CPLXMLNode), 1));
1633
9.26k
        psNode->eType = CXT_Text;
1634
9.26k
        psNode->pszValue = m_pszGeometry;
1635
1636
9.26k
        NodeLastChild &sNodeLastChild = apsXMLNode.back();
1637
9.26k
        CPLXMLNode *psLastChildParent = sNodeLastChild.psLastChild;
1638
9.26k
        if (psLastChildParent == nullptr)
1639
6.21k
        {
1640
6.21k
            CPLXMLNode *psParent = sNodeLastChild.psNode;
1641
6.21k
            if (psParent)
1642
6.21k
                psParent->psChild = psNode;
1643
6.21k
        }
1644
3.05k
        else
1645
3.05k
            psLastChildParent->psNext = psNode;
1646
9.26k
        sNodeLastChild.psLastChild = psNode;
1647
1648
9.26k
        m_pszGeometry = nullptr;
1649
9.26k
        m_nGeomAlloc = 0;
1650
9.26k
        m_nGeomLen = 0;
1651
9.26k
    }
1652
1653
70.5k
    CPLXMLNode *psThisNode = apsXMLNode.back().psNode;
1654
70.5k
    CPLXMLNode *psThisNodeChild = psThisNode->psChild;
1655
70.5k
    if (!m_oMapElementToSubstitute.empty() && psThisNodeChild &&
1656
17.6k
        psThisNodeChild->eType == CXT_Attribute &&
1657
9.48k
        strcmp(psThisNodeChild->pszValue, "gml:id") == 0 &&
1658
2.53k
        psThisNodeChild->psChild->pszValue)
1659
2.53k
    {
1660
2.53k
        auto oIter =
1661
2.53k
            m_oMapElementToSubstitute.find(psThisNodeChild->psChild->pszValue);
1662
2.53k
        if (oIter != m_oMapElementToSubstitute.end())
1663
192
        {
1664
192
            auto psLastChild = oIter->second->psChild;
1665
192
            if (psLastChild)
1666
192
            {
1667
                // CPLDebug("GML", "Substitution of xlink:href=\"#%s\" with actual content", psThisNodeChild->psChild->pszValue);
1668
192
                CPLXMLNode *psAfter = psThisNode->psNext;
1669
192
                psThisNode->psNext = nullptr;
1670
                // We can patch oIter->second as it stored as it in the current
1671
                // GMLFeature.
1672
                // Of course that would no longer be the case in case of
1673
                // cross-references between different GMLFeature, hence we clear
1674
                // m_oMapElementToSubstitute at the end of the current feature.
1675
204
                while (psLastChild->psNext)
1676
12
                    psLastChild = psLastChild->psNext;
1677
192
                if (psLastChild == psThisNode)
1678
0
                {
1679
                    /* Can happen in situations like:
1680
                     <foo xlink:href="#X">
1681
                        <bar gml:id="X"/>
1682
                     </foo>
1683
                     Do not attempt substitution as that would cause a memory
1684
                     leak.
1685
                    */
1686
0
                }
1687
192
                else
1688
192
                {
1689
192
                    psLastChild->psNext = CPLCloneXMLTree(psThisNode);
1690
192
                }
1691
192
                psThisNode->psNext = psAfter;
1692
192
            }
1693
192
        }
1694
2.53k
    }
1695
1696
70.5k
    if (m_nDepth == m_nGeometryDepth)
1697
29.2k
    {
1698
29.2k
        m_nGeometryDepth = 0;
1699
1700
29.2k
        CPLAssert(apsXMLNode.size() == 2);
1701
29.2k
        CPLXMLNode *psInterestNode = apsXMLNode.back().psNode;
1702
1703
        /*char* pszXML = CPLSerializeXMLTree(psInterestNode);
1704
        CPLDebug("GML", "geometry = %s", pszXML);
1705
        CPLFree(pszXML);*/
1706
1707
29.2k
        apsXMLNode.pop_back();
1708
1709
        /* AIXM ElevatedPoint. We want to parse this */
1710
        /* a bit specially because ElevatedPoint is aixm: stuff and */
1711
        /* the srsDimension of the <gml:pos> can be set to true although */
1712
        /* they are only 2 coordinates in practice */
1713
29.2k
        if (eAppSchemaType == APPSCHEMA_AIXM && psInterestNode != nullptr &&
1714
19
            strcmp(psInterestNode->pszValue, "ElevatedPoint") == 0)
1715
4
        {
1716
4
            psInterestNode = ParseAIXMElevationPoint(psInterestNode);
1717
4
        }
1718
29.2k
        else if (eAppSchemaType == APPSCHEMA_AIXM &&
1719
15
                 psInterestNode != nullptr &&
1720
15
                 (strcmp(psInterestNode->pszValue, "ElevatedCurve") == 0 ||
1721
0
                  strcmp(psInterestNode->pszValue, "ElevateSurface") == 0))
1722
15
        {
1723
15
            ParseAIXMElevationProperties(psInterestNode);
1724
15
        }
1725
29.1k
        else if (eAppSchemaType == APPSCHEMA_MTKGML &&
1726
2.36k
                 psInterestNode != nullptr)
1727
2.36k
        {
1728
2.36k
            if (strcmp(psInterestNode->pszValue, "Murtoviiva") == 0)
1729
0
            {
1730
0
                CPLFree(psInterestNode->pszValue);
1731
0
                psInterestNode->pszValue = CPLStrdup("gml:LineString");
1732
0
            }
1733
2.36k
            else if (strcmp(psInterestNode->pszValue, "Alue") == 0)
1734
232
            {
1735
232
                CPLFree(psInterestNode->pszValue);
1736
232
                psInterestNode->pszValue = CPLStrdup("gml:Polygon");
1737
232
            }
1738
2.13k
            else if (strcmp(psInterestNode->pszValue, "Piste") == 0)
1739
16
            {
1740
16
                CPLFree(psInterestNode->pszValue);
1741
16
                psInterestNode->pszValue = CPLStrdup("gml:Point");
1742
16
            }
1743
2.36k
        }
1744
26.8k
        else if (psInterestNode != nullptr &&
1745
26.8k
                 strcmp(psInterestNode->pszValue, "BoundingBox") == 0)
1746
20
        {
1747
20
            CPLFree(psInterestNode->pszValue);
1748
20
            psInterestNode->pszValue = CPLStrdup("Envelope");
1749
80
            for (CPLXMLNode *psChild = psInterestNode->psChild; psChild;
1750
60
                 psChild = psChild->psNext)
1751
60
            {
1752
60
                if (psChild->eType == CXT_Attribute &&
1753
20
                    strcmp(psChild->pszValue, "crs") == 0)
1754
0
                {
1755
0
                    CPLFree(psChild->pszValue);
1756
0
                    psChild->pszValue = CPLStrdup("srsName");
1757
0
                    break;
1758
0
                }
1759
60
            }
1760
20
        }
1761
1762
29.2k
        GMLFeature *poGMLFeature = m_poReader->GetState()->m_poFeature;
1763
29.2k
        if (stateStack[nStackDepth] == STATE_BOUNDED_BY_IN_FEATURE)
1764
192
        {
1765
192
            if (eAppSchemaType == APPSCHEMA_CITYGML)
1766
192
                CPLDestroyXMLNode(psInterestNode);
1767
0
            else
1768
0
                poGMLFeature->SetBoundedByGeometry(psInterestNode);
1769
192
        }
1770
29.0k
        else
1771
29.0k
        {
1772
29.0k
            if (m_poReader->FetchAllGeometries())
1773
0
                poGMLFeature->AddGeometry(psInterestNode);
1774
29.0k
            else
1775
29.0k
            {
1776
29.0k
                GMLFeatureClass *poClass = poGMLFeature->GetClass();
1777
29.0k
                if (poClass->GetGeometryPropertyCount() > 1)
1778
1.88k
                {
1779
1.88k
                    if (poGMLFeature->GetGeometryRef(m_nGeometryPropertyIndex))
1780
532
                    {
1781
                        // If we have already a geometry, setting a new one
1782
                        // will invalidate nodes potentially stored in
1783
                        // m_oMapElementToSubstitute, so clear it
1784
532
                        m_oMapElementToSubstitute.clear();
1785
532
                    }
1786
1.88k
                    poGMLFeature->SetGeometryDirectly(m_nGeometryPropertyIndex,
1787
1.88k
                                                      psInterestNode);
1788
1.88k
                }
1789
27.1k
                else
1790
27.1k
                {
1791
27.1k
                    if (poGMLFeature->GetGeometryRef(0))
1792
17.2k
                    {
1793
                        // If we have already a geometry, setting a new one
1794
                        // will invalidate nodes potentially stored in
1795
                        // m_oMapElementToSubstitute, so clear it
1796
17.2k
                        m_oMapElementToSubstitute.clear();
1797
17.2k
                    }
1798
27.1k
                    poGMLFeature->SetGeometryDirectly(psInterestNode);
1799
27.1k
                }
1800
29.0k
            }
1801
1802
29.0k
            POP_STATE();
1803
29.0k
        }
1804
29.2k
    }
1805
1806
70.5k
    apsXMLNode.pop_back();
1807
1808
70.5k
    return OGRERR_NONE;
1809
70.5k
}
1810
1811
/************************************************************************/
1812
/*                    endElementCityGMLGenericAttr()                    */
1813
/************************************************************************/
1814
OGRErr GMLHandler::endElementCityGMLGenericAttr()
1815
1816
1.47k
{
1817
1.47k
    if (m_pszCityGMLGenericAttrName != nullptr && m_bInCurField)
1818
621
    {
1819
621
        if (m_pszCurField != nullptr)
1820
621
        {
1821
621
            m_poReader->SetFeaturePropertyDirectly(m_pszCityGMLGenericAttrName,
1822
621
                                                   m_pszCurField, -1);
1823
621
        }
1824
621
        m_pszCurField = nullptr;
1825
621
        m_nCurFieldLen = 0;
1826
621
        m_nCurFieldAlloc = 0;
1827
621
        m_bInCurField = false;
1828
621
        CPLFree(m_pszCityGMLGenericAttrName);
1829
621
        m_pszCityGMLGenericAttrName = nullptr;
1830
621
    }
1831
1832
1.47k
    if (m_inCityGMLGenericAttrDepth == m_nDepth)
1833
607
    {
1834
607
        POP_STATE();
1835
607
    }
1836
1837
1.47k
    return OGRERR_NONE;
1838
1.47k
}
1839
1840
/************************************************************************/
1841
/*                        endElementAttribute()                         */
1842
/************************************************************************/
1843
OGRErr GMLHandler::endElementAttribute()
1844
1845
249k
{
1846
249k
    GMLReadState *poState = m_poReader->GetState();
1847
1848
249k
    if (m_bInCurField)
1849
219k
    {
1850
219k
        if (m_pszCurField == nullptr && m_poReader->IsEmptyAsNull())
1851
170k
        {
1852
170k
            if (m_pszValue != nullptr)
1853
130
            {
1854
130
                m_poReader->SetFeaturePropertyDirectly(poState->osPath.c_str(),
1855
130
                                                       m_pszValue, -1);
1856
130
                m_pszValue = nullptr;
1857
130
            }
1858
170k
        }
1859
49.6k
        else
1860
49.6k
        {
1861
49.6k
            m_poReader->SetFeaturePropertyDirectly(
1862
49.6k
                poState->osPath.c_str(),
1863
49.6k
                m_pszCurField ? m_pszCurField : CPLStrdup(""),
1864
49.6k
                m_nAttributeIndex);
1865
49.6k
            m_pszCurField = nullptr;
1866
49.6k
        }
1867
1868
219k
        if (m_pszHref != nullptr)
1869
8
        {
1870
8
            CPLString osPropNameHref = poState->osPath + "_href";
1871
8
            m_poReader->SetFeaturePropertyDirectly(osPropNameHref, m_pszHref,
1872
8
                                                   -1);
1873
8
            m_pszHref = nullptr;
1874
8
        }
1875
1876
219k
        if (m_pszUom != nullptr)
1877
223
        {
1878
223
            CPLString osPropNameUom = poState->osPath + "_uom";
1879
223
            m_poReader->SetFeaturePropertyDirectly(osPropNameUom, m_pszUom, -1);
1880
223
            m_pszUom = nullptr;
1881
223
        }
1882
1883
219k
        if (m_pszKieli != nullptr)
1884
7
        {
1885
7
            CPLString osPropName = poState->osPath + "_kieli";
1886
7
            m_poReader->SetFeaturePropertyDirectly(osPropName, m_pszKieli, -1);
1887
7
            m_pszKieli = nullptr;
1888
7
        }
1889
1890
219k
        m_nCurFieldLen = 0;
1891
219k
        m_nCurFieldAlloc = 0;
1892
219k
        m_bInCurField = false;
1893
219k
        m_nAttributeIndex = -1;
1894
1895
219k
        CPLFree(m_pszValue);
1896
219k
        m_pszValue = nullptr;
1897
219k
    }
1898
1899
249k
    poState->PopPath();
1900
1901
249k
    if (m_nAttributeDepth == m_nDepth)
1902
65.7k
    {
1903
65.7k
        POP_STATE();
1904
65.7k
    }
1905
1906
249k
    return OGRERR_NONE;
1907
249k
}
1908
1909
/************************************************************************/
1910
/*                    startElementFeatureProperty()                     */
1911
/************************************************************************/
1912
1913
OGRErr GMLHandler::startElementFeatureProperty(const char * /*pszName*/,
1914
                                               int /*nLenName*/, void *attr)
1915
0
{
1916
0
    if (m_nDepth == m_nAttributeDepth + 1)
1917
0
    {
1918
0
        const char *pszGMLId = GetFID(attr);
1919
0
        if (pszGMLId != nullptr)
1920
0
        {
1921
0
            m_poReader->SetFeaturePropertyDirectly(
1922
0
                nullptr, CPLStrdup(CPLSPrintf("#%s", pszGMLId)),
1923
0
                m_nAttributeIndex);
1924
0
        }
1925
0
    }
1926
1927
0
    return OGRERR_NONE;
1928
0
}
1929
1930
/************************************************************************/
1931
/*                      endElementFeatureProperty()                      */
1932
/************************************************************************/
1933
1934
OGRErr GMLHandler::endElementFeatureProperty()
1935
1936
0
{
1937
0
    if (m_nDepth == m_nAttributeDepth)
1938
0
    {
1939
0
        GMLReadState *poState = m_poReader->GetState();
1940
0
        poState->PopPath();
1941
1942
0
        POP_STATE();
1943
0
    }
1944
0
    return OGRERR_NONE;
1945
0
}
1946
1947
/************************************************************************/
1948
/*                          endElementFeature()                         */
1949
/************************************************************************/
1950
OGRErr GMLHandler::endElementFeature()
1951
1952
87.1k
{
1953
    /* -------------------------------------------------------------------- */
1954
    /*      If we are collecting a feature, and this element tag matches    */
1955
    /*      element name for the class, then we have finished the           */
1956
    /*      feature, and we pop the feature read state.                     */
1957
    /* -------------------------------------------------------------------- */
1958
87.1k
    if (m_nDepth == m_nDepthFeature)
1959
85.4k
    {
1960
85.4k
        m_oMapElementToSubstitute.clear();
1961
85.4k
        m_poReader->PopState();
1962
1963
85.4k
        POP_STATE();
1964
85.4k
    }
1965
1966
    /* -------------------------------------------------------------------- */
1967
    /*      Otherwise, we just pop the element off the local read states    */
1968
    /*      element stack.                                                  */
1969
    /* -------------------------------------------------------------------- */
1970
1.66k
    else
1971
1.66k
    {
1972
1.66k
        m_poReader->GetState()->PopPath();
1973
1.66k
    }
1974
1975
87.1k
    return OGRERR_NONE;
1976
87.1k
}
1977
1978
/************************************************************************/
1979
/*                          endElementDefault()                         */
1980
/************************************************************************/
1981
OGRErr GMLHandler::endElementDefault()
1982
1983
362k
{
1984
362k
    if (m_nDepth > 0)
1985
362k
        m_poReader->GetState()->PopPath();
1986
1987
362k
    return OGRERR_NONE;
1988
362k
}
1989
1990
/************************************************************************/
1991
/*                         dataHandlerAttribute()                       */
1992
/************************************************************************/
1993
1994
OGRErr GMLHandler::dataHandlerAttribute(const char *data, int nLen)
1995
1996
1.97M
{
1997
1.97M
    if (!m_bInCurField)
1998
67.0k
        return OGRERR_NONE;
1999
2000
1.90M
    int nIter = 0;
2001
2002
    // Ignore white space.
2003
1.90M
    if (m_nCurFieldLen == 0)
2004
91.4k
    {
2005
7.83M
        while (nIter < nLen)
2006
7.81M
        {
2007
7.81M
            const char ch = data[nIter];
2008
7.81M
            if (!(ch == ' ' || ch == 10 || ch == 13 || ch == '\t'))
2009
70.8k
                break;
2010
7.74M
            nIter++;
2011
7.74M
        }
2012
91.4k
    }
2013
2014
1.90M
    const int nCharsLen = nLen - nIter;
2015
2016
1.90M
    if (nCharsLen > INT_MAX - static_cast<int>(m_nCurFieldLen) - 1)
2017
0
    {
2018
0
        CPLError(CE_Failure, CPLE_OutOfMemory,
2019
0
                 "Too much data in a single element");
2020
0
        return OGRERR_NOT_ENOUGH_MEMORY;
2021
0
    }
2022
1.90M
    if (m_nCurFieldLen + nCharsLen + 1 > m_nCurFieldAlloc)
2023
91.8k
    {
2024
91.8k
        if (m_nCurFieldAlloc < INT_MAX - m_nCurFieldAlloc / 3 - nCharsLen - 1)
2025
91.8k
            m_nCurFieldAlloc =
2026
91.8k
                m_nCurFieldAlloc + m_nCurFieldAlloc / 3 + nCharsLen + 1;
2027
0
        else
2028
0
            m_nCurFieldAlloc = m_nCurFieldLen + nCharsLen + 1;
2029
91.8k
        char *pszNewCurField = static_cast<char *>(
2030
91.8k
            VSI_REALLOC_VERBOSE(m_pszCurField, m_nCurFieldAlloc));
2031
91.8k
        if (pszNewCurField == nullptr)
2032
0
        {
2033
0
            return OGRERR_NOT_ENOUGH_MEMORY;
2034
0
        }
2035
91.8k
        m_pszCurField = pszNewCurField;
2036
91.8k
    }
2037
1.90M
    memcpy(m_pszCurField + m_nCurFieldLen, data + nIter, nCharsLen);
2038
1.90M
    m_nCurFieldLen += nCharsLen;
2039
1.90M
    m_pszCurField[m_nCurFieldLen] = '\0';
2040
2041
1.90M
    return OGRERR_NONE;
2042
1.90M
}
2043
2044
/************************************************************************/
2045
/*                         dataHandlerGeometry()                        */
2046
/************************************************************************/
2047
2048
OGRErr GMLHandler::dataHandlerGeometry(const char *data, int nLen)
2049
2050
60.1k
{
2051
60.1k
    int nIter = 0;
2052
2053
    // Ignore white space
2054
60.1k
    if (m_nGeomLen == 0)
2055
53.1k
    {
2056
1.62M
        while (nIter < nLen)
2057
1.59M
        {
2058
1.59M
            char ch = data[nIter];
2059
1.59M
            if (!(ch == ' ' || ch == 10 || ch == 13 || ch == '\t'))
2060
19.2k
                break;
2061
1.57M
            nIter++;
2062
1.57M
        }
2063
53.1k
    }
2064
2065
60.1k
    const int nCharsLen = nLen - nIter;
2066
60.1k
    if (nCharsLen)
2067
26.2k
    {
2068
26.2k
        if (nCharsLen > INT_MAX - static_cast<int>(m_nGeomLen) - 1)
2069
0
        {
2070
0
            CPLError(CE_Failure, CPLE_OutOfMemory,
2071
0
                     "Too much data in a single element");
2072
0
            return OGRERR_NOT_ENOUGH_MEMORY;
2073
0
        }
2074
26.2k
        if (m_nGeomLen + nCharsLen + 1 > m_nGeomAlloc)
2075
19.7k
        {
2076
19.7k
            if (m_nGeomAlloc < INT_MAX - m_nGeomAlloc / 3 - nCharsLen - 1)
2077
19.7k
                m_nGeomAlloc = m_nGeomAlloc + m_nGeomAlloc / 3 + nCharsLen + 1;
2078
0
            else
2079
0
                m_nGeomAlloc = m_nGeomAlloc + nCharsLen + 1;
2080
19.7k
            char *pszNewGeometry = static_cast<char *>(
2081
19.7k
                VSI_REALLOC_VERBOSE(m_pszGeometry, m_nGeomAlloc));
2082
19.7k
            if (pszNewGeometry == nullptr)
2083
0
            {
2084
0
                return OGRERR_NOT_ENOUGH_MEMORY;
2085
0
            }
2086
19.7k
            m_pszGeometry = pszNewGeometry;
2087
19.7k
        }
2088
26.2k
        memcpy(m_pszGeometry + m_nGeomLen, data + nIter, nCharsLen);
2089
26.2k
        m_nGeomLen += nCharsLen;
2090
26.2k
        m_pszGeometry[m_nGeomLen] = '\0';
2091
26.2k
    }
2092
2093
60.1k
    return OGRERR_NONE;
2094
60.1k
}
2095
2096
/************************************************************************/
2097
/*                         IsGeometryElement()                          */
2098
/************************************************************************/
2099
2100
bool GMLHandler::IsGeometryElement(const char *pszElement)
2101
2102
316k
{
2103
316k
    int nFirst = 0;
2104
316k
    int nLast = GML_GEOMETRY_TYPE_COUNT - 1;
2105
316k
    unsigned long nHash = CPLHashSetHashStr(pszElement);
2106
316k
    do
2107
1.41M
    {
2108
1.41M
        const int nMiddle = (nFirst + nLast) / 2;
2109
1.41M
        if (nHash == pasGeometryNames[nMiddle].nHash)
2110
29.2k
            return strcmp(pszElement, pasGeometryNames[nMiddle].pszName) == 0;
2111
1.38M
        if (nHash < pasGeometryNames[nMiddle].nHash)
2112
954k
            nLast = nMiddle - 1;
2113
432k
        else
2114
432k
            nFirst = nMiddle + 1;
2115
1.38M
    } while (nFirst <= nLast);
2116
2117
286k
    if (eAppSchemaType == APPSCHEMA_AIXM &&
2118
603
        (strcmp(pszElement, "ElevatedPoint") == 0 ||
2119
599
         strcmp(pszElement, "ElevatedCurve") == 0 ||
2120
581
         strcmp(pszElement, "ElevatedSurface") == 0))
2121
22
    {
2122
22
        return true;
2123
22
    }
2124
2125
286k
    if (eAppSchemaType == APPSCHEMA_MTKGML &&
2126
20.0k
        (strcmp(pszElement, "Piste") == 0 || strcmp(pszElement, "Alue") == 0 ||
2127
19.7k
         strcmp(pszElement, "Murtoviiva") == 0))
2128
282
        return true;
2129
2130
286k
    return false;
2131
286k
}