Coverage Report

Created: 2026-11-12 08:28

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
1.31k
{
31
1.31k
    if (m_bAllowXSDCache && m_osXSDCacheDirectory.empty())
32
1.31k
    {
33
1.31k
        m_osXSDCacheDirectory = GDALGetCacheDirectory();
34
1.31k
        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
1.31k
        else
40
1.31k
        {
41
1.31k
            m_osXSDCacheDirectory = CPLFormFilenameSafe(
42
1.31k
                m_osXSDCacheDirectory, "gmlas_xsd_cache", nullptr);
43
1.31k
            CPLDebug("GMLAS", "XSD cache directory: %s",
44
1.31k
                     m_osXSDCacheDirectory.c_str());
45
1.31k
        }
46
1.31k
    }
47
1.31k
}
48
49
/************************************************************************/
50
/*                         CPLGetXMLBoolValue()                         */
51
/************************************************************************/
52
53
static bool CPLGetXMLBoolValue(CPLXMLNode *psNode, const char *pszKey,
54
                               bool bDefault)
55
56.5k
{
56
56.5k
    const char *pszVal = CPLGetXMLValue(psNode, pszKey, nullptr);
57
56.5k
    if (pszVal)
58
28.9k
        return CPLTestBool(pszVal);
59
27.6k
    else
60
27.6k
        return bDefault;
61
56.5k
}
62
63
/************************************************************************/
64
/*                            IsValidXPath()                            */
65
/************************************************************************/
66
67
static bool IsValidXPath(const CPLString &osXPath)
68
31.5k
{
69
    // Check that the XPath syntax belongs to the subset we
70
    // understand
71
31.5k
    bool bOK = !osXPath.empty();
72
676k
    for (size_t i = 0; i < osXPath.size(); ++i)
73
644k
    {
74
644k
        const char chCur = osXPath[i];
75
644k
        if (chCur == '/')
76
17.1k
        {
77
            // OK
78
17.1k
        }
79
627k
        else if (chCur == '@' && (i == 0 || osXPath[i - 1] == '/') &&
80
17.1k
                 i < osXPath.size() - 1 &&
81
17.1k
                 isalpha(static_cast<unsigned char>(osXPath[i + 1])))
82
17.1k
        {
83
            // OK
84
17.1k
        }
85
610k
        else if (chCur == '_' || isalpha(static_cast<unsigned char>(chCur)))
86
557k
        {
87
            // OK
88
557k
        }
89
52.6k
        else if (isdigit(static_cast<unsigned char>(chCur)) && i > 0 &&
90
11.8k
                 (isalnum(static_cast<unsigned char>(osXPath[i - 1])) ||
91
0
                  osXPath[i - 1] == '_'))
92
11.8k
        {
93
            // OK
94
11.8k
        }
95
40.7k
        else if (chCur == ':' && i > 0 &&
96
40.7k
                 (isalnum(static_cast<unsigned char>(osXPath[i - 1])) ||
97
0
                  osXPath[i - 1] == '_') &&
98
40.7k
                 i < osXPath.size() - 1 &&
99
40.7k
                 isalpha(static_cast<unsigned char>(osXPath[i + 1])))
100
40.7k
        {
101
            // OK
102
40.7k
        }
103
0
        else
104
0
        {
105
0
            bOK = false;
106
0
            break;
107
0
        }
108
644k
    }
109
31.5k
    return bOK;
110
31.5k
}
111
112
/************************************************************************/
113
/*                   GMLASConfigurationErrorHandler()                   */
114
/************************************************************************/
115
116
static void CPL_STDCALL GMLASConfigurationErrorHandler(CPLErr /*eErr*/,
117
                                                       CPLErrorNum /*nType*/,
118
                                                       const char *pszMsg)
119
1.31k
{
120
1.31k
    std::vector<CPLString> *paosErrors =
121
1.31k
        static_cast<std::vector<CPLString> *>(CPLGetErrorHandlerUserData());
122
1.31k
    paosErrors->push_back(pszMsg);
123
1.31k
}
124
125
/************************************************************************/
126
/*                          ParseNamespaces()                           */
127
/************************************************************************/
128
129
static void ParseNamespaces(CPLXMLNode *psContainerNode,
130
                            std::map<CPLString, CPLString> &oMap)
131
3.94k
{
132
3.94k
    CPLXMLNode *psNamespaces = CPLGetXMLNode(psContainerNode, "Namespaces");
133
3.94k
    if (psNamespaces != nullptr)
134
3.94k
    {
135
11.8k
        for (CPLXMLNode *psIter = psNamespaces->psChild; psIter != nullptr;
136
7.89k
             psIter = psIter->psNext)
137
7.89k
        {
138
7.89k
            if (psIter->eType == CXT_Element &&
139
7.89k
                EQUAL(psIter->pszValue, "Namespace"))
140
7.89k
            {
141
7.89k
                const std::string osPrefix =
142
7.89k
                    CPLGetXMLValue(psIter, "prefix", "");
143
7.89k
                const std::string osURI = CPLGetXMLValue(psIter, "uri", "");
144
7.89k
                if (!osPrefix.empty() && !osURI.empty())
145
7.89k
                {
146
7.89k
                    if (oMap.find(osPrefix) == oMap.end())
147
7.89k
                    {
148
7.89k
                        oMap[osPrefix] = osURI;
149
7.89k
                    }
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
7.89k
                }
159
7.89k
            }
160
7.89k
        }
161
3.94k
    }
162
3.94k
}
163
164
/************************************************************************/
165
/*                         GetDefaultConfFile()                         */
166
/************************************************************************/
167
168
/* static */
169
std::string GMLASConfiguration::GetDefaultConfFile(bool &bUnlinkAfterUse)
170
1.31k
{
171
1.31k
    bUnlinkAfterUse = false;
172
1.31k
#if !defined(USE_ONLY_EMBEDDED_RESOURCE_FILES)
173
1.31k
    const char *pszConfigFile = CPLFindFile("gdal", szDEFAULT_CONF_FILENAME);
174
1.31k
    if (pszConfigFile)
175
0
        return pszConfigFile;
176
1.31k
#endif
177
1.31k
#ifdef EMBED_RESOURCE_FILES
178
1.31k
    static const bool bOnce [[maybe_unused]] = []()
179
1.31k
    {
180
6
        CPLDebug("GMLAS", "Using embedded %s", szDEFAULT_CONF_FILENAME);
181
6
        return true;
182
6
    }();
183
1.31k
    bUnlinkAfterUse = true;
184
1.31k
    const std::string osTmpFilename =
185
1.31k
        VSIMemGenerateHiddenFilename(szDEFAULT_CONF_FILENAME);
186
1.31k
    VSIFCloseL(VSIFileFromMemBuffer(
187
1.31k
        osTmpFilename.c_str(),
188
1.31k
        const_cast<GByte *>(
189
1.31k
            reinterpret_cast<const GByte *>(GMLASConfXMLGetFileContent())),
190
1.31k
        static_cast<int>(strlen(GMLASConfXMLGetFileContent())),
191
1.31k
        /* bTakeOwnership = */ false));
192
1.31k
    return osTmpFilename;
193
#else
194
    return std::string();
195
#endif
196
1.31k
}
197
198
/************************************************************************/
199
/*                                Load()                                */
200
/************************************************************************/
201
202
bool GMLASConfiguration::Load(const char *pszFilename)
203
1.31k
{
204
    // Allow configuration to be inlined
205
1.31k
    CPLXMLNode *psRoot = STARTS_WITH(pszFilename, "<Configuration")
206
1.31k
                             ? CPLParseXMLString(pszFilename)
207
1.31k
                             : CPLParseXMLFile(pszFilename);
208
1.31k
    if (psRoot == nullptr)
209
0
    {
210
0
        Finalize();
211
0
        return false;
212
0
    }
213
1.31k
    CPLXMLTreeCloser oCloser(psRoot);
214
1.31k
    CPL_IGNORE_RET_VAL(oCloser);
215
216
    // Validate the configuration file
217
1.31k
    if (CPLTestBool(CPLGetConfigOption("GDAL_XML_VALIDATION", "YES")))
218
1.31k
    {
219
1.31k
#ifdef EMBED_RESOURCE_FILES
220
1.31k
        std::string osTmpFilename;
221
1.31k
        CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
222
1.31k
#endif
223
#ifdef USE_ONLY_EMBEDDED_RESOURCE_FILES
224
        const char *pszXSD = nullptr;
225
#else
226
1.31k
        const char *pszXSD = CPLFindFile("gdal", "gmlasconf.xsd");
227
1.31k
#endif
228
1.31k
#ifdef EMBED_RESOURCE_FILES
229
1.31k
        if (!pszXSD)
230
1.31k
        {
231
1.31k
            static const bool bOnce [[maybe_unused]] = []()
232
1.31k
            {
233
6
                CPLDebug("GMLAS", "Using embedded gmlasconf.xsd");
234
6
                return true;
235
6
            }();
236
1.31k
            osTmpFilename = VSIMemGenerateHiddenFilename("gmlasconf.xsd");
237
1.31k
            pszXSD = osTmpFilename.c_str();
238
1.31k
            VSIFCloseL(VSIFileFromMemBuffer(
239
1.31k
                osTmpFilename.c_str(),
240
1.31k
                const_cast<GByte *>(reinterpret_cast<const GByte *>(
241
1.31k
                    GMLASConfXSDGetFileContent())),
242
1.31k
                static_cast<int>(strlen(GMLASConfXSDGetFileContent())),
243
1.31k
                /* bTakeOwnership = */ false));
244
1.31k
        }
245
#else
246
        if (pszXSD)
247
#endif
248
1.31k
        {
249
1.31k
            std::vector<CPLString> aosErrors;
250
1.31k
            const CPLErr eErrClass = CPLGetLastErrorType();
251
1.31k
            const CPLErrorNum nErrNum = CPLGetLastErrorNo();
252
1.31k
            const CPLString osErrMsg = CPLGetLastErrorMsg();
253
1.31k
            CPLPushErrorHandlerEx(GMLASConfigurationErrorHandler, &aosErrors);
254
1.31k
            int bRet = CPLValidateXML(pszFilename, pszXSD, nullptr);
255
1.31k
            CPLPopErrorHandler();
256
1.31k
            if (!bRet && !aosErrors.empty() &&
257
1.31k
                strstr(aosErrors[0].c_str(), "missing libxml2 support") ==
258
1.31k
                    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
1.31k
            else
267
1.31k
            {
268
1.31k
                CPLErrorSetState(eErrClass, nErrNum, osErrMsg);
269
1.31k
            }
270
1.31k
        }
271
272
1.31k
#ifdef EMBED_RESOURCE_FILES
273
1.31k
        if (!osTmpFilename.empty())
274
1.31k
            VSIUnlink(osTmpFilename.c_str());
275
1.31k
#endif
276
1.31k
    }
277
278
1.31k
    m_bAllowRemoteSchemaDownload =
279
1.31k
        CPLGetXMLBoolValue(psRoot, "=Configuration.AllowRemoteSchemaDownload",
280
1.31k
                           ALLOW_REMOTE_SCHEMA_DOWNLOAD_DEFAULT);
281
282
1.31k
    m_bAllowXSDCache = CPLGetXMLBoolValue(
283
1.31k
        psRoot, "=Configuration.SchemaCache.enabled", ALLOW_XSD_CACHE_DEFAULT);
284
1.31k
    if (m_bAllowXSDCache)
285
1.31k
    {
286
1.31k
        m_osXSDCacheDirectory =
287
1.31k
            CPLGetXMLValue(psRoot, "=Configuration.SchemaCache.Directory", "");
288
1.31k
    }
289
290
1.31k
    m_bSchemaFullChecking = CPLGetXMLBoolValue(
291
1.31k
        psRoot, "=Configuration.SchemaAnalysisOptions.SchemaFullChecking",
292
1.31k
        SCHEMA_FULL_CHECKING_DEFAULT);
293
294
1.31k
    m_bHandleMultipleImports = CPLGetXMLBoolValue(
295
1.31k
        psRoot, "=Configuration.SchemaAnalysisOptions.HandleMultipleImports",
296
1.31k
        HANDLE_MULTIPLE_IMPORTS_DEFAULT);
297
298
1.31k
    m_bValidate = CPLGetXMLBoolValue(
299
1.31k
        psRoot, "=Configuration.Validation.enabled", VALIDATE_DEFAULT);
300
301
1.31k
    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
1.31k
    m_bExposeMetadataLayers =
309
1.31k
        CPLGetXMLBoolValue(psRoot, "=Configuration.ExposeMetadataLayers",
310
1.31k
                           EXPOSE_METADATA_LAYERS_DEFAULT);
311
312
1.31k
    m_bAlwaysGenerateOGRId = CPLGetXMLBoolValue(
313
1.31k
        psRoot, "=Configuration.LayerBuildingRules.AlwaysGenerateOGRId",
314
1.31k
        ALWAYS_GENERATE_OGR_ID_DEFAULT);
315
316
1.31k
    m_bRemoveUnusedLayers = CPLGetXMLBoolValue(
317
1.31k
        psRoot, "=Configuration.LayerBuildingRules.RemoveUnusedLayers",
318
1.31k
        REMOVE_UNUSED_LAYERS_DEFAULT);
319
320
1.31k
    m_bRemoveUnusedFields = CPLGetXMLBoolValue(
321
1.31k
        psRoot, "=Configuration.LayerBuildingRules.RemoveUnusedFields",
322
1.31k
        REMOVE_UNUSED_FIELDS_DEFAULT);
323
324
1.31k
    m_bUseArrays = CPLGetXMLBoolValue(
325
1.31k
        psRoot, "=Configuration.LayerBuildingRules.UseArrays",
326
1.31k
        USE_ARRAYS_DEFAULT);
327
1.31k
    m_bUseNullState = CPLGetXMLBoolValue(
328
1.31k
        psRoot, "=Configuration.LayerBuildingRules.UseNullState",
329
1.31k
        USE_NULL_STATE_DEFAULT);
330
1.31k
    m_bIncludeGeometryXML = CPLGetXMLBoolValue(
331
1.31k
        psRoot, "=Configuration.LayerBuildingRules.GML.IncludeGeometryXML",
332
1.31k
        INCLUDE_GEOMETRY_XML_DEFAULT);
333
1.31k
    m_bInstantiateGMLFeaturesOnly = CPLGetXMLBoolValue(
334
1.31k
        psRoot,
335
1.31k
        "=Configuration.LayerBuildingRules.GML.InstantiateGMLFeaturesOnly",
336
1.31k
        INSTANTIATE_GML_FEATURES_ONLY_DEFAULT);
337
1.31k
    m_nIdentifierMaxLength = atoi(CPLGetXMLValue(
338
1.31k
        psRoot, "=Configuration.LayerBuildingRules.IdentifierMaxLength", "0"));
339
1.31k
    m_bCaseInsensitiveIdentifier = CPLGetXMLBoolValue(
340
1.31k
        psRoot, "=Configuration.LayerBuildingRules.CaseInsensitiveIdentifier",
341
1.31k
        CASE_INSENSITIVE_IDENTIFIER_DEFAULT);
342
1.31k
    m_bPGIdentifierLaundering = CPLGetXMLBoolValue(
343
1.31k
        psRoot,
344
1.31k
        "=Configuration.LayerBuildingRules.PostgreSQLIdentifierLaundering",
345
1.31k
        PG_IDENTIFIER_LAUNDERING_DEFAULT);
346
347
1.31k
    CPLXMLNode *psFlatteningRules = CPLGetXMLNode(
348
1.31k
        psRoot, "=Configuration.LayerBuildingRules.FlatteningRules");
349
1.31k
    if (psFlatteningRules)
350
1.31k
    {
351
1.31k
        m_nMaximumFieldsForFlattening = atoi(CPLGetXMLValue(
352
1.31k
            psFlatteningRules, "MaximumNumberOfFields",
353
1.31k
            CPLSPrintf("%d", MAXIMUM_FIELDS_FLATTENING_DEFAULT)));
354
355
1.31k
        ParseNamespaces(psFlatteningRules, m_oMapPrefixToURIFlatteningRules);
356
357
10.5k
        for (CPLXMLNode *psIter = psFlatteningRules->psChild; psIter != nullptr;
358
9.21k
             psIter = psIter->psNext)
359
9.21k
        {
360
9.21k
            if (psIter->eType == CXT_Element &&
361
3.94k
                EQUAL(psIter->pszValue, "ForceFlatteningXPath"))
362
1.31k
            {
363
1.31k
                m_osForcedFlattenedXPath.push_back(
364
1.31k
                    CPLGetXMLValue(psIter, "", ""));
365
1.31k
            }
366
7.89k
            else if (psIter->eType == CXT_Element &&
367
2.63k
                     EQUAL(psIter->pszValue, "DisableFlatteningXPath"))
368
0
            {
369
0
                m_osDisabledFlattenedXPath.push_back(
370
0
                    CPLGetXMLValue(psIter, "", ""));
371
0
            }
372
9.21k
        }
373
1.31k
    }
374
375
1.31k
    const char *pszSWEProcessingActivation = CPLGetXMLValue(
376
1.31k
        psRoot, "=Configuration.LayerBuildingRules.SWEProcessing.Activation",
377
1.31k
        "ifSWENamespaceFoundInTopElement");
378
1.31k
    if (EQUAL(pszSWEProcessingActivation, "ifSWENamespaceFoundInTopElement"))
379
1.31k
        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
1.31k
    m_bSWEProcessDataRecord = CPLTestBool(CPLGetXMLValue(
385
1.31k
        psRoot,
386
1.31k
        "=Configuration.LayerBuildingRules.SWEProcessing.ProcessDataRecord",
387
1.31k
        "true"));
388
1.31k
    m_bSWEProcessDataArray = CPLTestBool(CPLGetXMLValue(
389
1.31k
        psRoot,
390
1.31k
        "=Configuration.LayerBuildingRules.SWEProcessing.ProcessDataArray",
391
1.31k
        "true"));
392
393
1.31k
    CPLXMLNode *psTypingConstraints =
394
1.31k
        CPLGetXMLNode(psRoot, "=Configuration.TypingConstraints");
395
1.31k
    if (psTypingConstraints)
396
1.31k
    {
397
1.31k
        ParseNamespaces(psTypingConstraints, m_oMapPrefixToURITypeConstraints);
398
399
1.31k
        for (CPLXMLNode *psIter = psTypingConstraints->psChild;
400
3.94k
             psIter != nullptr; psIter = psIter->psNext)
401
2.63k
        {
402
2.63k
            if (psIter->eType == CXT_Element &&
403
2.63k
                EQUAL(psIter->pszValue, "ChildConstraint"))
404
1.31k
            {
405
1.31k
                const CPLString &osXPath(
406
1.31k
                    CPLGetXMLValue(psIter, "ContainerXPath", ""));
407
1.31k
                CPLXMLNode *psChildrenTypes =
408
1.31k
                    CPLGetXMLNode(psIter, "ChildrenElements");
409
1.31k
                if (IsValidXPath(osXPath))
410
1.31k
                {
411
1.31k
                    for (CPLXMLNode *psIter2 = psChildrenTypes
412
1.31k
                                                   ? psChildrenTypes->psChild
413
1.31k
                                                   : nullptr;
414
2.63k
                         psIter2 != nullptr; psIter2 = psIter2->psNext)
415
1.31k
                    {
416
1.31k
                        if (psIter2->eType == CXT_Element &&
417
1.31k
                            EQUAL(psIter2->pszValue, "Element"))
418
1.31k
                        {
419
1.31k
                            m_oMapChildrenElementsConstraints[osXPath]
420
1.31k
                                .push_back(CPLGetXMLValue(psIter2, "", ""));
421
1.31k
                        }
422
1.31k
                    }
423
1.31k
                }
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
1.31k
            }
430
2.63k
        }
431
1.31k
    }
432
433
1.31k
    CPLXMLNode *psIgnoredXPaths =
434
1.31k
        CPLGetXMLNode(psRoot, "=Configuration.IgnoredXPaths");
435
1.31k
    if (psIgnoredXPaths)
436
1.31k
    {
437
1.31k
        const bool bGlobalWarnIfIgnoredXPathFound = CPLGetXMLBoolValue(
438
1.31k
            psIgnoredXPaths, "WarnIfIgnoredXPathFoundInDocInstance",
439
1.31k
            WARN_IF_EXCLUDED_XPATH_FOUND_DEFAULT);
440
441
1.31k
        ParseNamespaces(psIgnoredXPaths, m_oMapPrefixToURIIgnoredXPaths);
442
443
35.5k
        for (CPLXMLNode *psIter = psIgnoredXPaths->psChild; psIter != nullptr;
444
34.2k
             psIter = psIter->psNext)
445
34.2k
        {
446
34.2k
            if (psIter->eType == CXT_Element &&
447
32.9k
                EQUAL(psIter->pszValue, "XPath"))
448
30.2k
            {
449
30.2k
                const CPLString &osXPath(CPLGetXMLValue(psIter, "", ""));
450
30.2k
                if (IsValidXPath(osXPath))
451
30.2k
                {
452
30.2k
                    m_aosIgnoredXPaths.push_back(osXPath);
453
454
30.2k
                    const bool bWarnIfIgnoredXPathFound = CPLGetXMLBoolValue(
455
30.2k
                        psIter, "warnIfIgnoredXPathFoundInDocInstance",
456
30.2k
                        bGlobalWarnIfIgnoredXPathFound);
457
30.2k
                    m_oMapIgnoredXPathToWarn[osXPath] =
458
30.2k
                        bWarnIfIgnoredXPathFound;
459
30.2k
                }
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
30.2k
            }
466
34.2k
        }
467
1.31k
    }
468
469
1.31k
    CPLXMLNode *psXLinkResolutionNode =
470
1.31k
        CPLGetXMLNode(psRoot, "=Configuration.XLinkResolution");
471
1.31k
    if (psXLinkResolutionNode != nullptr)
472
1.31k
        m_oXLinkResolution.LoadFromXML(psXLinkResolutionNode);
473
474
    // Parse WriterConfig
475
1.31k
    CPLXMLNode *psWriterConfig =
476
1.31k
        CPLGetXMLNode(psRoot, "=Configuration.WriterConfig");
477
1.31k
    if (psWriterConfig != nullptr)
478
1.31k
    {
479
1.31k
        m_nIndentSize =
480
1.31k
            atoi(CPLGetXMLValue(psWriterConfig, "IndentationSize",
481
1.31k
                                CPLSPrintf("%d", INDENT_SIZE_DEFAULT)));
482
1.31k
        m_nIndentSize =
483
1.31k
            std::min(INDENT_SIZE_MAX, std::max(INDENT_SIZE_MIN, m_nIndentSize));
484
485
1.31k
        m_osComment = CPLGetXMLValue(psWriterConfig, "Comment", "");
486
487
1.31k
        m_osLineFormat = CPLGetXMLValue(psWriterConfig, "LineFormat", "");
488
489
1.31k
        m_osSRSNameFormat = CPLGetXMLValue(psWriterConfig, "SRSNameFormat", "");
490
491
1.31k
        m_osWrapping = CPLGetXMLValue(psWriterConfig, "Wrapping",
492
1.31k
                                      szWFS2_FEATURECOLLECTION);
493
494
1.31k
        m_osTimestamp = CPLGetXMLValue(psWriterConfig, "Timestamp", "");
495
496
1.31k
        m_osWFS20SchemaLocation = CPLGetXMLValue(
497
1.31k
            psWriterConfig, "WFS20SchemaLocation", szWFS20_SCHEMALOCATION);
498
1.31k
    }
499
500
1.31k
    Finalize();
501
502
1.31k
    return true;
503
1.31k
}
504
505
/************************************************************************/
506
/*                            LoadFromXML()                             */
507
/************************************************************************/
508
509
bool GMLASXLinkResolutionConf::LoadFromXML(CPLXMLNode *psRoot)
510
1.31k
{
511
1.31k
    m_nTimeOut = atoi(CPLGetXMLValue(psRoot, "Timeout", "0"));
512
513
1.31k
    m_nMaxFileSize = atoi(CPLGetXMLValue(
514
1.31k
        psRoot, "MaxFileSize", CPLSPrintf("%d", MAX_FILE_SIZE_DEFAULT)));
515
516
1.31k
    m_nMaxGlobalResolutionTime =
517
1.31k
        atoi(CPLGetXMLValue(psRoot, "MaxGlobalResolutionTime", "0"));
518
519
1.31k
    m_osProxyServerPort = CPLGetXMLValue(psRoot, "ProxyServerPort", "");
520
1.31k
    m_osProxyUserPassword = CPLGetXMLValue(psRoot, "ProxyUserPassword", "");
521
1.31k
    m_osProxyAuth = CPLGetXMLValue(psRoot, "ProxyAuth", "");
522
523
1.31k
    m_osCacheDirectory = CPLGetXMLValue(psRoot, "CacheDirectory", "");
524
1.31k
    if (m_osCacheDirectory.empty())
525
1.31k
    {
526
1.31k
        m_osCacheDirectory = GDALGetCacheDirectory();
527
1.31k
        if (!m_osCacheDirectory.empty())
528
1.31k
        {
529
1.31k
            m_osCacheDirectory = CPLFormFilenameSafe(
530
1.31k
                m_osCacheDirectory, "xlink_resolved_cache", nullptr);
531
1.31k
        }
532
1.31k
    }
533
534
1.31k
    m_bDefaultResolutionEnabled =
535
1.31k
        CPLGetXMLBoolValue(psRoot, "DefaultResolution.enabled",
536
1.31k
                           DEFAULT_RESOLUTION_ENABLED_DEFAULT);
537
538
1.31k
    m_bDefaultAllowRemoteDownload =
539
1.31k
        CPLGetXMLBoolValue(psRoot, "DefaultResolution.AllowRemoteDownload",
540
1.31k
                           ALLOW_REMOTE_DOWNLOAD_DEFAULT);
541
542
    // TODO when we support other modes
543
    // m_eDefaultResolutionMode =
544
545
1.31k
    m_nDefaultResolutionDepth =
546
1.31k
        atoi(CPLGetXMLValue(psRoot, "DefaultResolution.ResolutionDepth", "1"));
547
548
1.31k
    m_bDefaultCacheResults = CPLGetXMLBoolValue(
549
1.31k
        psRoot, "DefaultResolution.CacheResults", CACHE_RESULTS_DEFAULT);
550
551
1.31k
    CPLXMLNode *psIterURL = psRoot->psChild;
552
14.4k
    for (; psIterURL != nullptr; psIterURL = psIterURL->psNext)
553
13.1k
    {
554
13.1k
        if (psIterURL->eType == CXT_Element &&
555
6.58k
            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
13.1k
    }
602
603
1.31k
    m_bResolveInternalXLinks = CPLGetXMLBoolValue(
604
1.31k
        psRoot, "ResolveInternalXLinks", INTERNAL_XLINK_RESOLUTION_DEFAULT);
605
606
1.31k
    return true;
607
1.31k
}