LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/s101 - ogrs101featurecatalog.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 207 294 70.4 %
Date: 2026-07-09 08:35:43 Functions: 12 14 85.7 %

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

Generated by: LCOV version 1.14