LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/s101 - ogrs101featurecatalog.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 226 286 79.0 %
Date: 2026-05-08 18:52:02 Functions: 13 14 92.9 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  S-101 driver
       4             :  * Purpose:  Implements OGRS101FeatureCatalog
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2026, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "ogr_s101.h"
      14             : #include "ogrs101featurecatalog.h"
      15             : #include "cpl_http.h"
      16             : #include "cpl_minixml.h"
      17             : #include "cpl_multiproc.h"
      18             : 
      19             : #include <algorithm>
      20             : #include <cinttypes>
      21             : 
      22             : #include <mutex>
      23             : 
      24             : static OGRS101FeatureCatalog::LoadingStatus geStatus =
      25             :     OGRS101FeatureCatalog::LoadingStatus::UNINIT;
      26             : static OGRS101FeatureCatalog *gpoFeatureCatalog = nullptr;
      27             : 
      28             : // Matches S-101 2.0 spec
      29             : constexpr const char *FEATURE_CATALOG_URL =
      30             :     "https://raw.githubusercontent.com/iho-ohi/S-101-Documentation-and-FC/"
      31             :     "fbcf8aaa72331a33668aa554e702e2a28f27cbc0/S-101FC/FeatureCatalogue.xml";
      32             : constexpr const char *FEATURE_CATALOG_FILENAME =
      33             :     "101_Feature_Catalogue_2.0.0.xml";
      34             : constexpr int FEATURE_CATALOG_EXPECTED_FILE_SIZE = 2017402;
      35             : 
      36             : /************************************************************************/
      37             : /*                              GetMutex()                              */
      38             : /************************************************************************/
      39             : 
      40        1529 : static std::mutex &GetMutex()
      41             : {
      42             :     static std::mutex goMutex;
      43        1529 :     return goMutex;
      44             : }
      45             : 
      46             : /************************************************************************/
      47             : /*                     GetSingletonFeatureCatalog()                     */
      48             : /************************************************************************/
      49             : 
      50             : /** Get the global feature catalog instance */
      51             : 
      52             : /* static */
      53             : std::pair<OGRS101FeatureCatalog::LoadingStatus, const OGRS101FeatureCatalog *>
      54         266 : OGRS101FeatureCatalog::GetSingletonFeatureCatalog(bool bStrict)
      55             : {
      56         266 :     std::lock_guard lock(GetMutex());
      57         266 :     if (geStatus == LoadingStatus::UNINIT)
      58             :     {
      59             :         auto poFeatureCatalog =
      60          28 :             std::make_unique<OGRS101FeatureCatalog>(bStrict);
      61          14 :         geStatus = poFeatureCatalog->Load();
      62          14 :         if (geStatus == LoadingStatus::OK)
      63          14 :             gpoFeatureCatalog = poFeatureCatalog.release();
      64             :     }
      65         532 :     return {geStatus, gpoFeatureCatalog};
      66             : }
      67             : 
      68             : /************************************************************************/
      69             : /*                   CleanupSingletonFeatureCatalog()                   */
      70             : /************************************************************************/
      71             : 
      72             : /** Clean the global feature catalog instance */
      73             : 
      74             : /* static */
      75        1263 : void OGRS101FeatureCatalog::CleanupSingletonFeatureCatalog()
      76             : {
      77        1263 :     std::lock_guard lock(GetMutex());
      78        1263 :     geStatus = LoadingStatus::UNINIT;
      79             :     // Delete global object
      80        1263 :     CPL_IGNORE_RET_VAL(
      81        2526 :         std::unique_ptr<OGRS101FeatureCatalog>(gpoFeatureCatalog));
      82        1263 :     gpoFeatureCatalog = nullptr;
      83        1263 : }
      84             : 
      85             : /************************************************************************/
      86             : /*                       OGRS101FeatureCatalog()                        */
      87             : /************************************************************************/
      88             : 
      89             : /** Constructor */
      90          14 : OGRS101FeatureCatalog::OGRS101FeatureCatalog(bool bStrict) : m_bStrict(bStrict)
      91             : {
      92          14 : }
      93             : 
      94             : /************************************************************************/
      95             : /*                         EmitErrorOrWarning()                         */
      96             : /************************************************************************/
      97             : 
      98           0 : /*static*/ bool OGRS101FeatureCatalog::EmitErrorOrWarning(
      99             :     const char *pszFile, const char *pszFunc, int nLine, const char *pszMsg,
     100             :     bool bError, bool bRecoverable)
     101             : {
     102           0 :     return OGRS101Reader::EmitErrorOrWarning(pszFile, pszFunc, nLine, pszMsg,
     103           0 :                                              bError, bRecoverable);
     104             : }
     105             : 
     106             : /************************************************************************/
     107             : /*                                Load()                                */
     108             : /************************************************************************/
     109             : 
     110             : /** Load the feature catalog XML file. */
     111          14 : OGRS101FeatureCatalog::LoadingStatus OGRS101FeatureCatalog::Load()
     112             : {
     113          14 :     bool bError = false;
     114          28 :     const std::string osFilename = GetFilename(bError);
     115          14 :     if (bError)
     116           0 :         return LoadingStatus::ERROR;
     117          14 :     if (osFilename.empty())
     118           0 :         return LoadingStatus::SKIPPED;
     119             : 
     120          14 :     CPLDebugOnce("S101", "Reading feature catalog from %s", osFilename.c_str());
     121          28 :     CPLXMLTreeCloser oTree(CPLParseXMLFile(osFilename.c_str()));
     122          14 :     if (!oTree)
     123             :     {
     124           0 :         return m_bStrict ? LoadingStatus::ERROR : LoadingStatus::SKIPPED;
     125             :     }
     126          14 :     if (!CPLGetXMLNode(oTree.get(), "=S100FC:S100_FC_FeatureCatalogue"))
     127             :     {
     128           0 :         return EMIT_ERROR_OR_WARNING(CPLSPrintf(
     129             :                    "Cannot find S100FC:S100_FC_FeatureCatalogue in %s",
     130             :                    osFilename.c_str()))
     131           0 :                    ? LoadingStatus::SKIPPED
     132           0 :                    : LoadingStatus::ERROR;
     133             :     }
     134          14 :     CPLStripXMLNamespace(oTree.get(), "S100FC", /* bRecurse = */ true);
     135          14 :     const auto psRoot = CPLGetXMLNode(oTree.get(), "=S100_FC_FeatureCatalogue");
     136          14 :     CPLAssert(psRoot);
     137             : 
     138          14 :     return LoadSimpleAttributes(osFilename.c_str(), psRoot) &&
     139          14 :                    LoadComplexAttributes(osFilename.c_str(), psRoot) &&
     140          14 :                    LoadInformationTypes(osFilename.c_str(), psRoot) &&
     141          14 :                    LoadFeatureTypes(osFilename.c_str(), psRoot)
     142          28 :                ? LoadingStatus::OK
     143           0 :            : m_bStrict ? LoadingStatus::ERROR
     144          14 :                        : LoadingStatus::SKIPPED;
     145             : }
     146             : 
     147             : /************************************************************************/
     148             : /*                            GetFilename()                             */
     149             : /************************************************************************/
     150             : 
     151             : /** Get XML feature catalog filename. */
     152          14 : std::string OGRS101FeatureCatalog::GetFilename(bool &bError) const
     153             : {
     154          14 :     bError = false;
     155             : 
     156             :     // First try path given by GDAL_S101_FEATURE_CATALOG config option
     157          14 :     if (const char *pszFC =
     158          14 :             CPLGetConfigOption("GDAL_S101_FEATURE_CATALOG", nullptr))
     159             :     {
     160           0 :         if (EQUAL(pszFC, "") || EQUAL(pszFC, "NO") || EQUAL(pszFC, "FALSE") ||
     161           0 :             EQUAL(pszFC, "OFF") || EQUAL(pszFC, "0"))
     162           0 :             return {};
     163             : 
     164             :         VSIStatBufL sStat;
     165           0 :         if (VSIStatL(pszFC, &sStat) != 0)
     166             :         {
     167           0 :             bError = !EMIT_ERROR_OR_WARNING(
     168             :                 CPLSPrintf("Feature catalog %s cannot be found", pszFC));
     169           0 :             return {};
     170             :         }
     171           0 :         return pszFC;
     172             :     }
     173             : 
     174             :     // Then try file cached in ~/.gdal directory
     175          28 :     const std::string osCacheDir = GDALGetCacheDirectory();
     176          14 :     if (!osCacheDir.empty())
     177             :     {
     178             :         std::string osTmpFilename = CPLFormFilenameSafe(
     179          14 :             osCacheDir.c_str(), FEATURE_CATALOG_FILENAME, nullptr);
     180             :         VSIStatBufL sStat;
     181          14 :         if (VSIStatL(osTmpFilename.c_str(), &sStat) == 0)
     182             :         {
     183          13 :             return osTmpFilename;
     184             :         }
     185             :     }
     186             : 
     187             :     // Then try file in ${prefix}/share/gdal/
     188             :     {
     189           1 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     190           1 :         if (const char *pszFC = CPLFindFile("gdal", FEATURE_CATALOG_FILENAME))
     191             :         {
     192           0 :             return pszFC;
     193             :         }
     194             :     }
     195             : 
     196             :     // Finally try do download file from the web
     197           1 :     if (!osCacheDir.empty() && CPLHTTPEnabled())
     198             :     {
     199             :         // Try to download catalog from FEATURE_CATALOG_URL only
     200             :         // once per process
     201           3 :         static const std::string osStaticString = [&osCacheDir]()
     202             :         {
     203           1 :             std::string osFilenameLocal;
     204             :             CPLHTTPResult *psResult;
     205             :             {
     206           1 :                 CPLDebug("S101", "Downloading feature catalog from %s",
     207             :                          FEATURE_CATALOG_URL);
     208           1 :                 CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     209           1 :                 const char *const apszOptions[] = {"TIMEOUT=1", nullptr};
     210           1 :                 psResult = CPLHTTPFetch(FEATURE_CATALOG_URL, apszOptions);
     211             :             }
     212           1 :             if (psResult)
     213             :             {
     214           1 :                 if (psResult->nStatus == 0 &&
     215           1 :                     psResult->nDataLen == FEATURE_CATALOG_EXPECTED_FILE_SIZE)
     216             :                 {
     217             :                     VSIStatBufL sStat;
     218           1 :                     if (VSIStatL(osCacheDir.c_str(), &sStat) != 0)
     219           0 :                         VSIMkdir(osCacheDir.c_str(), 0755);
     220             : 
     221             :                     std::string osFilename = CPLFormFilenameSafe(
     222           2 :                         osCacheDir.c_str(), FEATURE_CATALOG_FILENAME, nullptr);
     223             :                     const std::string osFilenameTmp =
     224           0 :                         osFilename + ".tmp" +
     225             :                         CPLSPrintf("_%" PRId64,
     226           2 :                                    static_cast<int64_t>(CPLGetPID()));
     227           1 :                     VSILFILE *f = VSIFOpenL(osFilenameTmp.c_str(), "wb");
     228           1 :                     if (f)
     229             :                     {
     230           2 :                         bool bOK = VSIFWriteL(psResult->pabyData,
     231           1 :                                               psResult->nDataLen, 1, f) == 1;
     232           1 :                         bOK = VSIFCloseL(f) == 0 && bOK;
     233           1 :                         bOK = bOK && VSIRename(osFilenameTmp.c_str(),
     234             :                                                osFilename.c_str()) == 0;
     235           1 :                         if (bOK)
     236             :                         {
     237           1 :                             osFilenameLocal = std::move(osFilename);
     238           1 :                             CPLDebug("S101",
     239             :                                      "Feature catalog successfully "
     240             :                                      "written in %s",
     241             :                                      osFilenameLocal.c_str());
     242             :                         }
     243             :                         else
     244             :                         {
     245           0 :                             VSIUnlink(osFilenameTmp.c_str());
     246             :                         }
     247             :                     }
     248             :                     else
     249             :                     {
     250           0 :                         CPLDebug("S101", "Cannot write temporary file %s",
     251             :                                  osFilenameTmp.c_str());
     252           1 :                     }
     253             :                 }
     254             :                 else
     255             :                 {
     256           0 :                     CPLDebug(
     257             :                         "S101",
     258             :                         "Downloading of %s failed (status=%d, filesize=%d)",
     259             :                         FEATURE_CATALOG_URL, psResult->nStatus,
     260             :                         psResult->nDataLen);
     261             :                 }
     262           1 :                 CPLHTTPDestroyResult(psResult);
     263             :             }
     264           1 :             return osFilenameLocal;
     265           1 :         }();
     266             : 
     267           1 :         return osStaticString;
     268             :     }
     269             : 
     270           0 :     CPLDebugOnce("S101", "Cannot find feature catalog XML");
     271           0 :     return {};
     272             : }
     273             : 
     274             : /************************************************************************/
     275             : /*                        LoadSimpleAttributes()                        */
     276             : /************************************************************************/
     277             : 
     278          14 : bool OGRS101FeatureCatalog::LoadSimpleAttributes(const char *pszFC,
     279             :                                                  const CPLXMLNode *psRoot)
     280             : {
     281             :     const CPLXMLNode *psSimpleAttrs =
     282          14 :         CPLGetXMLNode(psRoot, "S100_FC_SimpleAttributes");
     283          14 :     if (!psSimpleAttrs)
     284             :     {
     285           0 :         return EMIT_ERROR_OR_WARNING(
     286             :             CPLSPrintf("Cannot find S100_FC_SimpleAttributes in %s", pszFC));
     287             :     }
     288        3318 :     for (const CPLXMLNode *psSimpleAttr = psSimpleAttrs->psChild; psSimpleAttr;
     289        3304 :          psSimpleAttr = psSimpleAttr->psNext)
     290             :     {
     291        3304 :         if (psSimpleAttr->eType == CXT_Element &&
     292        3304 :             strcmp(psSimpleAttr->pszValue, "S100_FC_SimpleAttribute") == 0)
     293             :         {
     294        3304 :             const char *pszCode = CPLGetXMLValue(psSimpleAttr, "code", nullptr);
     295             :             const char *pszValueType =
     296        3304 :                 CPLGetXMLValue(psSimpleAttr, "valueType", nullptr);
     297        3304 :             if (pszCode && pszValueType)
     298             :             {
     299        3304 :                 SimpleAttribute attr;
     300        3304 :                 attr.code = pszCode;
     301        3304 :                 attr.name = CPLGetXMLValue(psSimpleAttr, "name", "");
     302        3304 :                 attr.type = pszValueType;
     303             : 
     304        3304 :                 constexpr const char *const apszValueTypes[] = {
     305             :                     VALUE_TYPE_BOOLEAN,
     306             :                     VALUE_TYPE_ENUMERATION,
     307             :                     VALUE_TYPE_INTEGER,
     308             :                     VALUE_TYPE_REAL,
     309             :                     VALUE_TYPE_TRUNCATED_DATE,
     310             :                     VALUE_TYPE_TEXT,
     311             :                     VALUE_TYPE_TIME,
     312             :                     VALUE_TYPE_URI,
     313             :                     VALUE_TYPE_URN,
     314             :                 };
     315        3304 :                 if (std::find_if(std::begin(apszValueTypes),
     316             :                                  std::end(apszValueTypes),
     317        9996 :                                  [pszValueType](const char *x)
     318       13300 :                                  { return strcmp(x, pszValueType) == 0; }) ==
     319        3304 :                     std::end(apszValueTypes))
     320             :                 {
     321           0 :                     if (!EMIT_ERROR_OR_WARNING(
     322             :                             CPLSPrintf("In %s: unknown valueType %s in "
     323             :                                        "simple attribute of code %s",
     324             :                                        pszFC, pszValueType, pszCode)))
     325             :                     {
     326           0 :                         return false;
     327             :                     }
     328             :                 }
     329             : 
     330        3304 :                 if (strcmp(pszValueType, "enumeration") == 0)
     331             :                 {
     332        1568 :                     if (const CPLXMLNode *psListedValues =
     333        1568 :                             CPLGetXMLNode(psSimpleAttr, "listedValues"))
     334             :                     {
     335        1568 :                         for (const CPLXMLNode *psListedValue =
     336             :                                  psListedValues->psChild;
     337       16044 :                              psListedValue;
     338       14476 :                              psListedValue = psListedValue->psNext)
     339             :                         {
     340       14476 :                             if (psListedValue->eType == CXT_Element &&
     341       14476 :                                 strcmp(psListedValue->pszValue,
     342             :                                        "listedValue") == 0)
     343             :                             {
     344       14476 :                                 const char *pszEnumLabel = CPLGetXMLValue(
     345       14476 :                                     psListedValue, "label", nullptr);
     346       14476 :                                 const char *pszEnumCode = CPLGetXMLValue(
     347             :                                     psListedValue, "code", nullptr);
     348       28952 :                                 if (pszEnumCode && pszEnumLabel &&
     349       14476 :                                     CPLGetValueType(pszEnumCode) ==
     350             :                                         CPL_VALUE_INTEGER)
     351             :                                 {
     352       28952 :                                     if (!attr.enumeratedValues
     353             :                                              .insert(
     354           0 :                                                  std::pair<int, std::string>(
     355       28952 :                                                      atoi(pszEnumCode),
     356       28952 :                                                      std::move(pszEnumLabel)))
     357       14476 :                                              .second)
     358             :                                     {
     359           0 :                                         if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     360             :                                                 "%s: several listedValue with "
     361             :                                                 "code "
     362             :                                                 "%s in %s",
     363             :                                                 pszFC, pszEnumCode, pszCode)))
     364             :                                         {
     365           0 :                                             return false;
     366             :                                         }
     367             :                                     }
     368             :                                 }
     369           0 :                                 else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     370             :                                              "%s: invalid listedValue in "
     371             :                                              "simple attribute %s",
     372             :                                              pszFC, pszCode)))
     373             :                                 {
     374           0 :                                     return false;
     375             :                                 }
     376             :                             }
     377             :                         }
     378             :                     }
     379             :                 }
     380        6608 :                 if (!m_simpleAttributes
     381        6608 :                          .insert(std::pair<std::string, SimpleAttribute>(
     382        6608 :                              pszCode, std::move(attr)))
     383        3304 :                          .second)
     384             :                 {
     385           0 :                     if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     386             :                             "%s: several simple attributes with code %s", pszFC,
     387             :                             pszCode)))
     388             :                     {
     389           0 :                         return false;
     390             :                     }
     391        3304 :                 }
     392             :             }
     393           0 :             else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     394             :                          "In %s: invalid S100_FC_SimpleAttribute", pszFC)))
     395             :             {
     396           0 :                 return false;
     397             :             }
     398             :         }
     399             :     }
     400             : 
     401          14 :     return true;
     402             : }
     403             : 
     404             : /************************************************************************/
     405             : /*                       LoadComplexAttributes()                        */
     406             : /************************************************************************/
     407             : 
     408          14 : bool OGRS101FeatureCatalog::LoadComplexAttributes(const char *pszFC,
     409             :                                                   const CPLXMLNode *psRoot)
     410             : {
     411             :     const CPLXMLNode *psComplexAttrs =
     412          14 :         CPLGetXMLNode(psRoot, "S100_FC_ComplexAttributes");
     413          14 :     if (!psComplexAttrs)
     414             :     {
     415           0 :         return EMIT_ERROR_OR_WARNING(
     416             :             CPLSPrintf("Cannot find S100_FC_ComplexAttributes in %s", pszFC));
     417             :     }
     418          14 :     for (const CPLXMLNode *psComplexAttr = psComplexAttrs->psChild;
     419         602 :          psComplexAttr; psComplexAttr = psComplexAttr->psNext)
     420             :     {
     421         588 :         if (psComplexAttr->eType == CXT_Element &&
     422         588 :             strcmp(psComplexAttr->pszValue, "S100_FC_ComplexAttribute") == 0)
     423             :         {
     424             :             const char *pszCode =
     425         588 :                 CPLGetXMLValue(psComplexAttr, "code", nullptr);
     426         588 :             if (pszCode)
     427             :             {
     428         588 :                 ComplexAttribute attr;
     429         588 :                 attr.code = pszCode;
     430         588 :                 attr.name = CPLGetXMLValue(psComplexAttr, "name", "");
     431             : 
     432             :                 const CPLXMLNode *psSubAttributeBinding =
     433         588 :                     CPLGetXMLNode(psComplexAttr, "subAttributeBinding");
     434        2198 :                 for (; psSubAttributeBinding;
     435        1610 :                      psSubAttributeBinding = psSubAttributeBinding->psNext)
     436             :                 {
     437        1610 :                     if (psSubAttributeBinding->eType == CXT_Element &&
     438        1610 :                         strcmp(psSubAttributeBinding->pszValue,
     439             :                                "subAttributeBinding") == 0)
     440             :                     {
     441        1610 :                         const char *pszAttributeRef = CPLGetXMLValue(
     442             :                             psSubAttributeBinding, "attribute.ref", nullptr);
     443        1610 :                         if (pszAttributeRef)
     444             :                         {
     445        1610 :                             attr.attributeBindings.insert(pszAttributeRef);
     446             :                         }
     447             :                     }
     448             :                 }
     449        1176 :                 if (!m_complexAttributes
     450        1176 :                          .insert(std::pair<std::string, ComplexAttribute>(
     451        1176 :                              pszCode, std::move(attr)))
     452         588 :                          .second)
     453             :                 {
     454           0 :                     if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     455             :                             "In %s: several complex attributes with code %s",
     456             :                             pszFC, pszCode)))
     457             :                     {
     458           0 :                         return false;
     459             :                     }
     460             :                 }
     461             :             }
     462           0 :             else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     463             :                          "In %s: invalid S100_FC_ComplexAttribute", pszFC)))
     464             :             {
     465           0 :                 return false;
     466             :             }
     467             :         }
     468             :     }
     469             : 
     470         602 :     for (const auto &[code, def] : m_complexAttributes)
     471             :     {
     472        2198 :         for (const auto &attrCode : def.attributeBindings)
     473             :         {
     474        1932 :             if (!cpl::contains(m_simpleAttributes, attrCode) &&
     475         322 :                 !cpl::contains(m_complexAttributes, attrCode))
     476             :             {
     477           0 :                 if (!EMIT_ERROR_OR_WARNING(
     478             :                         CPLSPrintf("In %s: complex attribute %s refers to "
     479             :                                    "attribute %s which does not exist",
     480             :                                    pszFC, code.c_str(), attrCode.c_str())))
     481             :                 {
     482           0 :                     return false;
     483             :                 }
     484             :             }
     485             :         }
     486             :     }
     487             : 
     488          14 :     return true;
     489             : }
     490             : 
     491             : /************************************************************************/
     492             : /*                        LoadInformationTypes()                        */
     493             : /************************************************************************/
     494             : 
     495          14 : bool OGRS101FeatureCatalog::LoadInformationTypes(const char *pszFC,
     496             :                                                  const CPLXMLNode *psRoot)
     497             : {
     498             :     const CPLXMLNode *psInformationTypes =
     499          14 :         CPLGetXMLNode(psRoot, "S100_FC_InformationTypes");
     500          14 :     if (!psInformationTypes)
     501             :     {
     502           0 :         return EMIT_ERROR_OR_WARNING(
     503             :             CPLSPrintf("Cannot find S100_FC_InformationTypes in %s", pszFC));
     504             :     }
     505          14 :     for (const CPLXMLNode *psInformationType = psInformationTypes->psChild;
     506          84 :          psInformationType; psInformationType = psInformationType->psNext)
     507             :     {
     508          70 :         if (psInformationType->eType == CXT_Element &&
     509          70 :             strcmp(psInformationType->pszValue, "S100_FC_InformationType") == 0)
     510             :         {
     511             :             const char *pszCode =
     512          70 :                 CPLGetXMLValue(psInformationType, "code", nullptr);
     513          70 :             if (pszCode)
     514             :             {
     515          70 :                 InformationType featureType;
     516          70 :                 featureType.code = pszCode;
     517             :                 featureType.name =
     518          70 :                     CPLGetXMLValue(psInformationType, "name", "");
     519             :                 featureType.definition =
     520          70 :                     CPLGetXMLValue(psInformationType, "definition", "");
     521             :                 featureType.alias =
     522          70 :                     CPLGetXMLValue(psInformationType, "alias", "");
     523             : 
     524             :                 const CPLXMLNode *psAttributeBinding =
     525          70 :                     CPLGetXMLNode(psInformationType, "attributeBinding");
     526         392 :                 for (; psAttributeBinding;
     527         322 :                      psAttributeBinding = psAttributeBinding->psNext)
     528             :                 {
     529         322 :                     if (psAttributeBinding->eType == CXT_Element &&
     530         322 :                         strcmp(psAttributeBinding->pszValue,
     531             :                                "attributeBinding") == 0)
     532             :                     {
     533         322 :                         const char *pszAttributeRef = CPLGetXMLValue(
     534         322 :                             psAttributeBinding, "attribute.ref", nullptr);
     535         322 :                         if (pszAttributeRef)
     536             :                         {
     537         322 :                             if (!cpl::contains(m_simpleAttributes,
     538         532 :                                                pszAttributeRef) &&
     539         210 :                                 !cpl::contains(m_complexAttributes,
     540             :                                                pszAttributeRef))
     541             :                             {
     542           0 :                                 if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     543             :                                         "In %s: information type %s refers to "
     544             :                                         "attribute %s which does not exist",
     545             :                                         pszFC, pszCode, pszAttributeRef)))
     546             :                                 {
     547           0 :                                     return false;
     548             :                                 }
     549             :                             }
     550             : 
     551             :                             featureType.attributeBindings.insert(
     552         322 :                                 pszAttributeRef);
     553             :                         }
     554             :                     }
     555             :                 }
     556             : 
     557         140 :                 if (!m_informationTypes
     558         140 :                          .insert(std::pair<std::string, InformationType>(
     559         140 :                              pszCode, std::move(featureType)))
     560          70 :                          .second)
     561             :                 {
     562           0 :                     if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     563             :                             "%s: several information types with code %s", pszFC,
     564             :                             pszCode)))
     565             :                     {
     566           0 :                         return false;
     567             :                     }
     568             :                 }
     569             :             }
     570           0 :             else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     571             :                          "%s: invalid S100_FC_InformationType", pszFC)))
     572             :             {
     573           0 :                 return false;
     574             :             }
     575             :         }
     576             :     }
     577             : 
     578          14 :     return true;
     579             : }
     580             : 
     581             : /************************************************************************/
     582             : /*                          LoadFeatureTypes()                          */
     583             : /************************************************************************/
     584             : 
     585          14 : bool OGRS101FeatureCatalog::LoadFeatureTypes(const char *pszFC,
     586             :                                              const CPLXMLNode *psRoot)
     587             : {
     588             :     const CPLXMLNode *psFeatureTypes =
     589          14 :         CPLGetXMLNode(psRoot, "S100_FC_FeatureTypes");
     590          14 :     if (!psFeatureTypes)
     591             :     {
     592           0 :         return EMIT_ERROR_OR_WARNING(
     593             :             CPLSPrintf("Cannot find S100_FC_FeatureTypes in %s", pszFC));
     594             :     }
     595          14 :     for (const CPLXMLNode *psFeatureType = psFeatureTypes->psChild;
     596        2674 :          psFeatureType; psFeatureType = psFeatureType->psNext)
     597             :     {
     598        2660 :         if (psFeatureType->eType == CXT_Element &&
     599        2660 :             strcmp(psFeatureType->pszValue, "S100_FC_FeatureType") == 0)
     600             :         {
     601             :             const char *pszCode =
     602        2660 :                 CPLGetXMLValue(psFeatureType, "code", nullptr);
     603        2660 :             if (pszCode)
     604             :             {
     605        2660 :                 FeatureType featureType;
     606        2660 :                 featureType.code = pszCode;
     607        2660 :                 featureType.name = CPLGetXMLValue(psFeatureType, "name", "");
     608             :                 featureType.definition =
     609        2660 :                     CPLGetXMLValue(psFeatureType, "definition", "");
     610        2660 :                 featureType.alias = CPLGetXMLValue(psFeatureType, "alias", "");
     611             : 
     612             :                 const CPLXMLNode *psAttributeBinding =
     613        2660 :                     CPLGetXMLNode(psFeatureType, "attributeBinding");
     614       47558 :                 for (; psAttributeBinding;
     615       44898 :                      psAttributeBinding = psAttributeBinding->psNext)
     616             :                 {
     617       44898 :                     if (psAttributeBinding->eType == CXT_Element &&
     618       44898 :                         strcmp(psAttributeBinding->pszValue,
     619             :                                "attributeBinding") == 0)
     620             :                     {
     621       27972 :                         const char *pszAttributeRef = CPLGetXMLValue(
     622       27972 :                             psAttributeBinding, "attribute.ref", nullptr);
     623       27972 :                         if (pszAttributeRef)
     624             :                         {
     625       27972 :                             if (!cpl::contains(m_simpleAttributes,
     626       36666 :                                                pszAttributeRef) &&
     627        8694 :                                 !cpl::contains(m_complexAttributes,
     628             :                                                pszAttributeRef))
     629             :                             {
     630           0 :                                 if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     631             :                                         "In %s: feature type %s refers to "
     632             :                                         "attribute %s which does not exist",
     633             :                                         pszFC, pszCode, pszAttributeRef)))
     634             :                                 {
     635           0 :                                     return false;
     636             :                                 }
     637             :                             }
     638             : 
     639             :                             featureType.attributeBindings.insert(
     640       27972 :                                 pszAttributeRef);
     641             :                         }
     642             :                     }
     643             :                 }
     644             : 
     645             :                 const CPLXMLNode *psPermittedPrimitives =
     646        2660 :                     CPLGetXMLNode(psFeatureType, "permittedPrimitives");
     647        6958 :                 for (; psPermittedPrimitives;
     648        4298 :                      psPermittedPrimitives = psPermittedPrimitives->psNext)
     649             :                 {
     650        4298 :                     if (psPermittedPrimitives->eType == CXT_Element &&
     651        4298 :                         strcmp(psPermittedPrimitives->pszValue,
     652        4298 :                                "permittedPrimitives") == 0 &&
     653        4298 :                         psPermittedPrimitives->psChild &&
     654        4298 :                         psPermittedPrimitives->psChild->eType == CXT_Text &&
     655        4298 :                         psPermittedPrimitives->psChild->pszValue)
     656             :                     {
     657        4298 :                         const char *pszPermittedPrimitive =
     658        4298 :                             psPermittedPrimitives->psChild->pszValue;
     659        4298 :                         constexpr const char *const apszPermittedPrimitives[] =
     660             :                             {
     661             :                                 PERMITTED_PRIMITIVE_NO_GEOMETRY,
     662             :                                 PERMITTED_PRIMITIVE_POINT,
     663             :                                 PERMITTED_PRIMITIVE_POINTSET,
     664             :                                 PERMITTED_PRIMITIVE_CURVE,
     665             :                                 PERMITTED_PRIMITIVE_SURFACE,
     666             :                             };
     667        4298 :                         if (std::find_if(
     668             :                                 std::begin(apszPermittedPrimitives),
     669             :                                 std::end(apszPermittedPrimitives),
     670       15806 :                                 [pszPermittedPrimitive](const char *x)
     671             :                                 {
     672       15806 :                                     return strcmp(x, pszPermittedPrimitive) ==
     673       15806 :                                            0;
     674        4298 :                                 }) != std::end(apszPermittedPrimitives))
     675             :                         {
     676             :                             featureType.permittedPrimitives.insert(
     677        4298 :                                 pszPermittedPrimitive);
     678             :                         }
     679           0 :                         else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     680             :                                      "In %s: unknown permittedPrimitives %s in "
     681             :                                      "feature type with code %s",
     682             :                                      pszFC, pszPermittedPrimitive, pszCode)))
     683             :                         {
     684           0 :                             return false;
     685             :                         }
     686             :                     }
     687             :                 }
     688             : 
     689        5320 :                 if (!m_featureTypes
     690        5320 :                          .insert(std::pair<std::string, FeatureType>(
     691        5320 :                              pszCode, std::move(featureType)))
     692        2660 :                          .second)
     693             :                 {
     694           0 :                     if (!EMIT_ERROR_OR_WARNING(
     695             :                             CPLSPrintf("%s: several feature types with code %s",
     696             :                                        pszFC, pszCode)))
     697             :                     {
     698           0 :                         return false;
     699             :                     }
     700             :                 }
     701             :             }
     702           0 :             else if (!EMIT_ERROR_OR_WARNING(
     703             :                          CPLSPrintf("%s: invalid S100_FC_FeatureType", pszFC)))
     704             :             {
     705           0 :                 return false;
     706             :             }
     707             :         }
     708             :     }
     709             : 
     710          14 :     return true;
     711             : }

Generated by: LCOV version 1.14