Coverage Report

Created: 2025-12-31 08:30

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/ogr/ogrsf_frmts/gmlas/ogrgmlasconf.cpp
Line
Count
Source
1
/******************************************************************************
2
 * Project:  OGR
3
 * Purpose:  OGRGMLASDriver implementation
4
 * Author:   Even Rouault, <even dot rouault at spatialys dot com>
5
 *
6
 * Initial development funded by the European Earth observation programme
7
 * Copernicus
8
 *
9
 ******************************************************************************
10
 * Copyright (c) 2016, Even Rouault, <even dot rouault at spatialys dot com>
11
 *
12
 * SPDX-License-Identifier: MIT
13
 ****************************************************************************/
14
15
#include "ogr_gmlas.h"
16
17
#include "cpl_minixml.h"
18
19
#include <algorithm>
20
21
#ifdef EMBED_RESOURCE_FILES
22
#include "embedded_resources.h"
23
#endif
24
25
/************************************************************************/
26
/*                              Finalize()                              */
27
/************************************************************************/
28
29
void GMLASConfiguration::Finalize()
30
2.42k
{
31
2.42k
    if (m_bAllowXSDCache && m_osXSDCacheDirectory.empty())
32
2.42k
    {
33
2.42k
        m_osXSDCacheDirectory = GDALGetCacheDirectory();
34
2.42k
        if (m_osXSDCacheDirectory.empty())
35
0
        {
36
0
            CPLError(CE_Warning, CPLE_AppDefined,
37
0
                     "Could not determine a directory for GMLAS XSD cache");
38
0
        }
39
2.42k
        else
40
2.42k
        {
41
2.42k
            m_osXSDCacheDirectory = CPLFormFilenameSafe(
42
2.42k
                m_osXSDCacheDirectory, "gmlas_xsd_cache", nullptr);
43
2.42k
            CPLDebug("GMLAS", "XSD cache directory: %s",
44
2.42k
                     m_osXSDCacheDirectory.c_str());
45
2.42k
        }
46
2.42k
    }
47
2.42k
}
48
49
/************************************************************************/
50
/*                          CPLGetXMLBoolValue()                        */
51
/************************************************************************/
52
53
static bool CPLGetXMLBoolValue(CPLXMLNode *psNode, const char *pszKey,
54
                               bool bDefault)
55
104k
{
56
104k
    const char *pszVal = CPLGetXMLValue(psNode, pszKey, nullptr);
57
104k
    if (pszVal)
58
53.4k
        return CPLTestBool(pszVal);
59
50.9k
    else
60
50.9k
        return bDefault;
61
104k
}
62
63
/************************************************************************/
64
/*                            IsValidXPath()                            */
65
/************************************************************************/
66
67
static bool IsValidXPath(const CPLString &osXPath)
68
58.2k
{
69
    // Check that the XPath syntax belongs to the subset we
70
    // understand
71
58.2k
    bool bOK = !osXPath.empty();
72
1.24M
    for (size_t i = 0; i < osXPath.size(); ++i)
73
1.18M
    {
74
1.18M
        const char chCur = osXPath[i];
75
1.18M
        if (chCur == '/')
76
31.5k
        {
77
            // OK
78
31.5k
        }
79
1.15M
        else if (chCur == '@' && (i == 0 || osXPath[i - 1] == '/') &&
80
31.5k
                 i < osXPath.size() - 1 &&
81
31.5k
                 isalpha(static_cast<unsigned char>(osXPath[i + 1])))
82
31.5k
        {
83
            // OK
84
31.5k
        }
85
1.12M
        else if (chCur == '_' || isalpha(static_cast<unsigned char>(chCur)))
86
1.02M
        {
87
            // OK
88
1.02M
        }
89
97.1k
        else if (isdigit(static_cast<unsigned char>(chCur)) && i > 0 &&
90
21.8k
                 (isalnum(static_cast<unsigned char>(osXPath[i - 1])) ||
91
0
                  osXPath[i - 1] == '_'))
92
21.8k
        {
93
            // OK
94
21.8k
        }
95
75.2k
        else if (chCur == ':' && i > 0 &&
96
75.2k
                 (isalnum(static_cast<unsigned char>(osXPath[i - 1])) ||
97
0
                  osXPath[i - 1] == '_') &&
98
75.2k
                 i < osXPath.size() - 1 &&
99
75.2k
                 isalpha(static_cast<unsigned char>(osXPath[i + 1])))
100
75.2k
        {
101
            // OK
102
75.2k
        }
103
0
        else
104
0
        {
105
0
            bOK = false;
106
0
            break;
107
0
        }
108
1.18M
    }
109
58.2k
    return bOK;
110
58.2k
}
111
112
/************************************************************************/
113
/*                    GMLASConfigurationErrorHandler()                  */
114
/************************************************************************/
115
116
static void CPL_STDCALL GMLASConfigurationErrorHandler(CPLErr /*eErr*/,
117
                                                       CPLErrorNum /*nType*/,
118
                                                       const char *pszMsg)
119
2.42k
{
120
2.42k
    std::vector<CPLString> *paosErrors =
121
2.42k
        static_cast<std::vector<CPLString> *>(CPLGetErrorHandlerUserData());
122
2.42k
    paosErrors->push_back(pszMsg);
123
2.42k
}
124
125
/************************************************************************/
126
/*                           ParseNamespaces()                          */
127
/************************************************************************/
128
129
static void ParseNamespaces(CPLXMLNode *psContainerNode,
130
                            std::map<CPLString, CPLString> &oMap)
131
7.28k
{
132
7.28k
    CPLXMLNode *psNamespaces = CPLGetXMLNode(psContainerNode, "Namespaces");
133
7.28k
    if (psNamespaces != nullptr)
134
7.28k
    {
135
21.8k
        for (CPLXMLNode *psIter = psNamespaces->psChild; psIter != nullptr;
136
14.5k
             psIter = psIter->psNext)
137
14.5k
        {
138
14.5k
            if (psIter->eType == CXT_Element &&
139
14.5k
                EQUAL(psIter->pszValue, "Namespace"))
140
14.5k
            {
141
14.5k
                const std::string osPrefix =
142
14.5k
                    CPLGetXMLValue(psIter, "prefix", "");
143
14.5k
                const std::string osURI = CPLGetXMLValue(psIter, "uri", "");
144
14.5k
                if (!osPrefix.empty() && !osURI.empty())
145
14.5k
                {
146
14.5k
                    if (oMap.find(osPrefix) == oMap.end())
147
14.5k
                    {
148
14.5k
                        oMap[osPrefix] = osURI;
149
14.5k
                    }
150
0
                    else
151
0
                    {
152
0
                        CPLError(CE_Warning, CPLE_AppDefined,
153
0
                                 "Prefix %s was already mapped to %s. "
154
0
                                 "Attempt to map it to %s ignored",
155
0
                                 osPrefix.c_str(), oMap[osPrefix].c_str(),
156
0
                                 osURI.c_str());
157
0
                    }
158
14.5k
                }
159
14.5k
            }
160
14.5k
        }
161
7.28k
    }
162
7.28k
}
163
164
/************************************************************************/
165
/*                           GetDefaultConfFile()                       */
166
/************************************************************************/
167
168
/* static */
169
std::string GMLASConfiguration::GetDefaultConfFile(bool &bUnlinkAfterUse)
170
2.42k
{
171
2.42k
    bUnlinkAfterUse = false;
172
2.42k
#if !defined(USE_ONLY_EMBEDDED_RESOURCE_FILES)
173
2.42k
    const char *pszConfigFile = CPLFindFile("gdal", szDEFAULT_CONF_FILENAME);
174
2.42k
    if (pszConfigFile)
175
0
        return pszConfigFile;
176
2.42k
#endif
177
2.42k
#ifdef EMBED_RESOURCE_FILES
178
2.42k
    static const bool bOnce [[maybe_unused]] = []()
179
2.42k
    {
180
5
        CPLDebug("GMLAS", "Using embedded %s", szDEFAULT_CONF_FILENAME);
181
5
        return true;
182
5
    }();
183
2.42k
    bUnlinkAfterUse = true;
184
2.42k
    const std::string osTmpFilename =
185
2.42k
        VSIMemGenerateHiddenFilename(szDEFAULT_CONF_FILENAME);
186
2.42k
    VSIFCloseL(VSIFileFromMemBuffer(
187
2.42k
        osTmpFilename.c_str(),
188
2.42k
        const_cast<GByte *>(
189
2.42k
            reinterpret_cast<const GByte *>(GMLASConfXMLGetFileContent())),
190
2.42k
        static_cast<int>(strlen(GMLASConfXMLGetFileContent())),
191
2.42k
        /* bTakeOwnership = */ false));
192
2.42k
    return osTmpFilename;
193
#else
194
    return std::string();
195
#endif
196
2.42k
}
197
198
/************************************************************************/
199
/*                                 Load()                               */
200
/************************************************************************/
201
202
bool GMLASConfiguration::Load(const char *pszFilename)
203
2.42k
{
204
    // Allow configuration to be inlined
205
2.42k
    CPLXMLNode *psRoot = STARTS_WITH(pszFilename, "<Configuration")
206
2.42k
                             ? CPLParseXMLString(pszFilename)
207
2.42k
                             : CPLParseXMLFile(pszFilename);
208
2.42k
    if (psRoot == nullptr)
209
0
    {
210
0
        Finalize();
211
0
        return false;
212
0
    }
213
2.42k
    CPLXMLTreeCloser oCloser(psRoot);
214
2.42k
    CPL_IGNORE_RET_VAL(oCloser);
215
216
    // Validate the configuration file
217
2.42k
    if (CPLTestBool(CPLGetConfigOption("GDAL_XML_VALIDATION", "YES")))
218
2.42k
    {
219
2.42k
#ifdef EMBED_RESOURCE_FILES
220
2.42k
        std::string osTmpFilename;
221
2.42k
        CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
222
2.42k
#endif
223
#ifdef USE_ONLY_EMBEDDED_RESOURCE_FILES
224
        const char *pszXSD = nullptr;
225
#else
226
2.42k
        const char *pszXSD = CPLFindFile("gdal", "gmlasconf.xsd");
227
2.42k
#endif
228
2.42k
#ifdef EMBED_RESOURCE_FILES
229
2.42k
        if (!pszXSD)
230
2.42k
        {
231
2.42k
            static const bool bOnce [[maybe_unused]] = []()
232
2.42k
            {
233
5
                CPLDebug("GMLAS", "Using embedded gmlasconf.xsd");
234
5
                return true;
235
5
            }();
236
2.42k
            osTmpFilename = VSIMemGenerateHiddenFilename("gmlasconf.xsd");
237
2.42k
            pszXSD = osTmpFilename.c_str();
238
2.42k
            VSIFCloseL(VSIFileFromMemBuffer(
239
2.42k
                osTmpFilename.c_str(),
240
2.42k
                const_cast<GByte *>(reinterpret_cast<const GByte *>(
241
2.42k
                    GMLASConfXSDGetFileContent())),
242
2.42k
                static_cast<int>(strlen(GMLASConfXSDGetFileContent())),
243
2.42k
                /* bTakeOwnership = */ false));
244
2.42k
        }
245
#else
246
        if (pszXSD)
247
#endif
248
2.42k
        {
249
2.42k
            std::vector<CPLString> aosErrors;
250
2.42k
            const CPLErr eErrClass = CPLGetLastErrorType();
251
2.42k
            const CPLErrorNum nErrNum = CPLGetLastErrorNo();
252
2.42k
            const CPLString osErrMsg = CPLGetLastErrorMsg();
253
2.42k
            CPLPushErrorHandlerEx(GMLASConfigurationErrorHandler, &aosErrors);
254
2.42k
            int bRet = CPLValidateXML(pszFilename, pszXSD, nullptr);
255
2.42k
            CPLPopErrorHandler();
256
2.42k
            if (!bRet && !aosErrors.empty() &&
257
2.42k
                strstr(aosErrors[0].c_str(), "missing libxml2 support") ==
258
2.42k
                    nullptr)
259
0
            {
260
0
                for (size_t i = 0; i < aosErrors.size(); i++)
261
0
                {
262
0
                    CPLError(CE_Warning, CPLE_AppDefined, "%s",
263
0
                             aosErrors[i].c_str());
264
0
                }
265
0
            }
266
2.42k
            else
267
2.42k
            {
268
2.42k
                CPLErrorSetState(eErrClass, nErrNum, osErrMsg);
269
2.42k
            }
270
2.42k
        }
271
272
2.42k
#ifdef EMBED_RESOURCE_FILES
273
2.42k
        if (!osTmpFilename.empty())
274
2.42k
            VSIUnlink(osTmpFilename.c_str());
275
2.42k
#endif
276
2.42k
    }
277
278
2.42k
    m_bAllowRemoteSchemaDownload =
279
2.42k
        CPLGetXMLBoolValue(psRoot, "=Configuration.AllowRemoteSchemaDownload",
280
2.42k
                           ALLOW_REMOTE_SCHEMA_DOWNLOAD_DEFAULT);
281
282
2.42k
    m_bAllowXSDCache = CPLGetXMLBoolValue(
283
2.42k
        psRoot, "=Configuration.SchemaCache.enabled", ALLOW_XSD_CACHE_DEFAULT);
284
2.42k
    if (m_bAllowXSDCache)
285
2.42k
    {
286
2.42k
        m_osXSDCacheDirectory =
287
2.42k
            CPLGetXMLValue(psRoot, "=Configuration.SchemaCache.Directory", "");
288
2.42k
    }
289
290
2.42k
    m_bSchemaFullChecking = CPLGetXMLBoolValue(
291
2.42k
        psRoot, "=Configuration.SchemaAnalysisOptions.SchemaFullChecking",
292
2.42k
        SCHEMA_FULL_CHECKING_DEFAULT);
293
294
2.42k
    m_bHandleMultipleImports = CPLGetXMLBoolValue(
295
2.42k
        psRoot, "=Configuration.SchemaAnalysisOptions.HandleMultipleImports",
296
2.42k
        HANDLE_MULTIPLE_IMPORTS_DEFAULT);
297
298
2.42k
    m_bValidate = CPLGetXMLBoolValue(
299
2.42k
        psRoot, "=Configuration.Validation.enabled", VALIDATE_DEFAULT);
300
301
2.42k
    if (m_bValidate)
302
0
    {
303
0
        m_bFailIfValidationError =
304
0
            CPLGetXMLBoolValue(psRoot, "=Configuration.Validation.FailIfError",
305
0
                               FAIL_IF_VALIDATION_ERROR_DEFAULT);
306
0
    }
307
308
2.42k
    m_bExposeMetadataLayers =
309
2.42k
        CPLGetXMLBoolValue(psRoot, "=Configuration.ExposeMetadataLayers",
310
2.42k
                           EXPOSE_METADATA_LAYERS_DEFAULT);
311
312
2.42k
    m_bAlwaysGenerateOGRId = CPLGetXMLBoolValue(
313
2.42k
        psRoot, "=Configuration.LayerBuildingRules.AlwaysGenerateOGRId",
314
2.42k
        ALWAYS_GENERATE_OGR_ID_DEFAULT);
315
316
2.42k
    m_bRemoveUnusedLayers = CPLGetXMLBoolValue(
317
2.42k
        psRoot, "=Configuration.LayerBuildingRules.RemoveUnusedLayers",
318
2.42k
        REMOVE_UNUSED_LAYERS_DEFAULT);
319
320
2.42k
    m_bRemoveUnusedFields = CPLGetXMLBoolValue(
321
2.42k
        psRoot, "=Configuration.LayerBuildingRules.RemoveUnusedFields",
322
2.42k
        REMOVE_UNUSED_FIELDS_DEFAULT);
323
324
2.42k
    m_bUseArrays = CPLGetXMLBoolValue(
325
2.42k
        psRoot, "=Configuration.LayerBuildingRules.UseArrays",
326
2.42k
        USE_ARRAYS_DEFAULT);
327
2.42k
    m_bUseNullState = CPLGetXMLBoolValue(
328
2.42k
        psRoot, "=Configuration.LayerBuildingRules.UseNullState",
329
2.42k
        USE_NULL_STATE_DEFAULT);
330
2.42k
    m_bIncludeGeometryXML = CPLGetXMLBoolValue(
331
2.42k
        psRoot, "=Configuration.LayerBuildingRules.GML.IncludeGeometryXML",
332
2.42k
        INCLUDE_GEOMETRY_XML_DEFAULT);
333
2.42k
    m_bInstantiateGMLFeaturesOnly = CPLGetXMLBoolValue(
334
2.42k
        psRoot,
335
2.42k
        "=Configuration.LayerBuildingRules.GML.InstantiateGMLFeaturesOnly",
336
2.42k
        INSTANTIATE_GML_FEATURES_ONLY_DEFAULT);
337
2.42k
    m_nIdentifierMaxLength = atoi(CPLGetXMLValue(
338
2.42k
        psRoot, "=Configuration.LayerBuildingRules.IdentifierMaxLength", "0"));
339
2.42k
    m_bCaseInsensitiveIdentifier = CPLGetXMLBoolValue(
340
2.42k
        psRoot, "=Configuration.LayerBuildingRules.CaseInsensitiveIdentifier",
341
2.42k
        CASE_INSENSITIVE_IDENTIFIER_DEFAULT);
342
2.42k
    m_bPGIdentifierLaundering = CPLGetXMLBoolValue(
343
2.42k
        psRoot,
344
2.42k
        "=Configuration.LayerBuildingRules.PostgreSQLIdentifierLaundering",
345
2.42k
        PG_IDENTIFIER_LAUNDERING_DEFAULT);
346
347
2.42k
    CPLXMLNode *psFlatteningRules = CPLGetXMLNode(
348
2.42k
        psRoot, "=Configuration.LayerBuildingRules.FlatteningRules");
349
2.42k
    if (psFlatteningRules)
350
2.42k
    {
351
2.42k
        m_nMaximumFieldsForFlattening = atoi(CPLGetXMLValue(
352
2.42k
            psFlatteningRules, "MaximumNumberOfFields",
353
2.42k
            CPLSPrintf("%d", MAXIMUM_FIELDS_FLATTENING_DEFAULT)));
354
355
2.42k
        ParseNamespaces(psFlatteningRules, m_oMapPrefixToURIFlatteningRules);
356
357
19.4k
        for (CPLXMLNode *psIter = psFlatteningRules->psChild; psIter != nullptr;
358
16.9k
             psIter = psIter->psNext)
359
16.9k
        {
360
16.9k
            if (psIter->eType == CXT_Element &&
361
7.28k
                EQUAL(psIter->pszValue, "ForceFlatteningXPath"))
362
2.42k
            {
363
2.42k
                m_osForcedFlattenedXPath.push_back(
364
2.42k
                    CPLGetXMLValue(psIter, "", ""));
365
2.42k
            }
366
14.5k
            else if (psIter->eType == CXT_Element &&
367
4.85k
                     EQUAL(psIter->pszValue, "DisableFlatteningXPath"))
368
0
            {
369
0
                m_osDisabledFlattenedXPath.push_back(
370
0
                    CPLGetXMLValue(psIter, "", ""));
371
0
            }
372
16.9k
        }
373
2.42k
    }
374
375
2.42k
    const char *pszSWEProcessingActivation = CPLGetXMLValue(
376
2.42k
        psRoot, "=Configuration.LayerBuildingRules.SWEProcessing.Activation",
377
2.42k
        "ifSWENamespaceFoundInTopElement");
378
2.42k
    if (EQUAL(pszSWEProcessingActivation, "ifSWENamespaceFoundInTopElement"))
379
2.42k
        m_eSWEActivationMode = SWE_ACTIVATE_IF_NAMESPACE_FOUND;
380
0
    else if (CPLTestBool(pszSWEProcessingActivation))
381
0
        m_eSWEActivationMode = SWE_ACTIVATE_TRUE;
382
0
    else
383
0
        m_eSWEActivationMode = SWE_ACTIVATE_FALSE;
384
2.42k
    m_bSWEProcessDataRecord = CPLTestBool(CPLGetXMLValue(
385
2.42k
        psRoot,
386
2.42k
        "=Configuration.LayerBuildingRules.SWEProcessing.ProcessDataRecord",
387
2.42k
        "true"));
388
2.42k
    m_bSWEProcessDataArray = CPLTestBool(CPLGetXMLValue(
389
2.42k
        psRoot,
390
2.42k
        "=Configuration.LayerBuildingRules.SWEProcessing.ProcessDataArray",
391
2.42k
        "true"));
392
393
2.42k
    CPLXMLNode *psTypingConstraints =
394
2.42k
        CPLGetXMLNode(psRoot, "=Configuration.TypingConstraints");
395
2.42k
    if (psTypingConstraints)
396
2.42k
    {
397
2.42k
        ParseNamespaces(psTypingConstraints, m_oMapPrefixToURITypeConstraints);
398
399
2.42k
        for (CPLXMLNode *psIter = psTypingConstraints->psChild;
400
7.28k
             psIter != nullptr; psIter = psIter->psNext)
401
4.85k
        {
402
4.85k
            if (psIter->eType == CXT_Element &&
403
4.85k
                EQUAL(psIter->pszValue, "ChildConstraint"))
404
2.42k
            {
405
2.42k
                const CPLString &osXPath(
406
2.42k
                    CPLGetXMLValue(psIter, "ContainerXPath", ""));
407
2.42k
                CPLXMLNode *psChildrenTypes =
408
2.42k
                    CPLGetXMLNode(psIter, "ChildrenElements");
409
2.42k
                if (IsValidXPath(osXPath))
410
2.42k
                {
411
2.42k
                    for (CPLXMLNode *psIter2 = psChildrenTypes
412
2.42k
                                                   ? psChildrenTypes->psChild
413
2.42k
                                                   : nullptr;
414
4.85k
                         psIter2 != nullptr; psIter2 = psIter2->psNext)
415
2.42k
                    {
416
2.42k
                        if (psIter2->eType == CXT_Element &&
417
2.42k
                            EQUAL(psIter2->pszValue, "Element"))
418
2.42k
                        {
419
2.42k
                            m_oMapChildrenElementsConstraints[osXPath]
420
2.42k
                                .push_back(CPLGetXMLValue(psIter2, "", ""));
421
2.42k
                        }
422
2.42k
                    }
423
2.42k
                }
424
0
                else
425
0
                {
426
0
                    CPLError(CE_Warning, CPLE_AppDefined,
427
0
                             "XPath syntax %s not supported", osXPath.c_str());
428
0
                }
429
2.42k
            }
430
4.85k
        }
431
2.42k
    }
432
433
2.42k
    CPLXMLNode *psIgnoredXPaths =
434
2.42k
        CPLGetXMLNode(psRoot, "=Configuration.IgnoredXPaths");
435
2.42k
    if (psIgnoredXPaths)
436
2.42k
    {
437
2.42k
        const bool bGlobalWarnIfIgnoredXPathFound = CPLGetXMLBoolValue(
438
2.42k
            psIgnoredXPaths, "WarnIfIgnoredXPathFoundInDocInstance",
439
2.42k
            WARN_IF_EXCLUDED_XPATH_FOUND_DEFAULT);
440
441
2.42k
        ParseNamespaces(psIgnoredXPaths, m_oMapPrefixToURIIgnoredXPaths);
442
443
65.5k
        for (CPLXMLNode *psIter = psIgnoredXPaths->psChild; psIter != nullptr;
444
63.1k
             psIter = psIter->psNext)
445
63.1k
        {
446
63.1k
            if (psIter->eType == CXT_Element &&
447
60.7k
                EQUAL(psIter->pszValue, "XPath"))
448
55.8k
            {
449
55.8k
                const CPLString &osXPath(CPLGetXMLValue(psIter, "", ""));
450
55.8k
                if (IsValidXPath(osXPath))
451
55.8k
                {
452
55.8k
                    m_aosIgnoredXPaths.push_back(osXPath);
453
454
55.8k
                    const bool bWarnIfIgnoredXPathFound = CPLGetXMLBoolValue(
455
55.8k
                        psIter, "warnIfIgnoredXPathFoundInDocInstance",
456
55.8k
                        bGlobalWarnIfIgnoredXPathFound);
457
55.8k
                    m_oMapIgnoredXPathToWarn[osXPath] =
458
55.8k
                        bWarnIfIgnoredXPathFound;
459
55.8k
                }
460
0
                else
461
0
                {
462
0
                    CPLError(CE_Warning, CPLE_AppDefined,
463
0
                             "XPath syntax %s not supported", osXPath.c_str());
464
0
                }
465
55.8k
            }
466
63.1k
        }
467
2.42k
    }
468
469
2.42k
    CPLXMLNode *psXLinkResolutionNode =
470
2.42k
        CPLGetXMLNode(psRoot, "=Configuration.XLinkResolution");
471
2.42k
    if (psXLinkResolutionNode != nullptr)
472
2.42k
        m_oXLinkResolution.LoadFromXML(psXLinkResolutionNode);
473
474
    // Parse WriterConfig
475
2.42k
    CPLXMLNode *psWriterConfig =
476
2.42k
        CPLGetXMLNode(psRoot, "=Configuration.WriterConfig");
477
2.42k
    if (psWriterConfig != nullptr)
478
2.42k
    {
479
2.42k
        m_nIndentSize =
480
2.42k
            atoi(CPLGetXMLValue(psWriterConfig, "IndentationSize",
481
2.42k
                                CPLSPrintf("%d", INDENT_SIZE_DEFAULT)));
482
2.42k
        m_nIndentSize =
483
2.42k
            std::min(INDENT_SIZE_MAX, std::max(INDENT_SIZE_MIN, m_nIndentSize));
484
485
2.42k
        m_osComment = CPLGetXMLValue(psWriterConfig, "Comment", "");
486
487
2.42k
        m_osLineFormat = CPLGetXMLValue(psWriterConfig, "LineFormat", "");
488
489
2.42k
        m_osSRSNameFormat = CPLGetXMLValue(psWriterConfig, "SRSNameFormat", "");
490
491
2.42k
        m_osWrapping = CPLGetXMLValue(psWriterConfig, "Wrapping",
492
2.42k
                                      szWFS2_FEATURECOLLECTION);
493
494
2.42k
        m_osTimestamp = CPLGetXMLValue(psWriterConfig, "Timestamp", "");
495
496
2.42k
        m_osWFS20SchemaLocation = CPLGetXMLValue(
497
2.42k
            psWriterConfig, "WFS20SchemaLocation", szWFS20_SCHEMALOCATION);
498
2.42k
    }
499
500
2.42k
    Finalize();
501
502
2.42k
    return true;
503
2.42k
}
504
505
/************************************************************************/
506
/*                               LoadFromXML()                          */
507
/************************************************************************/
508
509
bool GMLASXLinkResolutionConf::LoadFromXML(CPLXMLNode *psRoot)
510
2.42k
{
511
2.42k
    m_nTimeOut = atoi(CPLGetXMLValue(psRoot, "Timeout", "0"));
512
513
2.42k
    m_nMaxFileSize = atoi(CPLGetXMLValue(
514
2.42k
        psRoot, "MaxFileSize", CPLSPrintf("%d", MAX_FILE_SIZE_DEFAULT)));
515
516
2.42k
    m_nMaxGlobalResolutionTime =
517
2.42k
        atoi(CPLGetXMLValue(psRoot, "MaxGlobalResolutionTime", "0"));
518
519
2.42k
    m_osProxyServerPort = CPLGetXMLValue(psRoot, "ProxyServerPort", "");
520
2.42k
    m_osProxyUserPassword = CPLGetXMLValue(psRoot, "ProxyUserPassword", "");
521
2.42k
    m_osProxyAuth = CPLGetXMLValue(psRoot, "ProxyAuth", "");
522
523
2.42k
    m_osCacheDirectory = CPLGetXMLValue(psRoot, "CacheDirectory", "");
524
2.42k
    if (m_osCacheDirectory.empty())
525
2.42k
    {
526
2.42k
        m_osCacheDirectory = GDALGetCacheDirectory();
527
2.42k
        if (!m_osCacheDirectory.empty())
528
2.42k
        {
529
2.42k
            m_osCacheDirectory = CPLFormFilenameSafe(
530
2.42k
                m_osCacheDirectory, "xlink_resolved_cache", nullptr);
531
2.42k
        }
532
2.42k
    }
533
534
2.42k
    m_bDefaultResolutionEnabled =
535
2.42k
        CPLGetXMLBoolValue(psRoot, "DefaultResolution.enabled",
536
2.42k
                           DEFAULT_RESOLUTION_ENABLED_DEFAULT);
537
538
2.42k
    m_bDefaultAllowRemoteDownload =
539
2.42k
        CPLGetXMLBoolValue(psRoot, "DefaultResolution.AllowRemoteDownload",
540
2.42k
                           ALLOW_REMOTE_DOWNLOAD_DEFAULT);
541
542
    // TODO when we support other modes
543
    // m_eDefaultResolutionMode =
544
545
2.42k
    m_nDefaultResolutionDepth =
546
2.42k
        atoi(CPLGetXMLValue(psRoot, "DefaultResolution.ResolutionDepth", "1"));
547
548
2.42k
    m_bDefaultCacheResults = CPLGetXMLBoolValue(
549
2.42k
        psRoot, "DefaultResolution.CacheResults", CACHE_RESULTS_DEFAULT);
550
551
2.42k
    CPLXMLNode *psIterURL = psRoot->psChild;
552
26.7k
    for (; psIterURL != nullptr; psIterURL = psIterURL->psNext)
553
24.2k
    {
554
24.2k
        if (psIterURL->eType == CXT_Element &&
555
12.1k
            strcmp(psIterURL->pszValue, "URLSpecificResolution") == 0)
556
0
        {
557
0
            GMLASXLinkResolutionConf::URLSpecificResolution oItem;
558
0
            oItem.m_osURLPrefix = CPLGetXMLValue(psIterURL, "URLPrefix", "");
559
560
0
            oItem.m_bAllowRemoteDownload =
561
0
                CPLGetXMLBoolValue(psIterURL, "AllowRemoteDownload",
562
0
                                   ALLOW_REMOTE_DOWNLOAD_DEFAULT);
563
564
0
            const char *pszResolutionModel =
565
0
                CPLGetXMLValue(psIterURL, "ResolutionMode", "RawContent");
566
0
            if (EQUAL(pszResolutionModel, "RawContent"))
567
0
                oItem.m_eResolutionMode = RawContent;
568
0
            else
569
0
                oItem.m_eResolutionMode = FieldsFromXPath;
570
571
0
            oItem.m_nResolutionDepth =
572
0
                atoi(CPLGetXMLValue(psIterURL, "ResolutionDepth", "1"));
573
574
0
            oItem.m_bCacheResults = CPLGetXMLBoolValue(
575
0
                psIterURL, "CacheResults", CACHE_RESULTS_DEFAULT);
576
577
0
            CPLXMLNode *psIter = psIterURL->psChild;
578
0
            for (; psIter != nullptr; psIter = psIter->psNext)
579
0
            {
580
0
                if (psIter->eType == CXT_Element &&
581
0
                    strcmp(psIter->pszValue, "HTTPHeader") == 0)
582
0
                {
583
0
                    CPLString osName(CPLGetXMLValue(psIter, "Name", ""));
584
0
                    CPLString osValue(CPLGetXMLValue(psIter, "Value", ""));
585
0
                    oItem.m_aosNameValueHTTPHeaders.push_back(
586
0
                        std::pair<CPLString, CPLString>(osName, osValue));
587
0
                }
588
0
                else if (psIter->eType == CXT_Element &&
589
0
                         strcmp(psIter->pszValue, "Field") == 0)
590
0
                {
591
0
                    URLSpecificResolution::XPathDerivedField oField;
592
0
                    oField.m_osName = CPLGetXMLValue(psIter, "Name", "");
593
0
                    oField.m_osType = CPLGetXMLValue(psIter, "Type", "");
594
0
                    oField.m_osXPath = CPLGetXMLValue(psIter, "XPath", "");
595
0
                    oItem.m_aoFields.push_back(std::move(oField));
596
0
                }
597
0
            }
598
599
0
            m_aoURLSpecificRules.push_back(std::move(oItem));
600
0
        }
601
24.2k
    }
602
603
2.42k
    m_bResolveInternalXLinks = CPLGetXMLBoolValue(
604
2.42k
        psRoot, "ResolveInternalXLinks", INTERNAL_XLINK_RESOLUTION_DEFAULT);
605
606
2.42k
    return true;
607
2.42k
}