LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gmlas - ogrgmlasconf.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 216 243 88.9 %
Date: 2024-11-21 22:18:42 Functions: 8 9 88.9 %

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

Generated by: LCOV version 1.14