LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/s101 - ogrs101readerattributes.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 388 506 76.7 %
Date: 2026-05-08 18:52:02 Functions: 10 12 83.3 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  S-101 driver
       4             :  * Purpose:  Implements OGRS101Reader
       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 "ogrs101readerconstants.h"
      16             : 
      17             : #include <algorithm>
      18             : #include <charconv>
      19             : #include <cfloat>
      20             : #include <limits>
      21             : #include <memory>
      22             : #include <optional>
      23             : #include <set>
      24             : #include <tuple>
      25             : #include <utility>
      26             : 
      27             : #include "include_fast_float.h"
      28             : 
      29             : /************************************************************************/
      30             : /*                          IngestAttributes()                          */
      31             : /************************************************************************/
      32             : 
      33             : /** For a given record that has a ATTR/INAS/FACS field, ingest all attributes
      34             :  * from a particular instance of that field
      35             :  */
      36        1605 : bool OGRS101Reader::IngestAttributes(
      37             :     const DDFRecord *poRecord, int iRecord, const char *pszIDFieldName,
      38             :     const char *pszAttrFieldName, const DDFField *poATTRField, int iField,
      39             :     bool bMultipleFields, std::vector<S101AttrDef> &asS101AttrDefs) const
      40             : {
      41        3210 :     std::set<std::tuple<AttrCode, AttrRepeat, AttrIndex>> oSetNATC_ATIX_PAIX;
      42             : 
      43          52 :     const auto GetErrorContext = [iRecord, pszIDFieldName, pszAttrFieldName,
      44         104 :                                   iField, bMultipleFields](AttrIndex iATTR)
      45             :     {
      46          52 :         if (bMultipleFields)
      47             :         {
      48           0 :             return CPLSPrintf(
      49             :                 "Record index=%d of %s, %s[%d] field, attribute idx=%d",
      50             :                 iRecord, pszIDFieldName, pszAttrFieldName, iField,
      51           0 :                 static_cast<int>(iATTR));
      52             :         }
      53             :         else
      54             :         {
      55          52 :             return CPLSPrintf(
      56             :                 "Record index=%d of %s, %s field, attribute idx=%d", iRecord,
      57          52 :                 pszIDFieldName, pszAttrFieldName, static_cast<int>(iATTR));
      58             :         }
      59        1605 :     };
      60             : 
      61             :     const AttrCode nLargestNATC(
      62        1605 :         !m_attributeCodes.empty() ? m_attributeCodes.rbegin()->first : 0);
      63             :     const bool bAttributeCodesSequential =
      64        3207 :         !m_attributeCodes.empty() && m_attributeCodes.begin()->first == 1 &&
      65        1602 :         static_cast<size_t>(static_cast<int>(nLargestNATC)) ==
      66        1602 :             m_attributeCodes.size();
      67             : 
      68        1605 :     const int nRepeatCount = poATTRField->GetRepeatCount();
      69        1605 :     AttrCode nLastNATC = -1;
      70        1605 :     AttrRepeat nLastATIX = -1;
      71        1605 :     AttrIndex nLastPAIX = -1;
      72             : 
      73             :     // Find multi-valued parts of the path
      74        3210 :     std::map<std::pair<AttrCode, AttrIndex>, int> oMapOccurrenceCount;
      75        3754 :     for (AttrIndex iATTR = 0; iATTR < nRepeatCount; ++iATTR)
      76             :     {
      77             :         const auto GetIntSubfield =
      78        8596 :             [poRecord, poATTRField, iATTR](const char *pszSubFieldName)
      79             :         {
      80        4298 :             return poRecord->GetIntSubfield(poATTRField, pszSubFieldName,
      81        4298 :                                             static_cast<int>(iATTR));
      82        2149 :         };
      83             : 
      84        2149 :         ++oMapOccurrenceCount[{GetIntSubfield(NATC_SUBFIELD),
      85        2149 :                                GetIntSubfield(PAIX_SUBFIELD)}];
      86             :     }
      87             : 
      88        1605 :     const size_t nS101AttrDefsBaseIdx = asS101AttrDefs.size();
      89             : 
      90        3733 :     for (AttrIndex iATTR = 0; iATTR < nRepeatCount; ++iATTR)
      91             :     {
      92             :         const auto GetIntSubfield =
      93       17060 :             [poRecord, poATTRField, iATTR](const char *pszSubFieldName)
      94             :         {
      95        8530 :             return poRecord->GetIntSubfield(poATTRField, pszSubFieldName,
      96        8530 :                                             static_cast<int>(iATTR));
      97        2136 :         };
      98             : 
      99        2136 :         const int nATIN = GetIntSubfield(ATIN_SUBFIELD);
     100        2136 :         if (nATIN != INSTRUCTION_INSERT)
     101             :         {
     102           3 :             if (!EMIT_ERROR_OR_WARNING(
     103             :                     CPLSPrintf("%s: wrong value %d for ATIN subfield.",
     104             :                                GetErrorContext(iATTR), nATIN)))
     105             :             {
     106           8 :                 return false;
     107             :             }
     108           2 :             nLastNATC = -1;
     109           2 :             nLastATIX = -1;
     110           2 :             nLastPAIX = -1;
     111           2 :             asS101AttrDefs.push_back(S101AttrDef());
     112           6 :             continue;
     113             :         }
     114             : 
     115        2133 :         const AttrCode nNATC(GetIntSubfield(NATC_SUBFIELD));
     116        2162 :         if (!cpl::contains(m_attributeCodes, nNATC) &&
     117          29 :             !EMIT_ERROR_OR_WARNING(
     118             :                 CPLSPrintf("%s: cannot find attribute code %d in ATCS field "
     119             :                            "of the Dataset General Information Record%s.",
     120             :                            GetErrorContext(iATTR), static_cast<int>(nNATC),
     121             :                            bAttributeCodesSequential
     122             :                                ? CPLSPrintf(". Must be in [1, %d]",
     123             :                                             static_cast<int>(nLargestNATC))
     124             :                                : "")))
     125             :         {
     126           1 :             return false;
     127             :         }
     128             : 
     129        2132 :         const AttrRepeat nATIX(GetIntSubfield(ATIX_SUBFIELD));
     130        2132 :         if (!(nATIX >= 1 && nATIX <= nRepeatCount))
     131             :         {
     132           3 :             if (!EMIT_ERROR_OR_WARNING(
     133             :                     CPLSPrintf("%s: wrong value %d for ATIX subfield. "
     134             :                                "Must be in [1, %d].",
     135             :                                GetErrorContext(iATTR), static_cast<int>(nATIX),
     136             :                                nRepeatCount)))
     137             :             {
     138           1 :                 return false;
     139             :             }
     140           2 :             nLastNATC = -1;
     141           2 :             nLastATIX = -1;
     142           2 :             nLastPAIX = -1;
     143           2 :             asS101AttrDefs.push_back(S101AttrDef());
     144           2 :             continue;
     145             :         }
     146             : 
     147        2129 :         const AttrIndex nPAIX = GetIntSubfield(PAIX_SUBFIELD);
     148             :         // The parent index must be lower than the current attribute index,
     149             :         // since parents are required to be listed before.
     150        2129 :         if (!(nPAIX >= 0 && nPAIX <= iATTR))
     151             :         {
     152           3 :             if (!EMIT_ERROR_OR_WARNING(
     153             :                     CPLSPrintf("%s: wrong value %d for PAIX subfield. "
     154             :                                "Must be in [0, %d].",
     155             :                                GetErrorContext(iATTR), static_cast<int>(nPAIX),
     156             :                                static_cast<int>(iATTR))))
     157             :             {
     158           1 :                 return false;
     159             :             }
     160           2 :             nLastNATC = -1;
     161           2 :             nLastATIX = -1;
     162           2 :             nLastPAIX = -1;
     163           2 :             asS101AttrDefs.push_back(S101AttrDef());
     164           2 :             continue;
     165             :         }
     166             : 
     167        2126 :         if (nPAIX == nLastPAIX)
     168             :         {
     169         220 :             if (nNATC == nLastNATC)
     170             :             {
     171          78 :                 if (nATIX != nLastATIX + 1 &&
     172          78 :                     !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     173             :                         "%s: wrong value %d for ATIX subfield. Expected %d.",
     174             :                         GetErrorContext(iATTR), static_cast<int>(nATIX),
     175             :                         static_cast<int>(nLastATIX + 1))))
     176             :                 {
     177           1 :                     return false;
     178             :                 }
     179             :             }
     180         145 :             else if (nATIX != 1)
     181             :             {
     182           3 :                 if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     183             :                         "%s: wrong value %d for ATIX subfield. Expected %d.",
     184             :                         GetErrorContext(iATTR), static_cast<int>(nATIX), 1)))
     185             :                 {
     186           1 :                     return false;
     187             :                 }
     188             :             }
     189             :         }
     190             : 
     191             :         // (NATC,ATIX,PAIX) tuple should be unique within a record
     192        2129 :         if (!oSetNATC_ATIX_PAIX.insert({nNATC, nATIX, nPAIX}).second &&
     193           5 :             !EMIT_ERROR_OR_WARNING(
     194             :                 CPLSPrintf("%s: several instances of "
     195             :                            "(NATC,ATIX,PAIX)=(%d,%d,%d) "
     196             :                            "in field %s of the same record.",
     197             :                            GetErrorContext(iATTR), static_cast<int>(nNATC),
     198             :                            static_cast<int>(nATIX), static_cast<int>(nPAIX),
     199             :                            pszAttrFieldName)))
     200             :         {
     201           1 :             return false;
     202             :         }
     203             : 
     204             :         // Does this attribute have a parent?
     205        2123 :         const bool bIsMultiValued = oMapOccurrenceCount[{nNATC, nPAIX}] > 1;
     206        2123 :         PathVector oReversedPath{{nNATC, bIsMultiValued ? nATIX : 0}};
     207        2123 :         if (nPAIX > 0)
     208             :         {
     209             :             // Assertion can't trigger because nPAIX <= iATTR < asS101AttrDefs.size() - nS101AttrDefsBaseIdx
     210         935 :             CPLAssert(nS101AttrDefsBaseIdx +
     211             :                           static_cast<size_t>(static_cast<int>(nPAIX) - 1) <
     212             :                       asS101AttrDefs.size());
     213         935 :             auto &sParentAttrDef = asS101AttrDefs[nS101AttrDefsBaseIdx +
     214         935 :                                                   static_cast<int>(nPAIX) - 1];
     215         935 :             sParentAttrDef.bIsParent = true;
     216         938 :             if (!sParentAttrDef.osVal.empty() &&
     217           3 :                 !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     218             :                     "%s: parent attribute of index PAIX=%d has "
     219             :                     "a non empty ATVL subfield.",
     220             :                     GetErrorContext(iATTR), static_cast<int>(nPAIX))))
     221             :             {
     222           1 :                 return false;
     223             :             }
     224             : #if defined(__GNUC__)
     225             : #pragma GCC diagnostic push
     226             : #pragma GCC diagnostic ignored "-Wnull-dereference"
     227             : #endif
     228         934 :             oReversedPath.insert(oReversedPath.end(),
     229             :                                  sParentAttrDef.oReversedPath.begin(),
     230        1868 :                                  sParentAttrDef.oReversedPath.end());
     231             : #if defined(__GNUC__)
     232             : #pragma GCC diagnostic pop
     233             : #endif
     234             :         }
     235             : 
     236        2122 :         const char *pszATVL = poRecord->GetStringSubfield(
     237             :             poATTRField, ATVL_SUBFIELD, static_cast<int>(iATTR));
     238        2122 :         if (!pszATVL &&
     239           0 :             !EMIT_ERROR_OR_WARNING(CPLSPrintf("%s: cannot read ATVL subfield.",
     240             :                                               GetErrorContext(iATTR))))
     241             :         {
     242           0 :             return false;
     243             :         }
     244             : 
     245        2122 :         S101AttrDef sAttrDef;
     246        2122 :         sAttrDef.iField = iField;
     247        2122 :         sAttrDef.bMultipleFields = bMultipleFields;
     248        2122 :         sAttrDef.oReversedPath = std::move(oReversedPath);
     249        2122 :         if (pszATVL)
     250        2122 :             sAttrDef.osVal = pszATVL;
     251        2122 :         asS101AttrDefs.push_back(std::move(sAttrDef));
     252             : 
     253        2122 :         nLastNATC = nNATC;
     254        2122 :         nLastATIX = nATIX;
     255        2122 :         nLastPAIX = nPAIX;
     256             :     }
     257             : 
     258        1597 :     return true;
     259             : }
     260             : 
     261             : /************************************************************************/
     262             : /*                          IngestAttributes()                          */
     263             : /************************************************************************/
     264             : 
     265             : /** For a given record that has a ATTR/INAS/FACS field, ingest all attributes
     266             :  * from all instances of this field.
     267             :  */
     268        7103 : bool OGRS101Reader::IngestAttributes(
     269             :     const DDFRecord *poRecord, int iRecord, const char *pszIDFieldName,
     270             :     const char *pszAttrFieldName,
     271             :     std::vector<S101AttrDef> &asS101AttrDefs) const
     272             : {
     273        7103 :     asS101AttrDefs.clear();
     274             : 
     275       14206 :     const auto apoATTRFields = poRecord->GetFields(pszAttrFieldName);
     276        7103 :     const int nATTRFieldCount = static_cast<int>(apoATTRFields.size());
     277        7103 :     bool bSuccess = true;
     278        7103 :     if (EQUAL(pszAttrFieldName, ATTR_FIELD))
     279             :     {
     280        2379 :         for (int iATTRField = 0; bSuccess && iATTRField < nATTRFieldCount;
     281             :              ++iATTRField)
     282             :         {
     283         719 :             bSuccess = IngestAttributes(poRecord, iRecord, pszIDFieldName,
     284             :                                         pszAttrFieldName,
     285         719 :                                         apoATTRFields[iATTRField], iATTRField,
     286             :                                         nATTRFieldCount > 1, asS101AttrDefs);
     287             :         }
     288             :     }
     289             :     else
     290             :     {
     291        6329 :         for (int iATTRField = 0; bSuccess && iATTRField < nATTRFieldCount;
     292             :              ++iATTRField)
     293             :         {
     294         886 :             const auto poINASOrFASCField = apoATTRFields[iATTRField];
     295         886 :             if (poINASOrFASCField->GetParts().size() != 2)
     296             :             {
     297           0 :                 if (!EMIT_ERROR_OR_WARNING(
     298             :                         CPLSPrintf("Record index=%d of %s: missing components "
     299             :                                    "in %s field.",
     300             :                                    iRecord, pszIDFieldName, pszAttrFieldName)))
     301             :                 {
     302           0 :                     return false;
     303             :                 }
     304           0 :                 return true;
     305             :             }
     306         886 :             const auto poATTRField = poINASOrFASCField->GetParts()[1].get();
     307             : 
     308         886 :             bSuccess = IngestAttributes(
     309             :                 poRecord, iRecord, pszIDFieldName, pszAttrFieldName,
     310             :                 poATTRField, iATTRField, nATTRFieldCount > 1, asS101AttrDefs);
     311             :         }
     312             :     }
     313             : 
     314        9231 :     for (auto &sAttrDef : asS101AttrDefs)
     315             :     {
     316        2128 :         if (sAttrDef.bIsParent || sAttrDef.oReversedPath.empty())
     317         504 :             continue;
     318             : 
     319             :         // For last component, set the repetition part to 0, to be
     320             :         // actually able to detect multi-valued attributes!
     321        1624 :         sAttrDef.oReversedPath.front().second = 0;
     322             :     }
     323             : 
     324        7103 :     return bSuccess;
     325             : }
     326             : 
     327             : /************************************************************************/
     328             : /*                           BuildFieldName()                           */
     329             : /************************************************************************/
     330             : 
     331             : /** Returns a string with the concatenation of the parts, in reverse order.
     332             :  */
     333        1591 : std::string OGRS101Reader::BuildFieldName(const PathVector &oReversedPath,
     334             :                                           const char *pszAttrFieldName,
     335             :                                           int iField, bool bMultipleFields,
     336             :                                           const char *pszIDFieldName) const
     337             : {
     338        1591 :     std::string osAttrName;
     339        4227 :     for (size_t i = oReversedPath.size(); i > 0;)
     340             :     {
     341        2636 :         --i;
     342        2636 :         const auto &oPathComp = oReversedPath[i];
     343        2636 :         if (!osAttrName.empty())
     344        1045 :             osAttrName += '.';
     345        2636 :         const auto nCode = oPathComp.first;
     346        2636 :         const auto oIterNATC = m_attributeCodes.find(nCode);
     347        2636 :         if (oIterNATC == m_attributeCodes.end())
     348             :         {
     349          42 :             osAttrName += CPLSPrintf("code_%d", static_cast<int>(nCode));
     350             :         }
     351             :         else
     352             :         {
     353        2594 :             osAttrName += oIterNATC->second;
     354             :         }
     355             : 
     356        2636 :         if (bMultipleFields && strcmp(pszAttrFieldName, ATTR_FIELD) == 0)
     357             :         {
     358         347 :             osAttrName += '[';
     359         347 :             osAttrName += std::to_string(iField + 1);
     360         347 :             osAttrName += ']';
     361         347 :             bMultipleFields = false;
     362             :         }
     363             : 
     364        2636 :         const auto &nRepeat = oPathComp.second;
     365        2636 :         if (nRepeat > 0)
     366             :         {
     367         268 :             osAttrName += '[';
     368         268 :             osAttrName += std::to_string(static_cast<int>(nRepeat));
     369         268 :             osAttrName += ']';
     370             :         }
     371             :     }
     372             : 
     373        1591 :     if (strcmp(pszAttrFieldName, ATTR_FIELD) != 0)
     374             :     {
     375         395 :         std::string osPrefix;
     376         395 :         if (strcmp(pszIDFieldName, IRID_FIELD) == 0)
     377             :         {
     378          33 :             osPrefix = "association";
     379             :         }
     380         362 :         else if (strcmp(pszIDFieldName, FRID_FIELD) == 0)
     381             :         {
     382         280 :             if (strcmp(pszAttrFieldName, INAS_FIELD) == 0)
     383         140 :                 osPrefix = "infoAssociation";
     384             :             else
     385         140 :                 osPrefix = "featureAssociation";
     386             :         }
     387         395 :         if (!osPrefix.empty())
     388             :         {
     389         313 :             if (bMultipleFields)
     390             :             {
     391         120 :                 osPrefix += '[';
     392         120 :                 osPrefix += std::to_string(iField + 1);
     393         120 :                 osPrefix += ']';
     394             :             }
     395         313 :             osPrefix += '_';
     396             :         }
     397         395 :         osAttrName = osPrefix + osAttrName;
     398             :     }
     399             : 
     400        1591 :     return osAttrName;
     401             : }
     402             : 
     403             : /************************************************************************/
     404             : /*                          InferFeatureDefn()                          */
     405             : /************************************************************************/
     406             : 
     407             : /** Infer the feature definition from the content of INAS or ATTR records
     408             :  * of the index.
     409             :  */
     410        1693 : bool OGRS101Reader::InferFeatureDefn(
     411             :     const DDFRecordIndex &oIndex, const char *pszIDFieldName,
     412             :     const char *pszAttrFieldName, const std::vector<int> &anRecordIndices,
     413             :     OGRFeatureDefn &oFeatureDefn,
     414             :     std::map<std::string, std::unique_ptr<OGRFieldDomain>> &oMapFieldDomains,
     415             :     const OGRS101FeatureCatalogTypes::InformationType * /*psInformationType*/,
     416             :     const OGRS101FeatureCatalogTypes::FeatureType *psFeatureType) const
     417             : {
     418        1693 :     const bool bIsINAS = EQUAL(pszAttrFieldName, INAS_FIELD);
     419             : 
     420             :     struct OGRAttrDef
     421             :     {
     422             :         std::optional<OGRFieldType> oeType{};
     423             :         OGRFieldSubType eSubType = OFSTNone;
     424             :         bool bIsMultiValued = false;
     425             :         bool bMultipleFields = false;
     426             :         std::string osLongerName{};
     427             :         std::string osFieldDomainName{};
     428             :     };
     429             : 
     430             :     using PathVectorAndAttrIdx = std::pair<PathVector, int>;
     431        3386 :     std::map<PathVectorAndAttrIdx, OGRAttrDef> oMapFieldTypes;
     432             : 
     433        3386 :     std::vector<S101AttrDef> asS101AttrDefs;
     434        3386 :     std::map<PathVectorAndAttrIdx, int> mapPathToCount;
     435        1693 :     bool bFoundValidAssocField = false;
     436             : 
     437             :     // Iterate over the records (in the index of interest) to fill the
     438             :     // oMapFieldTypes map object that will be afterwards translated as
     439             :     // OGR feature definition
     440             :     // If anRecordIndices is not empty, it defines the subset of record
     441             :     // indices to iterate over. This is used for geometry records that are
     442             :     // dispatched to different OGR layers depending on the CRS.
     443        1693 :     const int nRecords = anRecordIndices.empty()
     444        2771 :                              ? oIndex.GetCount()
     445        1078 :                              : static_cast<int>(anRecordIndices.size());
     446        1693 :     int nMaxFieldRepeat = 1;
     447        4035 :     for (int iter = 0; iter < nRecords; ++iter)
     448             :     {
     449             :         const int iRecord =
     450        2358 :             anRecordIndices.empty() ? iter : anRecordIndices[iter];
     451             : 
     452          16 :         const auto GetErrorContext = [pszIDFieldName, iRecord]()
     453             :         {
     454          16 :             return CPLSPrintf("Record index=%d of %s", iRecord, pszIDFieldName);
     455        2358 :         };
     456             : 
     457        2358 :         const auto poRecord = oIndex.GetByIndex(iRecord);
     458        2358 :         CPLAssert(poRecord);
     459             : 
     460             :         const int nRUIN =
     461        2358 :             poRecord->GetIntSubfield(pszIDFieldName, 0, RUIN_SUBFIELD, 0);
     462        2358 :         if (nRUIN != INSTRUCTION_INSERT)
     463             :         {
     464           0 :             if (!EMIT_ERROR_OR_WARNING(CPLSPrintf("%s: wrong value %d for RUIN "
     465             :                                                   "subfield of %s field.",
     466             :                                                   GetErrorContext(), nRUIN,
     467             :                                                   pszIDFieldName)))
     468             :             {
     469          16 :                 return false;
     470             :             }
     471           0 :             continue;
     472             :         }
     473             : 
     474        2358 :         if (!EQUAL(pszAttrFieldName, ATTR_FIELD))
     475             :         {
     476        1795 :             const auto apoFields = poRecord->GetFields(pszAttrFieldName);
     477        1795 :             bool bSkipRecord = false;
     478        1795 :             nMaxFieldRepeat =
     479        1795 :                 std::max(nMaxFieldRepeat, static_cast<int>(apoFields.size()));
     480        1795 :             if (!apoFields.empty())
     481             :             {
     482         240 :                 const DDFField *poField = apoFields[0];
     483             : 
     484             :                 const RecordName nRRNM =
     485         240 :                     poRecord->GetIntSubfield(poField, RRNM_SUBFIELD, 0);
     486         240 :                 const RecordName nExpectedRRNM =
     487         240 :                     bIsINAS ? RECORD_NAME_INFORMATION_TYPE
     488             :                             : RECORD_NAME_FEATURE_TYPE;
     489         240 :                 if (nRRNM != nExpectedRRNM)
     490             :                 {
     491           8 :                     if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     492             :                             "%s: Invalid value for RRNM subfield of %s field: "
     493             :                             "got %d, expected %d.",
     494             :                             GetErrorContext(), pszAttrFieldName,
     495             :                             static_cast<int>(nRRNM),
     496             :                             static_cast<int>(nExpectedRRNM))))
     497             :                     {
     498           7 :                         return false;
     499             :                     }
     500             :                 }
     501             : 
     502             :                 const int nRRID =
     503         236 :                     poRecord->GetIntSubfield(poField, RRID_SUBFIELD, 0);
     504         436 :                 if ((bIsINAS &&
     505         470 :                      !m_oInformationTypeRecordIndex.FindRecord(nRRID)) ||
     506         234 :                     (!bIsINAS && !m_oFeatureTypeRecordIndex.FindRecord(nRRID)))
     507             :                 {
     508           2 :                     if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     509             :                             "%s: Invalid value %d for RRID subfield of %s "
     510             :                             "field: "
     511             :                             "does not match the record identifier of an "
     512             :                             "existing "
     513             :                             "%s record.",
     514             :                             GetErrorContext(), static_cast<int>(nRRID),
     515             :                             pszAttrFieldName,
     516             :                             bIsINAS ? "InformationType" : "FeatureType")))
     517             :                     {
     518           1 :                         return false;
     519             :                     }
     520             :                 }
     521             : 
     522         235 :                 if (bIsINAS)
     523             :                 {
     524             :                     const InfoAssocCode nNIAC =
     525         199 :                         poRecord->GetIntSubfield(poField, NIAC_SUBFIELD, 0);
     526         201 :                     if (!cpl::contains(m_informationAssociationCodes, nNIAC) &&
     527           2 :                         !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     528             :                             "%s: cannot find attribute code %d in IACS field "
     529             :                             "of the Dataset General Information Record.",
     530             :                             GetErrorContext(), static_cast<int>(nNIAC))))
     531             :                     {
     532           1 :                         return false;
     533             :                     }
     534             :                 }
     535             :                 else
     536             :                 {
     537             :                     const FeatureAssocCode nNFAC =
     538          36 :                         poRecord->GetIntSubfield(poField, NFAC_SUBFIELD, 0);
     539          36 :                     if (!cpl::contains(m_featureAssociationCodes, nNFAC) &&
     540           0 :                         !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     541             :                             "%s: cannot find attribute code %d in NFAC field "
     542             :                             "of the Dataset General Information Record.",
     543             :                             GetErrorContext(), static_cast<int>(nNFAC))))
     544             :                     {
     545           0 :                         return false;
     546             :                     }
     547             :                 }
     548             : 
     549             :                 const AssocRoleCode nNARC =
     550         234 :                     poRecord->GetIntSubfield(poField, NARC_SUBFIELD, 0);
     551         236 :                 if (!cpl::contains(m_associationRoleCodes, nNARC) &&
     552           2 :                     !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     553             :                         "%s: cannot find attribute code %d in ARCS field "
     554             :                         "of the Dataset General Information Record.",
     555             :                         GetErrorContext(), static_cast<int>(nNARC))))
     556             :                 {
     557           1 :                     return false;
     558             :                 }
     559             : 
     560         233 :                 const char *pszSubFieldName =
     561             :                     bIsINAS ? IUIN_SUBFIELD : FAUI_SUBFIELD;
     562             :                 int nInstruction =
     563         233 :                     poRecord->GetIntSubfield(poField, pszSubFieldName, 0);
     564         233 :                 if (nInstruction == 0)
     565             :                 {
     566             :                     // For 101GB00GB302045.000, non conformant
     567           0 :                     nInstruction = poRecord->GetIntSubfield(poField, "APUI", 0);
     568             :                 }
     569         233 :                 if (nInstruction != INSTRUCTION_INSERT)
     570             :                 {
     571           0 :                     if (!EMIT_ERROR_OR_WARNING(
     572             :                             CPLSPrintf("%s: wrong value %d for %s "
     573             :                                        "subfield of %s field.",
     574             :                                        GetErrorContext(), nInstruction,
     575             :                                        pszSubFieldName, pszAttrFieldName)))
     576             :                     {
     577           0 :                         return false;
     578             :                     }
     579           0 :                     bSkipRecord = true;
     580             :                 }
     581             :                 else
     582             :                 {
     583         233 :                     bFoundValidAssocField = true;
     584             :                 }
     585             :             }
     586        1788 :             if (bSkipRecord)
     587           0 :                 continue;
     588             :         }
     589             : 
     590             :         // First (inner) pass over attributes of the current record
     591             :         // to fill asS101AttrDefs, and do all needed sanity checks
     592        2351 :         if (!IngestAttributes(poRecord, iRecord, pszIDFieldName,
     593             :                               pszAttrFieldName, asS101AttrDefs))
     594           8 :             return false;
     595             : 
     596        2343 :         mapPathToCount.clear();
     597             : 
     598             :         // Update oMapFieldTypes with attributes found in this record
     599        2842 :         for (const auto &sAttrDef : asS101AttrDefs)
     600             :         {
     601         500 :             if (sAttrDef.bIsParent || sAttrDef.oReversedPath.empty())
     602          85 :                 continue;
     603             : 
     604             :             // Check that the top-level part of the attribute is expected for
     605             :             // that feature type (using feature catalog)
     606         415 :             if (psFeatureType)
     607             :             {
     608             :                 const auto oIterNATC =
     609           0 :                     m_attributeCodes.find(sAttrDef.oReversedPath.back().first);
     610           0 :                 if (oIterNATC != m_attributeCodes.end())
     611             :                 {
     612           0 :                     const std::string &osAttrCode = oIterNATC->second;
     613           0 :                     if (!cpl::contains(psFeatureType->attributeBindings,
     614             :                                        osAttrCode))
     615             :                     {
     616           0 :                         if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     617             :                                 "%s: attribute code %s not expected in feature "
     618             :                                 "type %s",
     619             :                                 GetErrorContext(), osAttrCode.c_str(),
     620             :                                 psFeatureType->code.c_str())))
     621             :                         {
     622           0 :                             return false;
     623             :                         }
     624             :                     }
     625             :                 }
     626             :             }
     627             : 
     628             :             const auto key =
     629         415 :                 std::make_pair(sAttrDef.oReversedPath, sAttrDef.iField);
     630         415 :             ++mapPathToCount[key];
     631             : 
     632             :             // Must be kept in that scope to create a OGR attribute even if
     633             :             // there is no field value
     634         415 :             auto &sOGRAttrDef = oMapFieldTypes[key];
     635             : 
     636         415 :             std::string typeFromCatalog;
     637         415 :             if (m_poFeatureCatalog)
     638             :             {
     639             :                 const auto oIterNATC =
     640         415 :                     m_attributeCodes.find(sAttrDef.oReversedPath.front().first);
     641         415 :                 if (oIterNATC != m_attributeCodes.end())
     642             :                 {
     643             :                     const auto &oMap =
     644         407 :                         m_poFeatureCatalog->GetSimpleAttributes();
     645         407 :                     const std::string &osAttrCode = oIterNATC->second;
     646         407 :                     const auto oIterAttr = oMap.find(osAttrCode);
     647         407 :                     if (oIterAttr != oMap.end())
     648             :                     {
     649         398 :                         const auto &attrDef = oIterAttr->second;
     650         398 :                         typeFromCatalog = attrDef.type;
     651             : 
     652         398 :                         sOGRAttrDef.osLongerName = attrDef.name;
     653             : 
     654         398 :                         if (typeFromCatalog ==
     655             :                             OGRS101FeatureCatalog::VALUE_TYPE_ENUMERATION)
     656             :                         {
     657          45 :                             sOGRAttrDef.osFieldDomainName = osAttrCode;
     658             : 
     659          45 :                             if (!sAttrDef.osVal.empty())
     660             :                             {
     661             :                                 // Checks that the coded value is an allowed code.
     662          45 :                                 const int nCode = atoi(sAttrDef.osVal.c_str());
     663          45 :                                 if (!cpl::contains(attrDef.enumeratedValues,
     664             :                                                    nCode))
     665             :                                 {
     666           2 :                                     if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     667             :                                             "%s: value %s does not belong to "
     668             :                                             "enumeration of attribute code %s",
     669             :                                             GetErrorContext(),
     670             :                                             sAttrDef.osVal.c_str(),
     671             :                                             osAttrCode.c_str())))
     672             :                                     {
     673           1 :                                         return false;
     674             :                                     }
     675             :                                 }
     676             :                             }
     677             : 
     678             :                             // Check if field domain exists. If not, create it.
     679          44 :                             if (!cpl::contains(oMapFieldDomains, osAttrCode))
     680             :                             {
     681          68 :                                 std::vector<OGRCodedValue> asValues;
     682         884 :                                 for (const auto &[code, value] :
     683         918 :                                      attrDef.enumeratedValues)
     684             :                                 {
     685             :                                     OGRCodedValue codedValue;
     686         442 :                                     codedValue.pszCode =
     687         442 :                                         CPLStrdup(CPLSPrintf("%d", code));
     688         442 :                                     codedValue.pszValue =
     689         442 :                                         CPLStrdup(value.c_str());
     690         442 :                                     asValues.push_back(std::move(codedValue));
     691             :                                 }
     692             :                                 auto poFieldDomain =
     693             :                                     std::make_unique<OGRCodedFieldDomain>(
     694           0 :                                         osAttrCode, attrDef.name, OFTString,
     695          68 :                                         OFSTNone, std::move(asValues));
     696          34 :                                 oMapFieldDomains[osAttrCode] =
     697          68 :                                     std::move(poFieldDomain);
     698             :                             }
     699             :                         }
     700             :                     }
     701             :                 }
     702             :             }
     703             : 
     704         414 :             sOGRAttrDef.bMultipleFields = sAttrDef.bMultipleFields;
     705             : 
     706         414 :             if (!sAttrDef.osVal.empty())
     707             :             {
     708         405 :                 const bool bNewAttrIsMultiValued = mapPathToCount[key] > 1;
     709         405 :                 if (bNewAttrIsMultiValued)
     710          13 :                     sOGRAttrDef.bIsMultiValued = true;
     711         405 :                 const auto eCPLType = CPLGetValueType(sAttrDef.osVal.c_str());
     712         487 :                 auto eOGRType = eCPLType == CPL_VALUE_STRING    ? OFTString
     713          82 :                                 : eCPLType == CPL_VALUE_INTEGER ? OFTInteger
     714             :                                                                 : OFTReal;
     715             : 
     716             :                 // Is it YYYYMMDD date ?
     717          57 :                 if (eOGRType == OFTInteger && sAttrDef.osVal.size() == 8 &&
     718         462 :                     sAttrDef.osVal[4] <= '1' && sAttrDef.osVal[6] <= '3')
     719             :                 {
     720             :                     const auto it = m_attributeCodes.find(
     721           0 :                         sAttrDef.oReversedPath.front().first);
     722           0 :                     if (it != m_attributeCodes.end())
     723             :                     {
     724           0 :                         if (cpl::ends_with(it->second, "Date"))
     725             :                         {
     726           0 :                             eOGRType = OFTDate;
     727             :                         }
     728             :                     }
     729             :                 }
     730             :                 // Is it YYYY---- truncated date ?
     731         323 :                 else if (eOGRType == OFTString && sAttrDef.osVal.size() == 8 &&
     732           0 :                          sAttrDef.osVal[4] == '-' && sAttrDef.osVal[5] == '-' &&
     733         728 :                          sAttrDef.osVal[6] == '-' && sAttrDef.osVal[7] == '-')
     734             :                 {
     735             :                     const auto it = m_attributeCodes.find(
     736           0 :                         sAttrDef.oReversedPath.front().first);
     737           0 :                     if (it != m_attributeCodes.end())
     738             :                     {
     739           0 :                         if (cpl::ends_with(it->second, "dateStart") ||
     740           0 :                             cpl::ends_with(it->second, "dateEnd"))
     741             :                         {
     742           0 :                             eOGRType = OFTDate;
     743             :                         }
     744             :                     }
     745             :                 }
     746             :                 // Is it time format ? ("094500", "094500", "094500+0100")
     747         728 :                 else if ((eOGRType == OFTInteger || eOGRType == OFTString) &&
     748         675 :                          sAttrDef.osVal.size() >= 6 &&
     749         295 :                          sAttrDef.osVal.size() <= 11 &&
     750         173 :                          std::all_of(sAttrDef.osVal.begin(),
     751         173 :                                      sAttrDef.osVal.begin() + 6, [](char c)
     752         988 :                                      { return c >= '0' && c <= '9'; }) &&
     753           1 :                          (sAttrDef.osVal.size() == 6 ||
     754           0 :                           (sAttrDef.osVal.size() == 7 &&
     755           0 :                            sAttrDef.osVal[6] == 'Z') ||
     756           0 :                           (sAttrDef.osVal.size() == 11 &&
     757           0 :                            (sAttrDef.osVal[6] == '+' ||
     758           0 :                             sAttrDef.osVal[6] == '-'))))
     759             :                 {
     760             :                     const auto it = m_attributeCodes.find(
     761           1 :                         sAttrDef.oReversedPath.front().first);
     762           1 :                     if (it != m_attributeCodes.end())
     763             :                     {
     764           1 :                         if (cpl::starts_with(it->second, "time"))
     765             :                         {
     766           0 :                             eOGRType = OFTTime;
     767             :                         }
     768             :                     }
     769             :                 }
     770             : 
     771         405 :                 if (!sOGRAttrDef.oeType.has_value())
     772             :                 {
     773         386 :                     sOGRAttrDef.oeType = eOGRType;
     774         431 :                     if (eOGRType == OFTInteger &&
     775          45 :                         typeFromCatalog ==
     776             :                             OGRS101FeatureCatalog::VALUE_TYPE_BOOLEAN)
     777             :                     {
     778           0 :                         sOGRAttrDef.eSubType = OFSTBoolean;
     779             :                     }
     780             :                 }
     781          25 :                 else if (eOGRType == OFTString &&
     782           6 :                          *sOGRAttrDef.oeType != OFTString)
     783             :                 {
     784           0 :                     sOGRAttrDef.oeType = OFTString;
     785           0 :                     sOGRAttrDef.eSubType = OFSTNone;
     786             :                 }
     787          20 :                 else if (eOGRType == OFTReal &&
     788           1 :                          *sOGRAttrDef.oeType == OFTInteger)
     789             :                 {
     790           1 :                     sOGRAttrDef.oeType = OFTReal;
     791           1 :                     sOGRAttrDef.eSubType = OFSTNone;
     792             :                 }
     793             :             }
     794             :         }
     795             :     }
     796             : 
     797        1677 :     if (bFoundValidAssocField)
     798             :     {
     799         462 :         for (int i = 0; i < nMaxFieldRepeat; ++i)
     800             :         {
     801             :             const std::string osSuffix =
     802         470 :                 nMaxFieldRepeat > 1 ? CPLSPrintf("[%d]", i + 1) : "";
     803             : 
     804         235 :             if (!bIsINAS)
     805             :             {
     806             :                 OGRFieldDefn oFieldDefn(
     807          40 :                     (OGR_FIELD_NAME_REF_FEAT_LAYER_NAME + osSuffix).c_str(),
     808          80 :                     OFTString);
     809          40 :                 oFeatureDefn.AddFieldDefn(&oFieldDefn);
     810             :             }
     811             : 
     812             :             {
     813             :                 OGRFieldDefn oFieldDefn(
     814             :                     ((bIsINAS ? OGR_FIELD_NAME_REF_INFO_RID
     815         235 :                               : OGR_FIELD_NAME_REF_FEAT_RID) +
     816             :                      osSuffix)
     817             :                         .c_str(),
     818         470 :                     OFTInteger);
     819         235 :                 oFeatureDefn.AddFieldDefn(&oFieldDefn);
     820             :             }
     821             :             {
     822             :                 OGRFieldDefn oFieldDefn(
     823         235 :                     ((bIsINAS ? OGR_FIELD_NAME_NIAC : OGR_FIELD_NAME_NFAC) +
     824             :                      osSuffix)
     825             :                         .c_str(),
     826         470 :                     OFTString);
     827         235 :                 oFeatureDefn.AddFieldDefn(&oFieldDefn);
     828             :             }
     829             :             {
     830             :                 OGRFieldDefn oFieldDefn(
     831             :                     ((bIsINAS ? OGR_FIELD_NAME_NARC
     832         235 :                               : OGR_FIELD_NAME_FEATURE_NARC) +
     833             :                      osSuffix)
     834             :                         .c_str(),
     835         470 :                     OFTString);
     836         235 :                 oFeatureDefn.AddFieldDefn(&oFieldDefn);
     837             :             }
     838             :         }
     839             :     }
     840             : 
     841             :     // Final pass to transform oMapFieldTypes into OGRField instances.
     842        2063 :     for (const auto &[oReversedPathAndFieldIndex, sOGRAttrDef] : oMapFieldTypes)
     843             :     {
     844         386 :         const auto &oReversedPath = oReversedPathAndFieldIndex.first;
     845         386 :         const int iField = oReversedPathAndFieldIndex.second;
     846             : 
     847             :         const std::string osAttrName =
     848             :             BuildFieldName(oReversedPath, pszAttrFieldName, iField,
     849         772 :                            sOGRAttrDef.bMultipleFields, pszIDFieldName);
     850             : 
     851         386 :         OGRFieldType eType = OFTString;
     852         386 :         if (sOGRAttrDef.oeType.has_value())
     853             :         {
     854         379 :             if (sOGRAttrDef.bIsMultiValued)
     855             :             {
     856          14 :                 eType = (*sOGRAttrDef.oeType) == OFTInteger ? OFTIntegerList
     857           1 :                         : (*sOGRAttrDef.oeType) == OFTReal  ? OFTRealList
     858             :                                                             : OFTStringList;
     859             :             }
     860             :             else
     861             :             {
     862         366 :                 eType = *sOGRAttrDef.oeType;
     863             :             }
     864             :         }
     865           7 :         else if (sOGRAttrDef.bIsMultiValued)
     866             :         {
     867           0 :             eType = OFTStringList;
     868             :         }
     869             : 
     870         386 :         if (oFeatureDefn.GetFieldIndex(osAttrName.c_str()) >= 0)
     871             :         {
     872           0 :             if (osAttrName == OGR_FIELD_NAME_SMIN ||
     873           0 :                 osAttrName == OGR_FIELD_NAME_SMAX)
     874             :             {
     875             :                 // 101FR00368570.000 has scaleMinimum as an ATTR, and
     876             :                 // doesn't define SPAS.SMIN
     877             :             }
     878             :             else
     879             :             {
     880           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
     881             :                          "Layer %s: %s field already exists",
     882           0 :                          oFeatureDefn.GetName(), osAttrName.c_str());
     883             :             }
     884             :         }
     885             :         else
     886             :         {
     887         772 :             OGRFieldDefn oFieldDefn(osAttrName.c_str(), eType);
     888         386 :             oFieldDefn.SetSubType(sOGRAttrDef.eSubType);
     889         386 :             if (!sOGRAttrDef.osLongerName.empty() && oReversedPath.size() == 1)
     890         296 :                 oFieldDefn.SetAlternativeName(sOGRAttrDef.osLongerName.c_str());
     891         386 :             if (!sOGRAttrDef.osFieldDomainName.empty())
     892          33 :                 oFieldDefn.SetDomainName(sOGRAttrDef.osFieldDomainName.c_str());
     893         386 :             oFeatureDefn.AddFieldDefn(&oFieldDefn);
     894             :         }
     895             :     }
     896             : 
     897        1677 :     return true;
     898             : }
     899             : 
     900             : /************************************************************************/
     901             : /*                       FillFeatureAttributes()                        */
     902             : /************************************************************************/
     903             : 
     904             : /** Fill attribute fields of the provided feature.
     905             :  */
     906        4752 : bool OGRS101Reader::FillFeatureAttributes(const DDFRecordIndex &oIndex,
     907             :                                           int iRecord,
     908             :                                           const char *pszAttrFieldName,
     909             :                                           OGRFeature &oFeature) const
     910             : {
     911        4752 :     const auto poRecord = oIndex.GetByIndex(iRecord);
     912        4752 :     if (!poRecord)
     913             :     {
     914           0 :         return EMIT_ERROR("Invalid record number");
     915             :     }
     916             : 
     917        4752 :     const auto poIDField = poRecord->GetField(0);
     918        4752 :     CPLAssert(poIDField);
     919        4752 :     const char *pszIDFieldName = poIDField->GetFieldDefn()->GetName();
     920             : 
     921             :     const int nRCID =
     922        4752 :         poRecord->GetIntSubfield(pszIDFieldName, 0, RCID_SUBFIELD, 0);
     923        4752 :     if (nRCID < 1 && !EMIT_ERROR_OR_WARNING(CPLSPrintf(
     924             :                          "Wrong value %d for RCID subfield of %s field.", nRCID,
     925             :                          pszIDFieldName)))
     926             :     {
     927           0 :         return false;
     928             :     }
     929        4752 :     oFeature.SetField(OGR_FIELD_NAME_RECORD_ID, nRCID);
     930             : 
     931             :     const int nRVER =
     932        4752 :         poRecord->GetIntSubfield(pszIDFieldName, 0, RVER_SUBFIELD, 0);
     933        4752 :     oFeature.SetField(OGR_FIELD_NAME_RECORD_VERSION, nRVER);
     934             : 
     935             :     const int nRUIN =
     936        4752 :         poRecord->GetIntSubfield(pszIDFieldName, 0, RUIN_SUBFIELD, 0);
     937        4752 :     if (nRUIN != INSTRUCTION_INSERT)
     938             :     {
     939           0 :         return EMIT_ERROR_OR_WARNING(
     940             :             CPLSPrintf("Wrong value %d for RUIN subfield of %s field.", nRUIN,
     941             :                        pszIDFieldName));
     942             :     }
     943             : 
     944        4752 :     const auto poFeatureDefn = oFeature.GetDefnRef();
     945             : 
     946             :     // First pass to detect which attribute entries correspond to parent nodes
     947             :     // that don't directly hold a field value.
     948        9504 :     std::vector<S101AttrDef> asS101AttrDefs;
     949        4752 :     if (!IngestAttributes(poRecord, iRecord, pszIDFieldName, pszAttrFieldName,
     950             :                           asS101AttrDefs))
     951           0 :         return false;
     952             : 
     953             :     struct OGRFieldIndexTag
     954             :     {
     955             :     };
     956             : 
     957             :     using OGRFieldIndex = cpl::IntWrapper<OGRFieldIndexTag>;
     958             : 
     959        9504 :     std::map<OGRFieldIndex, CPLStringList> stringListAttrs;
     960        9504 :     std::map<OGRFieldIndex, std::vector<int>> intListAttrs;
     961        9504 :     std::map<OGRFieldIndex, std::vector<double>> doubleListAttrs;
     962             :     // Second pass to set single-valued attributes, or store multi-valued
     963             :     // attributes in the 3 above maps.
     964        6375 :     for (const auto &sAttrDef : asS101AttrDefs)
     965             :     {
     966        1623 :         if (sAttrDef.bIsParent || sAttrDef.oReversedPath.empty())
     967         418 :             continue;
     968             : 
     969             :         const std::string osAttrName = BuildFieldName(
     970        1205 :             sAttrDef.oReversedPath, pszAttrFieldName, sAttrDef.iField,
     971        1205 :             sAttrDef.bMultipleFields, pszIDFieldName);
     972             : 
     973             :         const int iOGRFieldIdx =
     974        1205 :             poFeatureDefn->GetFieldIndex(osAttrName.c_str());
     975             :         // Shouldn't normally happen given all preceding run logic
     976        1205 :         CPLAssert(iOGRFieldIdx >= 0);
     977        1205 :         const auto eType = poFeatureDefn->GetFieldDefn(iOGRFieldIdx)->GetType();
     978        1205 :         const char *const pszATVL = sAttrDef.osVal.c_str();
     979        1205 :         switch (eType)
     980             :         {
     981         227 :             case OFTInteger:
     982             :             case OFTIntegerList:
     983             :             {
     984         227 :                 if (pszATVL[0])
     985             :                 {
     986         227 :                     const char *const last = pszATVL + sAttrDef.osVal.size();
     987         227 :                     int nVal = -1;
     988         227 :                     auto [ptr, ec] = std::from_chars(pszATVL, last, nVal);
     989         227 :                     if (ec == std::errc() && ptr == last)
     990             :                     {
     991         226 :                         if (eType == OFTInteger)
     992         112 :                             oFeature.SetField(iOGRFieldIdx, nVal);
     993             :                         else
     994         114 :                             intListAttrs[iOGRFieldIdx].push_back(nVal);
     995             :                     }
     996           1 :                     else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
     997             :                                  "Record index=%d of %s, attribute %s: "
     998             :                                  "non integer value '%s'.",
     999             :                                  iRecord, pszIDFieldName, osAttrName.c_str(),
    1000             :                                  pszATVL)))
    1001             :                     {
    1002           0 :                         return false;
    1003             :                     }
    1004             :                 }
    1005           0 :                 else if (eType == OFTIntegerList)
    1006             :                 {
    1007           0 :                     intListAttrs[iOGRFieldIdx].push_back(
    1008           0 :                         std::numeric_limits<int>::min());
    1009             :                 }
    1010         227 :                 break;
    1011             :             }
    1012             : 
    1013         114 :             case OFTReal:
    1014             :             case OFTRealList:
    1015             :             {
    1016         114 :                 if (pszATVL[0])
    1017             :                 {
    1018         114 :                     const char *const last = pszATVL + sAttrDef.osVal.size();
    1019         114 :                     double dfVal = -1;
    1020         114 :                     const fast_float::parse_options options{
    1021             :                         fast_float::chars_format::general, '.'};
    1022             :                     auto [ptr, ec] = fast_float::from_chars_advanced(
    1023         114 :                         pszATVL, last, dfVal, options);
    1024         114 :                     if (ec == std::errc() && ptr == last)
    1025             :                     {
    1026         113 :                         if (eType == OFTReal)
    1027         111 :                             oFeature.SetField(iOGRFieldIdx, dfVal);
    1028             :                         else
    1029           2 :                             doubleListAttrs[iOGRFieldIdx].push_back(dfVal);
    1030             :                     }
    1031           1 :                     else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
    1032             :                                  "Record index=%d of %s, attribute %s: "
    1033             :                                  "non double value '%s'.",
    1034             :                                  iRecord, pszIDFieldName, osAttrName.c_str(),
    1035             :                                  pszATVL)))
    1036             :                     {
    1037           0 :                         return false;
    1038             :                     }
    1039             :                 }
    1040           0 :                 else if (eType == OFTRealList)
    1041             :                 {
    1042           0 :                     doubleListAttrs[iOGRFieldIdx].push_back(
    1043           0 :                         std::numeric_limits<double>::quiet_NaN());
    1044             :                 }
    1045         114 :                 break;
    1046             :             }
    1047             : 
    1048         864 :             case OFTString:
    1049             :             case OFTStringList:
    1050             :             {
    1051           0 :                 std::unique_ptr<char, VSIFreeReleaser> pszTmpStr;
    1052         864 :                 const char *pszStr = pszATVL;
    1053         864 :                 if (!CPLIsUTF8(pszATVL,
    1054         864 :                                static_cast<int>(sAttrDef.osVal.size())))
    1055             :                 {
    1056             :                     // Not supposed to happen in compliant products
    1057           1 :                     if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
    1058             :                             "Record index=%d of %s, attribute %s: non "
    1059             :                             "UTF-8 string '%s'.",
    1060             :                             iRecord, pszIDFieldName, osAttrName.c_str(),
    1061             :                             pszATVL)))
    1062             :                     {
    1063           0 :                         return false;
    1064             :                     }
    1065           1 :                     pszTmpStr.reset(CPLUTF8ForceToASCII(pszATVL, '_'));
    1066           1 :                     pszStr = pszTmpStr.get();
    1067             :                 }
    1068         864 :                 if (eType == OFTString)
    1069         864 :                     oFeature.SetField(iOGRFieldIdx, pszStr);
    1070             :                 else
    1071           0 :                     stringListAttrs[iOGRFieldIdx].push_back(pszStr);
    1072         864 :                 break;
    1073             :             }
    1074             : 
    1075           0 :             case OFTDate:
    1076             :             {
    1077           0 :                 if (sAttrDef.osVal.size() == 8 &&
    1078           0 :                     std::all_of(sAttrDef.osVal.begin(), sAttrDef.osVal.end(),
    1079           0 :                                 [](char c) { return c >= '0' && c <= '9'; }))
    1080             :                 {
    1081           0 :                     const int nYear = (sAttrDef.osVal[0] - '0') * 1000 +
    1082           0 :                                       (sAttrDef.osVal[1] - '0') * 100 +
    1083           0 :                                       (sAttrDef.osVal[2] - '0') * 10 +
    1084           0 :                                       (sAttrDef.osVal[3] - '0');
    1085           0 :                     const int nMonth = (sAttrDef.osVal[4] - '0') * 10 +
    1086           0 :                                        (sAttrDef.osVal[5] - '0');
    1087           0 :                     const int nDay = (sAttrDef.osVal[6] - '0') * 10 +
    1088           0 :                                      (sAttrDef.osVal[7] - '0');
    1089           0 :                     oFeature.SetField(iOGRFieldIdx, nYear, nMonth, nDay);
    1090             :                 }
    1091           0 :                 else if (sAttrDef.osVal.size() == 8 &&
    1092           0 :                          sAttrDef.osVal[0] >= '0' && sAttrDef.osVal[0] <= '9' &&
    1093           0 :                          sAttrDef.osVal[1] >= '0' && sAttrDef.osVal[1] <= '9' &&
    1094           0 :                          sAttrDef.osVal[2] >= '0' && sAttrDef.osVal[2] <= '9' &&
    1095           0 :                          sAttrDef.osVal[3] >= '0' && sAttrDef.osVal[3] <= '9' &&
    1096           0 :                          sAttrDef.osVal[4] == '-' && sAttrDef.osVal[5] == '-' &&
    1097           0 :                          sAttrDef.osVal[6] == '-' && sAttrDef.osVal[7] == '-')
    1098             :                 {
    1099           0 :                     const int nYear = (sAttrDef.osVal[0] - '0') * 1000 +
    1100           0 :                                       (sAttrDef.osVal[1] - '0') * 100 +
    1101           0 :                                       (sAttrDef.osVal[2] - '0') * 10 +
    1102           0 :                                       (sAttrDef.osVal[3] - '0');
    1103           0 :                     if (cpl::ends_with(osAttrName, "dateEnd"))
    1104             :                     {
    1105           0 :                         oFeature.SetField(iOGRFieldIdx, nYear, 12, 31);
    1106             :                     }
    1107             :                     else
    1108             :                     {
    1109           0 :                         oFeature.SetField(iOGRFieldIdx, nYear, 1, 1);
    1110             :                     }
    1111             :                 }
    1112           0 :                 break;
    1113             :             }
    1114             : 
    1115           0 :             case OFTTime:
    1116             :             {
    1117           0 :                 if (sAttrDef.osVal.size() >= 6 &&
    1118           0 :                     std::all_of(sAttrDef.osVal.begin(),
    1119           0 :                                 sAttrDef.osVal.begin() + 6,
    1120           0 :                                 [](char c) { return c >= '0' && c <= '9'; }))
    1121             :                 {
    1122           0 :                     const int nHour = (sAttrDef.osVal[0] - '0') * 10 +
    1123           0 :                                       (sAttrDef.osVal[1] - '0');
    1124           0 :                     const int nMin = (sAttrDef.osVal[2] - '0') * 10 +
    1125           0 :                                      (sAttrDef.osVal[3] - '0');
    1126           0 :                     const int nSec = (sAttrDef.osVal[4] - '0') * 10 +
    1127           0 :                                      (sAttrDef.osVal[5] - '0');
    1128           0 :                     int nTZFlag = OGR_TZFLAG_UNKNOWN;
    1129           0 :                     if (sAttrDef.osVal.size() == 7 && sAttrDef.osVal[6] == 'Z')
    1130           0 :                         nTZFlag = OGR_TZFLAG_UTC;
    1131           0 :                     else if (sAttrDef.osVal.size() == 11)
    1132             :                     {
    1133           0 :                         const int nTZHour = (sAttrDef.osVal[7] - '0') * 10 +
    1134           0 :                                             (sAttrDef.osVal[8] - '0');
    1135           0 :                         const int nTZMin = (sAttrDef.osVal[9] - '0') * 10 +
    1136           0 :                                            (sAttrDef.osVal[10] - '0');
    1137           0 :                         const int n15Minutes = (nTZHour * 60 + nTZMin) / 15;
    1138           0 :                         if (sAttrDef.osVal[6] == '+')
    1139           0 :                             nTZFlag = OGR_TZFLAG_UTC + n15Minutes;
    1140             :                         else
    1141           0 :                             nTZFlag = OGR_TZFLAG_UTC - n15Minutes;
    1142             :                     }
    1143           0 :                     oFeature.SetField(iOGRFieldIdx, 0, 0, 0, nHour, nMin,
    1144             :                                       static_cast<float>(nSec), nTZFlag);
    1145             :                 }
    1146           0 :                 break;
    1147             :             }
    1148             : 
    1149           0 :             default:
    1150           0 :                 CPLAssert(false);
    1151             :         }
    1152             :     }
    1153             : 
    1154             :     // Set multi-valued fields.
    1155        4752 :     for (const auto &[iOGRFieldIdx, aosStrings] : stringListAttrs)
    1156           0 :         oFeature.SetField(static_cast<int>(iOGRFieldIdx), aosStrings.List());
    1157             : 
    1158        4809 :     for (const auto &[iOGRFieldIdx, anVals] : intListAttrs)
    1159         114 :         oFeature.SetField(static_cast<int>(iOGRFieldIdx),
    1160          57 :                           static_cast<int>(anVals.size()), anVals.data());
    1161             : 
    1162        4753 :     for (const auto &[iOGRFieldIdx, adfVals] : doubleListAttrs)
    1163           2 :         oFeature.SetField(static_cast<int>(iOGRFieldIdx),
    1164           1 :                           static_cast<int>(adfVals.size()), adfVals.data());
    1165             : 
    1166        4752 :     return true;
    1167             : }

Generated by: LCOV version 1.14