Coverage Report

Created: 2025-06-09 08:44

/src/gdal/ogr/ogrsf_frmts/jml/ogrjmllayer.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  JML Translator
4
 * Purpose:  Implements OGRJMLLayer class.
5
 *
6
 ******************************************************************************
7
 * Copyright (c) 2014, Even Rouault <even dot rouault at spatialys dot com>
8
 *
9
 * SPDX-License-Identifier: MIT
10
 ****************************************************************************/
11
12
#include "cpl_conv.h"
13
#include "ogr_jml.h"
14
#include "ogr_p.h"
15
16
#ifdef HAVE_EXPAT
17
18
constexpr int PARSER_BUF_SIZE = 8192;
19
20
/************************************************************************/
21
/*                              OGRJMLLayer()                           */
22
/************************************************************************/
23
24
OGRJMLLayer::OGRJMLLayer(const char *pszLayerName, OGRJMLDataset *poDSIn,
25
                         VSILFILE *fpIn)
26
1.01k
    : m_poDS(poDSIn), poFeatureDefn(new OGRFeatureDefn(pszLayerName)),
27
1.01k
      nNextFID(0), fp(fpIn), bHasReadSchema(false), oParser(nullptr),
28
1.01k
      currentDepth(0), bStopParsing(false), nWithoutEventCounter(0),
29
1.01k
      nDataHandlerCounter(0), bAccumulateElementValue(false),
30
1.01k
      pszElementValue(static_cast<char *>(CPLCalloc(1024, 1))),
31
1.01k
      nElementValueLen(0), nElementValueAlloc(1024), poFeature(nullptr),
32
1.01k
      ppoFeatureTab(nullptr), nFeatureTabLength(0), nFeatureTabIndex(0),
33
1.01k
      bSchemaFinished(false), nJCSGMLInputTemplateDepth(0),
34
1.01k
      nCollectionElementDepth(0), nFeatureCollectionDepth(0),
35
1.01k
      nFeatureElementDepth(0), nGeometryElementDepth(0), nColumnDepth(0),
36
1.01k
      nNameDepth(0), nTypeDepth(0), nAttributeElementDepth(0), iAttr(-1),
37
1.01k
      iRGBField(-1)
38
1.01k
{
39
1.01k
    SetDescription(poFeatureDefn->GetName());
40
1.01k
    poFeatureDefn->Reference();
41
1.01k
}
42
43
/************************************************************************/
44
/*                             ~OGRJMLLayer()                           */
45
/************************************************************************/
46
47
OGRJMLLayer::~OGRJMLLayer()
48
49
1.01k
{
50
1.01k
    if (oParser)
51
778
        XML_ParserFree(oParser);
52
1.01k
    poFeatureDefn->Release();
53
54
1.01k
    CPLFree(pszElementValue);
55
56
1.01k
    for (int i = nFeatureTabIndex; i < nFeatureTabLength; i++)
57
0
        delete ppoFeatureTab[i];
58
1.01k
    CPLFree(ppoFeatureTab);
59
60
1.01k
    if (poFeature)
61
0
        delete poFeature;
62
1.01k
}
63
64
/************************************************************************/
65
/*                            GetLayerDefn()                            */
66
/************************************************************************/
67
68
OGRFeatureDefn *OGRJMLLayer::GetLayerDefn()
69
3.74k
{
70
3.74k
    if (!bHasReadSchema)
71
778
        LoadSchema();
72
73
3.74k
    return poFeatureDefn;
74
3.74k
}
75
76
static void XMLCALL startElementCbk(void *pUserData, const char *pszName,
77
                                    const char **ppszAttr)
78
1.04k
{
79
1.04k
    static_cast<OGRJMLLayer *>(pUserData)->startElementCbk(pszName, ppszAttr);
80
1.04k
}
81
82
static void XMLCALL endElementCbk(void *pUserData, const char *pszName)
83
984
{
84
984
    static_cast<OGRJMLLayer *>(pUserData)->endElementCbk(pszName);
85
984
}
86
87
static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen)
88
25.0k
{
89
25.0k
    static_cast<OGRJMLLayer *>(pUserData)->dataHandlerCbk(data, nLen);
90
25.0k
}
91
92
/************************************************************************/
93
/*                            ResetReading()                            */
94
/************************************************************************/
95
96
void OGRJMLLayer::ResetReading()
97
98
779
{
99
779
    nNextFID = 0;
100
101
779
    VSIFSeekL(fp, 0, SEEK_SET);
102
779
    VSIFClearErrL(fp);
103
779
    if (oParser)
104
1
        XML_ParserFree(oParser);
105
106
779
    oParser = OGRCreateExpatXMLParser();
107
779
    XML_SetElementHandler(oParser, ::startElementCbk, ::endElementCbk);
108
779
    XML_SetCharacterDataHandler(oParser, ::dataHandlerCbk);
109
779
    XML_SetUserData(oParser, this);
110
111
779
    for (int i = nFeatureTabIndex; i < nFeatureTabLength; i++)
112
0
        delete ppoFeatureTab[i];
113
779
    nFeatureTabIndex = 0;
114
779
    nFeatureTabLength = 0;
115
779
    delete poFeature;
116
779
    poFeature = nullptr;
117
118
779
    currentDepth = 0;
119
120
779
    nCollectionElementDepth = 0;
121
779
    nFeatureElementDepth = 0;
122
779
    nGeometryElementDepth = 0;
123
779
    nAttributeElementDepth = 0;
124
779
    iAttr = -1;
125
126
779
    bAccumulateElementValue = false;
127
779
    nElementValueLen = 0;
128
779
    pszElementValue[0] = '\0';
129
779
}
130
131
/************************************************************************/
132
/*                        startElementCbk()                            */
133
/************************************************************************/
134
135
void OGRJMLLayer::startElementCbk(const char *pszName, const char **ppszAttr)
136
1.04k
{
137
1.04k
    if (bStopParsing)
138
0
        return;
139
140
1.04k
    nWithoutEventCounter = 0;
141
142
1.04k
    if (nFeatureElementDepth > 0 && nAttributeElementDepth == 0 &&
143
1.04k
        nGeometryElementDepth == 0 && osGeometryElement.compare(pszName) == 0)
144
0
    {
145
0
        nGeometryElementDepth = currentDepth;
146
0
        bAccumulateElementValue = true;
147
0
    }
148
1.04k
    else if (nFeatureElementDepth > 0 && nAttributeElementDepth == 0 &&
149
1.04k
             nGeometryElementDepth == 0)
150
0
    {
151
        /* We assume that attributes are present in the order they are */
152
        /* declared, so as a first guess, we can try the aoColumns[iAttr + 1] */
153
0
        int i = (iAttr + 1 < poFeatureDefn->GetFieldCount()) ? -1 : 0;
154
0
        for (; i < static_cast<int>(aoColumns.size()); i++)
155
0
        {
156
0
            const OGRJMLColumn &oColumn =
157
0
                (i < 0) ? aoColumns[iAttr + 1] : aoColumns[i];
158
0
            if (oColumn.osElementName != pszName)
159
0
                continue;
160
161
0
            if (oColumn.bIsBody)
162
0
            {
163
0
                if (!oColumn.osAttributeName.empty() && ppszAttr != nullptr &&
164
0
                    ppszAttr[0] != nullptr && ppszAttr[1] != nullptr &&
165
0
                    oColumn.osAttributeName.compare(ppszAttr[0]) == 0 &&
166
0
                    oColumn.osAttributeValue.compare(ppszAttr[1]) == 0)
167
0
                {
168
                    /* <osElementName
169
                     * osAttributeName="osAttributeValue">value</osElementName>
170
                     */
171
172
0
                    bAccumulateElementValue = true;
173
0
                    nAttributeElementDepth = currentDepth;
174
0
                    iAttr = (i < 0) ? iAttr + 1 : i;
175
0
                    break;
176
0
                }
177
0
                else if (oColumn.osAttributeName.empty())
178
0
                {
179
                    /* <osElementName>value</osElementName> */
180
181
0
                    bAccumulateElementValue = true;
182
0
                    nAttributeElementDepth = currentDepth;
183
0
                    iAttr = (i < 0) ? iAttr + 1 : i;
184
0
                    break;
185
0
                }
186
0
            }
187
0
            else if (!oColumn.osAttributeName.empty() && ppszAttr != nullptr &&
188
0
                     ppszAttr[0] != nullptr && ppszAttr[1] != nullptr &&
189
0
                     oColumn.osAttributeName.compare(ppszAttr[0]) == 0)
190
0
            {
191
                /* <osElementName osAttributeName="value"></osElementName> */
192
193
0
                AddStringToElementValue(ppszAttr[1], (int)strlen(ppszAttr[1]));
194
195
0
                nAttributeElementDepth = currentDepth;
196
0
                iAttr = (i < 0) ? iAttr + 1 : i;
197
0
                break;
198
0
            }
199
0
        }
200
0
    }
201
1.04k
    else if (nGeometryElementDepth > 0)
202
0
    {
203
0
        AddStringToElementValue("<", 1);
204
0
        AddStringToElementValue(pszName, (int)strlen(pszName));
205
206
0
        const char **papszIter = ppszAttr;
207
0
        while (papszIter && *papszIter != nullptr)
208
0
        {
209
0
            AddStringToElementValue(" ", 1);
210
0
            AddStringToElementValue(papszIter[0], (int)strlen(papszIter[0]));
211
0
            AddStringToElementValue("=\"", 2);
212
0
            AddStringToElementValue(papszIter[1], (int)strlen(papszIter[1]));
213
0
            AddStringToElementValue("\"", 1);
214
0
            papszIter += 2;
215
0
        }
216
217
0
        AddStringToElementValue(">", 1);
218
0
    }
219
1.04k
    else if (nFeatureCollectionDepth > 0 && nFeatureElementDepth == 0 &&
220
1.04k
             osFeatureElement.compare(pszName) == 0)
221
8
    {
222
8
        nFeatureElementDepth = currentDepth;
223
8
        poFeature = new OGRFeature(poFeatureDefn);
224
8
    }
225
1.04k
    else if (nFeatureCollectionDepth == 0 &&
226
1.04k
             osCollectionElement.compare(pszName) == 0)
227
1
    {
228
1
        nFeatureCollectionDepth = currentDepth;
229
1
    }
230
231
1.04k
    currentDepth++;
232
1.04k
}
233
234
/************************************************************************/
235
/*                        StopAccumulate()                              */
236
/************************************************************************/
237
238
void OGRJMLLayer::StopAccumulate()
239
1.18k
{
240
1.18k
    bAccumulateElementValue = false;
241
1.18k
    nElementValueLen = 0;
242
1.18k
    pszElementValue[0] = '\0';
243
1.18k
}
244
245
/************************************************************************/
246
/*                           endElementCbk()                            */
247
/************************************************************************/
248
249
void OGRJMLLayer::endElementCbk(const char *pszName)
250
984
{
251
984
    if (bStopParsing)
252
0
        return;
253
254
984
    nWithoutEventCounter = 0;
255
256
984
    currentDepth--;
257
258
984
    if (nAttributeElementDepth == currentDepth)
259
0
    {
260
0
        if (nElementValueLen)
261
0
            poFeature->SetField(iAttr, pszElementValue);
262
0
        else if (iAttr >= 0)
263
0
            poFeature->SetFieldNull(iAttr);
264
0
        nAttributeElementDepth = 0;
265
0
        StopAccumulate();
266
0
    }
267
984
    else if (nGeometryElementDepth > 0 && currentDepth > nGeometryElementDepth)
268
0
    {
269
0
        AddStringToElementValue("</", 2);
270
0
        AddStringToElementValue(pszName, static_cast<int>(strlen(pszName)));
271
0
        AddStringToElementValue(">", 1);
272
0
    }
273
984
    else if (nGeometryElementDepth == currentDepth)
274
0
    {
275
0
        if (nElementValueLen)
276
0
        {
277
0
            OGRGeometry *poGeom =
278
0
                OGRGeometry::FromHandle(OGR_G_CreateFromGML(pszElementValue));
279
0
            if (poGeom != nullptr &&
280
0
                poGeom->getGeometryType() == wkbGeometryCollection &&
281
0
                poGeom->IsEmpty())
282
0
            {
283
0
                delete poGeom;
284
0
            }
285
0
            else
286
0
                poFeature->SetGeometryDirectly(poGeom);
287
0
        }
288
289
0
        nGeometryElementDepth = 0;
290
0
        StopAccumulate();
291
0
    }
292
984
    else if (nFeatureElementDepth == currentDepth)
293
8
    {
294
        /* Builds a style string from R_G_B if we don't already have a */
295
        /* style string */
296
8
        OGRGeometry *poGeom = poFeature->GetGeometryRef();
297
8
        unsigned int R = 0;
298
8
        unsigned int G = 0;
299
8
        unsigned int B = 0;
300
8
        if (iRGBField >= 0 && poFeature->IsFieldSetAndNotNull(iRGBField) &&
301
8
            poFeature->GetStyleString() == nullptr && poGeom != nullptr &&
302
8
            sscanf(poFeature->GetFieldAsString(iRGBField), "%02X%02X%02X", &R,
303
0
                   &G, &B) == 3)
304
0
        {
305
0
            const OGRwkbGeometryType eGeomType =
306
0
                wkbFlatten(poGeom->getGeometryType());
307
0
            if (eGeomType == wkbPoint || eGeomType == wkbMultiPoint ||
308
0
                eGeomType == wkbLineString || eGeomType == wkbMultiLineString)
309
0
            {
310
0
                poFeature->SetStyleString(
311
0
                    CPLSPrintf("PEN(c:#%02X%02X%02X)", R, G, B));
312
0
            }
313
0
            else if (eGeomType == wkbPolygon || eGeomType == wkbMultiPolygon)
314
0
            {
315
0
                poFeature->SetStyleString(
316
0
                    CPLSPrintf("BRUSH(fc:#%02X%02X%02X)", R, G, B));
317
0
            }
318
0
        }
319
320
8
        poFeature->SetFID(nNextFID++);
321
322
8
        if ((m_poFilterGeom == nullptr || FilterGeometry(poGeom)) &&
323
8
            (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
324
8
        {
325
8
            ppoFeatureTab = static_cast<OGRFeature **>(CPLRealloc(
326
8
                ppoFeatureTab, sizeof(OGRFeature *) * (nFeatureTabLength + 1)));
327
8
            ppoFeatureTab[nFeatureTabLength] = poFeature;
328
8
            nFeatureTabLength++;
329
8
        }
330
0
        else
331
0
        {
332
0
            delete poFeature;
333
0
        }
334
8
        poFeature = nullptr;
335
8
        iAttr = -1;
336
337
8
        nFeatureElementDepth = 0;
338
8
    }
339
976
    else if (nFeatureCollectionDepth == currentDepth)
340
0
    {
341
0
        nFeatureCollectionDepth = 0;
342
0
    }
343
984
}
344
345
/************************************************************************/
346
/*                        AddStringToElementValue()                     */
347
/************************************************************************/
348
349
void OGRJMLLayer::AddStringToElementValue(const char *data, int nLen)
350
1.59k
{
351
1.59k
    if (nLen > INT_MAX - nElementValueLen - 1 - 1000)
352
0
    {
353
0
        CPLError(CE_Failure, CPLE_OutOfMemory,
354
0
                 "Too much data in a single element");
355
0
        XML_StopParser(oParser, XML_FALSE);
356
0
        bStopParsing = true;
357
0
        return;
358
0
    }
359
1.59k
    if (nElementValueLen + nLen + 1 > nElementValueAlloc)
360
0
    {
361
0
        char *pszNewElementValue = static_cast<char *>(VSI_REALLOC_VERBOSE(
362
0
            pszElementValue, nElementValueLen + nLen + 1 + 1000));
363
0
        if (pszNewElementValue == nullptr)
364
0
        {
365
0
            XML_StopParser(oParser, XML_FALSE);
366
0
            bStopParsing = true;
367
0
            return;
368
0
        }
369
0
        nElementValueAlloc = nElementValueLen + nLen + 1 + 1000;
370
0
        pszElementValue = pszNewElementValue;
371
0
    }
372
1.59k
    memcpy(pszElementValue + nElementValueLen, data, nLen);
373
1.59k
    nElementValueLen += nLen;
374
1.59k
    pszElementValue[nElementValueLen] = '\0';
375
1.59k
}
376
377
/************************************************************************/
378
/*                          dataHandlerCbk()                            */
379
/************************************************************************/
380
381
void OGRJMLLayer::dataHandlerCbk(const char *data, int nLen)
382
25.0k
{
383
25.0k
    if (bStopParsing)
384
0
        return;
385
386
25.0k
    nDataHandlerCounter++;
387
25.0k
    if (nDataHandlerCounter >= PARSER_BUF_SIZE)
388
0
    {
389
0
        CPLError(CE_Failure, CPLE_AppDefined,
390
0
                 "File probably corrupted (million laugh pattern)");
391
0
        XML_StopParser(oParser, XML_FALSE);
392
0
        bStopParsing = true;
393
0
        return;
394
0
    }
395
396
25.0k
    nWithoutEventCounter = 0;
397
398
25.0k
    if (bAccumulateElementValue)
399
1.59k
    {
400
1.59k
        AddStringToElementValue(data, nLen);
401
1.59k
    }
402
25.0k
}
403
404
/************************************************************************/
405
/*                           GetNextFeature()                           */
406
/************************************************************************/
407
408
OGRFeature *OGRJMLLayer::GetNextFeature()
409
750
{
410
750
    if (!bHasReadSchema)
411
0
        LoadSchema();
412
413
750
    if (bStopParsing)
414
742
        return nullptr;
415
416
8
    if (nFeatureTabIndex < nFeatureTabLength)
417
3
    {
418
3
        return ppoFeatureTab[nFeatureTabIndex++];
419
3
    }
420
421
5
    if (VSIFEofL(fp) || VSIFErrorL(fp))
422
0
        return nullptr;
423
424
5
    std::vector<char> aBuf(PARSER_BUF_SIZE);
425
426
5
    nFeatureTabLength = 0;
427
5
    nFeatureTabIndex = 0;
428
429
5
    nWithoutEventCounter = 0;
430
431
5
    int nDone = 0;
432
5
    do
433
6
    {
434
6
        nDataHandlerCounter = 0;
435
6
        unsigned int nLen =
436
6
            (unsigned int)VSIFReadL(aBuf.data(), 1, aBuf.size(), fp);
437
6
        nDone = (nLen < aBuf.size());
438
6
        if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
439
1
        {
440
1
            CPLError(CE_Failure, CPLE_AppDefined,
441
1
                     "XML parsing of JML file failed : %s "
442
1
                     "at line %d, column %d",
443
1
                     XML_ErrorString(XML_GetErrorCode(oParser)),
444
1
                     (int)XML_GetCurrentLineNumber(oParser),
445
1
                     (int)XML_GetCurrentColumnNumber(oParser));
446
1
            bStopParsing = true;
447
1
        }
448
6
        nWithoutEventCounter++;
449
6
    } while (!nDone && !bStopParsing && nFeatureTabLength == 0 &&
450
6
             nWithoutEventCounter < 10);
451
452
5
    if (nWithoutEventCounter == 10)
453
0
    {
454
0
        CPLError(CE_Failure, CPLE_AppDefined,
455
0
                 "Too much data inside one element. File probably corrupted");
456
0
        bStopParsing = true;
457
0
    }
458
459
5
    return (nFeatureTabLength) ? ppoFeatureTab[nFeatureTabIndex++] : nullptr;
460
5
}
461
462
static void XMLCALL startElementLoadSchemaCbk(void *pUserData,
463
                                              const char *pszName,
464
                                              const char **ppszAttr)
465
36.3k
{
466
36.3k
    static_cast<OGRJMLLayer *>(pUserData)->startElementLoadSchemaCbk(pszName,
467
36.3k
                                                                     ppszAttr);
468
36.3k
}
469
470
static void XMLCALL endElementLoadSchemaCbk(void *pUserData,
471
                                            const char *pszName)
472
32.8k
{
473
32.8k
    static_cast<OGRJMLLayer *>(pUserData)->endElementLoadSchemaCbk(pszName);
474
32.8k
}
475
476
/************************************************************************/
477
/*                           LoadSchema()                              */
478
/************************************************************************/
479
480
/** This function parses the beginning of the file to detect the fields */
481
void OGRJMLLayer::LoadSchema()
482
778
{
483
778
    if (bHasReadSchema)
484
0
        return;
485
486
778
    bHasReadSchema = true;
487
488
778
    oParser = OGRCreateExpatXMLParser();
489
778
    XML_SetElementHandler(oParser, ::startElementLoadSchemaCbk,
490
778
                          ::endElementLoadSchemaCbk);
491
778
    XML_SetCharacterDataHandler(oParser, ::dataHandlerCbk);
492
778
    XML_SetUserData(oParser, this);
493
494
778
    VSIFSeekL(fp, 0, SEEK_SET);
495
496
778
    std::vector<char> aBuf(PARSER_BUF_SIZE);
497
778
    int nDone = 0;
498
778
    do
499
1.19k
    {
500
1.19k
        nDataHandlerCounter = 0;
501
1.19k
        const unsigned int nLen = static_cast<unsigned int>(
502
1.19k
            VSIFReadL(aBuf.data(), 1, aBuf.size(), fp));
503
1.19k
        nDone = (nLen < aBuf.size());
504
1.19k
        if (XML_Parse(oParser, aBuf.data(), nLen, nDone) == XML_STATUS_ERROR)
505
777
        {
506
777
            CPLError(CE_Failure, CPLE_AppDefined,
507
777
                     "XML parsing of JML file failed : %s at line %d, "
508
777
                     "column %d",
509
777
                     XML_ErrorString(XML_GetErrorCode(oParser)),
510
777
                     static_cast<int>(XML_GetCurrentLineNumber(oParser)),
511
777
                     static_cast<int>(XML_GetCurrentColumnNumber(oParser)));
512
777
            bStopParsing = true;
513
777
        }
514
1.19k
        nWithoutEventCounter++;
515
1.19k
    } while (!nDone && !bStopParsing && !bSchemaFinished &&
516
1.19k
             nWithoutEventCounter < 10);
517
518
778
    XML_ParserFree(oParser);
519
778
    oParser = nullptr;
520
521
778
    if (nWithoutEventCounter == 10)
522
0
    {
523
0
        CPLError(CE_Failure, CPLE_AppDefined,
524
0
                 "Too much data inside one element. File probably corrupted");
525
0
        bStopParsing = true;
526
0
    }
527
528
778
    if (osCollectionElement.empty() || osFeatureElement.empty() ||
529
778
        osGeometryElement.empty())
530
719
    {
531
719
        CPLError(CE_Failure, CPLE_AppDefined,
532
719
                 "Missing CollectionElement, FeatureElement or "
533
719
                 "GeometryElement");
534
719
        bStopParsing = true;
535
719
    }
536
537
778
    if (!osSRSName.empty())
538
0
    {
539
0
        if (osSRSName.find("http://www.opengis.net/gml/srs/epsg.xml#") == 0)
540
0
        {
541
0
            OGRSpatialReference *poSRS = new OGRSpatialReference();
542
0
            poSRS->importFromEPSG(atoi(
543
0
                osSRSName
544
0
                    .substr(strlen("http://www.opengis.net/gml/srs/epsg.xml#"))
545
0
                    .c_str()));
546
0
            poSRS->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
547
0
            poFeatureDefn->GetGeomFieldDefn(0)->SetSpatialRef(poSRS);
548
0
            poSRS->Release();
549
0
        }
550
0
    }
551
552
778
    nJCSGMLInputTemplateDepth = 0;
553
778
    nCollectionElementDepth = 0;
554
778
    nFeatureCollectionDepth = 0;
555
778
    nFeatureElementDepth = 0;
556
778
    nGeometryElementDepth = 0;
557
778
    nColumnDepth = 0;
558
778
    nNameDepth = 0;
559
778
    nTypeDepth = 0;
560
778
    nAttributeElementDepth = 0;
561
562
778
    ResetReading();
563
778
}
564
565
/************************************************************************/
566
/*                  startElementLoadSchemaCbk()                         */
567
/************************************************************************/
568
569
void OGRJMLLayer::startElementLoadSchemaCbk(const char *pszName,
570
                                            const char **ppszAttr)
571
36.3k
{
572
36.3k
    if (bStopParsing)
573
0
        return;
574
575
36.3k
    nWithoutEventCounter = 0;
576
577
36.3k
    if (nJCSGMLInputTemplateDepth == 0 &&
578
36.3k
        strcmp(pszName, "JCSGMLInputTemplate") == 0)
579
91
        nJCSGMLInputTemplateDepth = currentDepth;
580
36.2k
    else if (nJCSGMLInputTemplateDepth > 0)
581
3.00k
    {
582
3.00k
        if (nCollectionElementDepth == 0 &&
583
3.00k
            strcmp(pszName, "CollectionElement") == 0)
584
140
        {
585
140
            nCollectionElementDepth = currentDepth;
586
140
            bAccumulateElementValue = true;
587
140
        }
588
2.86k
        else if (nFeatureElementDepth == 0 &&
589
2.86k
                 strcmp(pszName, "FeatureElement") == 0)
590
137
        {
591
137
            nFeatureElementDepth = currentDepth;
592
137
            bAccumulateElementValue = true;
593
137
        }
594
2.72k
        else if (nGeometryElementDepth == 0 &&
595
2.72k
                 strcmp(pszName, "GeometryElement") == 0)
596
124
        {
597
124
            nGeometryElementDepth = currentDepth;
598
124
            bAccumulateElementValue = true;
599
124
        }
600
2.60k
        else if (nColumnDepth == 0 && strcmp(pszName, "column") == 0)
601
263
        {
602
263
            nColumnDepth = currentDepth;
603
263
            oCurColumn.osName = "";
604
263
            oCurColumn.osType = "";
605
263
            oCurColumn.osElementName = "";
606
263
            oCurColumn.osAttributeName = "";
607
263
            oCurColumn.osAttributeValue = "";
608
263
            oCurColumn.bIsBody = false;
609
263
        }
610
2.33k
        else if (nColumnDepth > 0)
611
2.13k
        {
612
2.13k
            if (nNameDepth == 0 && strcmp(pszName, "name") == 0)
613
425
            {
614
425
                nNameDepth = currentDepth;
615
425
                bAccumulateElementValue = true;
616
425
            }
617
1.71k
            else if (nTypeDepth == 0 && strcmp(pszName, "type") == 0)
618
435
            {
619
435
                nTypeDepth = currentDepth;
620
435
                bAccumulateElementValue = true;
621
435
            }
622
1.27k
            else if (strcmp(pszName, "valueElement") == 0)
623
340
            {
624
340
                const char **papszIter = ppszAttr;
625
1.26k
                while (papszIter && *papszIter != nullptr)
626
924
                {
627
924
                    if (strcmp(*papszIter, "elementName") == 0)
628
338
                        oCurColumn.osElementName = papszIter[1];
629
586
                    else if (strcmp(*papszIter, "attributeName") == 0)
630
282
                        oCurColumn.osAttributeName = papszIter[1];
631
304
                    else if (strcmp(*papszIter, "attributeValue") == 0)
632
271
                        oCurColumn.osAttributeValue = papszIter[1];
633
924
                    papszIter += 2;
634
924
                }
635
340
            }
636
936
            else if (strcmp(pszName, "valueLocation") == 0)
637
316
            {
638
316
                const char **papszIter = ppszAttr;
639
645
                while (papszIter && *papszIter != nullptr)
640
329
                {
641
329
                    if (strcmp(*papszIter, "position") == 0)
642
236
                        oCurColumn.bIsBody = strcmp(papszIter[1], "body") == 0;
643
93
                    else if (strcmp(*papszIter, "attributeName") == 0)
644
14
                        oCurColumn.osAttributeName = papszIter[1];
645
329
                    papszIter += 2;
646
329
                }
647
316
            }
648
2.13k
        }
649
3.00k
    }
650
33.2k
    else if (nFeatureCollectionDepth == 0 &&
651
33.2k
             osCollectionElement.compare(pszName) == 0)
652
5
    {
653
5
        nFeatureCollectionDepth = currentDepth;
654
5
    }
655
33.2k
    else if (nFeatureCollectionDepth > 0 &&
656
33.2k
             currentDepth == nFeatureCollectionDepth + 2 &&
657
33.2k
             strcmp(pszName, "gml:Box") == 0)
658
0
    {
659
0
        const char **papszIter = ppszAttr;
660
0
        while (papszIter && *papszIter != nullptr)
661
0
        {
662
0
            if (strcmp(*papszIter, "srsName") == 0)
663
0
                osSRSName = papszIter[1];
664
0
            papszIter += 2;
665
0
        }
666
0
        bSchemaFinished = true;
667
0
    }
668
33.2k
    else if (nFeatureCollectionDepth >= 0 &&
669
33.2k
             currentDepth >= nFeatureCollectionDepth + 1 &&
670
33.2k
             osFeatureElement.compare(pszName) == 0)
671
8
    {
672
8
        bSchemaFinished = true;
673
8
    }
674
675
36.3k
    currentDepth++;
676
36.3k
}
677
678
/************************************************************************/
679
/*                   endElementLoadSchemaCbk()                          */
680
/************************************************************************/
681
682
void OGRJMLLayer::endElementLoadSchemaCbk(const char * /* pszName */)
683
32.8k
{
684
32.8k
    if (bStopParsing)
685
0
        return;
686
687
32.8k
    nWithoutEventCounter = 0;
688
689
32.8k
    currentDepth--;
690
691
32.8k
    if (nJCSGMLInputTemplateDepth == currentDepth)
692
12
    {
693
12
        nJCSGMLInputTemplateDepth = 0;
694
12
    }
695
32.8k
    else if (nCollectionElementDepth == currentDepth)
696
130
    {
697
130
        nCollectionElementDepth = 0;
698
130
        osCollectionElement = pszElementValue;
699
#ifdef DEBUG_VERBOSE
700
        CPLDebug("JML", "osCollectionElement = %s",
701
                 osCollectionElement.c_str());
702
#endif
703
130
        StopAccumulate();
704
130
    }
705
32.7k
    else if (nFeatureElementDepth == currentDepth)
706
124
    {
707
124
        nFeatureElementDepth = 0;
708
124
        osFeatureElement = pszElementValue;
709
#ifdef DEBUG_VERBOSE
710
        CPLDebug("JML", "osFeatureElement = %s", osFeatureElement.c_str());
711
#endif
712
124
        StopAccumulate();
713
124
    }
714
32.6k
    else if (nGeometryElementDepth == currentDepth)
715
108
    {
716
108
        nGeometryElementDepth = 0;
717
108
        osGeometryElement = pszElementValue;
718
#ifdef DEBUG_VERBOSE
719
        CPLDebug("JML", "osGeometryElement = %s", osGeometryElement.c_str());
720
#endif
721
108
        StopAccumulate();
722
108
    }
723
32.5k
    else if (nColumnDepth == currentDepth)
724
205
    {
725
205
        bool bIsOK = true;
726
205
        if (oCurColumn.osName.empty())
727
33
            bIsOK = false;
728
205
        if (oCurColumn.osType.empty())
729
46
            bIsOK = false;
730
205
        if (oCurColumn.osElementName.empty())
731
60
            bIsOK = false;
732
205
        if (oCurColumn.bIsBody)
733
97
        {
734
97
            if (oCurColumn.osAttributeName.empty() &&
735
97
                !oCurColumn.osAttributeValue.empty())
736
2
                bIsOK = false;
737
97
            if (!oCurColumn.osAttributeName.empty() &&
738
97
                oCurColumn.osAttributeValue.empty())
739
5
                bIsOK = false;
740
            /* Only 2 valid possibilities : */
741
            /* <osElementName
742
             * osAttributeName="osAttributeValue">value</osElementName> */
743
            /* <osElementName>value</osElementName> */
744
97
        }
745
108
        else
746
108
        {
747
            /* <osElementName osAttributeName="value"></osElementName> */
748
108
            if (oCurColumn.osAttributeName.empty())
749
65
                bIsOK = false;
750
108
            if (!oCurColumn.osAttributeValue.empty())
751
38
                bIsOK = false;
752
108
        }
753
754
205
        if (bIsOK)
755
87
        {
756
87
            OGRFieldType eType = OFTString;
757
87
            if (EQUAL(oCurColumn.osType, "INTEGER"))
758
3
                eType = OFTInteger;
759
84
            else if (EQUAL(oCurColumn.osType, "DOUBLE"))
760
0
                eType = OFTReal;
761
84
            else if (EQUAL(oCurColumn.osType, "DATE"))
762
14
                eType = OFTDateTime;
763
87
            OGRFieldDefn oField(oCurColumn.osName, eType);
764
765
87
            if (oCurColumn.osName == "R_G_B" && eType == OFTString)
766
7
                iRGBField = poFeatureDefn->GetFieldCount();
767
768
87
            poFeatureDefn->AddFieldDefn(&oField);
769
87
            aoColumns.push_back(oCurColumn);
770
87
        }
771
118
        else
772
118
        {
773
118
            CPLDebug("JML",
774
118
                     "Invalid column definition: name = %s, type = %s, "
775
118
                     "elementName = %s, attributeName = %s, "
776
118
                     "attributeValue = %s, bIsBody = %d",
777
118
                     oCurColumn.osName.c_str(), oCurColumn.osType.c_str(),
778
118
                     oCurColumn.osElementName.c_str(),
779
118
                     oCurColumn.osAttributeName.c_str(),
780
118
                     oCurColumn.osAttributeValue.c_str(),
781
118
                     static_cast<int>(oCurColumn.bIsBody));
782
118
        }
783
784
205
        nColumnDepth = 0;
785
205
    }
786
32.3k
    else if (nNameDepth == currentDepth)
787
401
    {
788
401
        nNameDepth = 0;
789
401
        oCurColumn.osName = pszElementValue;
790
#ifdef DEBUG_VERBOSE
791
        CPLDebug("JML", "oCurColumn.osName = %s", oCurColumn.osName.c_str());
792
#endif
793
401
        StopAccumulate();
794
401
    }
795
31.9k
    else if (nTypeDepth == currentDepth)
796
420
    {
797
420
        nTypeDepth = 0;
798
420
        oCurColumn.osType = pszElementValue;
799
#ifdef DEBUG_VERBOSE
800
        CPLDebug("JML", "oCurColumn.osType = %s", oCurColumn.osType.c_str());
801
#endif
802
420
        StopAccumulate();
803
420
    }
804
32.8k
}
805
806
/************************************************************************/
807
/*                           TestCapability()                           */
808
/************************************************************************/
809
810
int OGRJMLLayer::TestCapability(const char *pszCap)
811
812
1
{
813
1
    if (EQUAL(pszCap, OLCStringsAsUTF8))
814
0
        return true;
815
1
    else if (EQUAL(pszCap, OLCZGeometries))
816
0
        return true;
817
818
1
    return false;
819
1
}
820
821
#endif /* HAVE_EXPAT */