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 2081 : 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 4162 : 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 2081 : };
60 :
61 : const AttrCode nLargestNATC(
62 2081 : !m_attributeCodes.empty() ? m_attributeCodes.rbegin()->first : 0);
63 : const bool bAttributeCodesSequential =
64 4159 : !m_attributeCodes.empty() && m_attributeCodes.begin()->first == 1 &&
65 2078 : static_cast<size_t>(static_cast<int>(nLargestNATC)) ==
66 2078 : m_attributeCodes.size();
67 :
68 2081 : const int nRepeatCount = poATTRField->GetRepeatCount();
69 2081 : AttrCode nLastNATC = -1;
70 2081 : AttrRepeat nLastATIX = -1;
71 2081 : AttrIndex nLastPAIX = -1;
72 :
73 : // Find multi-valued parts of the path
74 4162 : std::map<std::pair<AttrCode, AttrIndex>, int> oMapOccurrenceCount;
75 5260 : for (AttrIndex iATTR = 0; iATTR < nRepeatCount; ++iATTR)
76 : {
77 : const auto GetIntSubfield =
78 12716 : [poRecord, poATTRField, iATTR](const char *pszSubFieldName)
79 : {
80 6358 : return poRecord->GetIntSubfield(poATTRField, pszSubFieldName,
81 6358 : static_cast<int>(iATTR));
82 3179 : };
83 :
84 3179 : ++oMapOccurrenceCount[{GetIntSubfield(NATC_SUBFIELD),
85 3179 : GetIntSubfield(PAIX_SUBFIELD)}];
86 : }
87 :
88 2081 : const size_t nS101AttrDefsBaseIdx = asS101AttrDefs.size();
89 :
90 5239 : for (AttrIndex iATTR = 0; iATTR < nRepeatCount; ++iATTR)
91 : {
92 : const auto GetIntSubfield =
93 25300 : [poRecord, poATTRField, iATTR](const char *pszSubFieldName)
94 : {
95 12650 : return poRecord->GetIntSubfield(poATTRField, pszSubFieldName,
96 12650 : static_cast<int>(iATTR));
97 3166 : };
98 :
99 3166 : const int nATIN = GetIntSubfield(ATIN_SUBFIELD);
100 3166 : 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 3163 : const AttrCode nNATC(GetIntSubfield(NATC_SUBFIELD));
116 3192 : 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 3162 : const AttrRepeat nATIX(GetIntSubfield(ATIX_SUBFIELD));
130 3162 : 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 3159 : 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 3159 : 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 3156 : if (nPAIX == nLastPAIX)
168 : {
169 420 : if (nNATC == nLastNATC)
170 : {
171 115 : if (nATIX != nLastATIX + 1 &&
172 115 : !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 308 : 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 3159 : 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 3153 : const bool bIsMultiValued = oMapOccurrenceCount[{nNATC, nPAIX}] > 1;
206 3153 : PathVector oReversedPath{{nNATC, bIsMultiValued ? nATIX : 0}};
207 3153 : if (nPAIX > 0)
208 : {
209 : // Assertion can't trigger because nPAIX <= iATTR < asS101AttrDefs.size() - nS101AttrDefsBaseIdx
210 1555 : CPLAssert(nS101AttrDefsBaseIdx +
211 : static_cast<size_t>(static_cast<int>(nPAIX) - 1) <
212 : asS101AttrDefs.size());
213 1555 : auto &sParentAttrDef = asS101AttrDefs[nS101AttrDefsBaseIdx +
214 1555 : static_cast<int>(nPAIX) - 1];
215 1555 : sParentAttrDef.bIsParent = true;
216 1558 : 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 1554 : oReversedPath.insert(oReversedPath.end(),
229 : sParentAttrDef.oReversedPath.begin(),
230 3108 : sParentAttrDef.oReversedPath.end());
231 : #if defined(__GNUC__)
232 : #pragma GCC diagnostic pop
233 : #endif
234 : }
235 :
236 3152 : const char *pszATVL = poRecord->GetStringSubfield(
237 : poATTRField, ATVL_SUBFIELD, static_cast<int>(iATTR));
238 3152 : 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 3152 : S101AttrDef sAttrDef;
246 3152 : sAttrDef.iField = iField;
247 3152 : sAttrDef.bMultipleFields = bMultipleFields;
248 3152 : sAttrDef.oReversedPath = std::move(oReversedPath);
249 3152 : if (pszATVL)
250 3152 : sAttrDef.osVal = pszATVL;
251 3152 : asS101AttrDefs.push_back(std::move(sAttrDef));
252 :
253 3152 : nLastNATC = nNATC;
254 3152 : nLastATIX = nATIX;
255 3152 : nLastPAIX = nPAIX;
256 : }
257 :
258 2073 : 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 9302 : bool OGRS101Reader::IngestAttributes(
269 : const DDFRecord *poRecord, int iRecord, const char *pszIDFieldName,
270 : const char *pszAttrFieldName,
271 : std::vector<S101AttrDef> &asS101AttrDefs) const
272 : {
273 9302 : asS101AttrDefs.clear();
274 :
275 18604 : const auto apoATTRFields = poRecord->GetFields(pszAttrFieldName);
276 9302 : const int nATTRFieldCount = static_cast<int>(apoATTRFields.size());
277 9302 : bool bSuccess = true;
278 9302 : if (EQUAL(pszAttrFieldName, ATTR_FIELD))
279 : {
280 2931 : for (int iATTRField = 0; bSuccess && iATTRField < nATTRFieldCount;
281 : ++iATTRField)
282 : {
283 950 : bSuccess = IngestAttributes(poRecord, iRecord, pszIDFieldName,
284 : pszAttrFieldName,
285 950 : apoATTRFields[iATTRField], iATTRField,
286 : nATTRFieldCount > 1, asS101AttrDefs);
287 : }
288 : }
289 : else
290 : {
291 8452 : for (int iATTRField = 0; bSuccess && iATTRField < nATTRFieldCount;
292 : ++iATTRField)
293 : {
294 1131 : const auto poINASOrFASCField = apoATTRFields[iATTRField];
295 1131 : 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 1131 : const auto poATTRField = poINASOrFASCField->GetParts()[1].get();
307 :
308 1131 : bSuccess = IngestAttributes(
309 : poRecord, iRecord, pszIDFieldName, pszAttrFieldName,
310 : poATTRField, iATTRField, nATTRFieldCount > 1, asS101AttrDefs);
311 : }
312 : }
313 :
314 12460 : for (auto &sAttrDef : asS101AttrDefs)
315 : {
316 3158 : if (sAttrDef.bIsParent || sAttrDef.oReversedPath.empty())
317 813 : continue;
318 :
319 : // For last component, set the repetition part to 0, to be
320 : // actually able to detect multi-valued attributes!
321 2345 : sAttrDef.oReversedPath.front().second = 0;
322 : }
323 :
324 9302 : return bSuccess;
325 : }
326 :
327 : /************************************************************************/
328 : /* BuildFieldName() */
329 : /************************************************************************/
330 :
331 : /** Returns a string with the concatenation of the parts, in reverse order.
332 : */
333 2301 : std::string OGRS101Reader::BuildFieldName(const PathVector &oReversedPath,
334 : const char *pszAttrFieldName,
335 : int iField, bool bMultipleFields,
336 : const char *pszIDFieldName) const
337 : {
338 2301 : std::string osAttrName;
339 6390 : for (size_t i = oReversedPath.size(); i > 0;)
340 : {
341 4089 : --i;
342 4089 : const auto &oPathComp = oReversedPath[i];
343 4089 : if (!osAttrName.empty())
344 1788 : osAttrName += '.';
345 4089 : const auto nCode = oPathComp.first;
346 4089 : const auto oIterNATC = m_attributeCodes.find(nCode);
347 4089 : if (oIterNATC == m_attributeCodes.end())
348 : {
349 42 : osAttrName += CPLSPrintf("code_%d", static_cast<int>(nCode));
350 : }
351 : else
352 : {
353 4047 : osAttrName += oIterNATC->second;
354 : }
355 :
356 4089 : 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 4089 : const auto &nRepeat = oPathComp.second;
365 4089 : if (nRepeat > 0)
366 : {
367 478 : osAttrName += '[';
368 478 : osAttrName += std::to_string(static_cast<int>(nRepeat));
369 478 : osAttrName += ']';
370 : }
371 : }
372 :
373 2301 : if (strcmp(pszAttrFieldName, ATTR_FIELD) != 0)
374 : {
375 537 : std::string osPrefix;
376 537 : if (strcmp(pszIDFieldName, IRID_FIELD) == 0)
377 : {
378 107 : osPrefix = "association";
379 : }
380 430 : else if (strcmp(pszIDFieldName, FRID_FIELD) == 0)
381 : {
382 348 : if (strcmp(pszAttrFieldName, INAS_FIELD) == 0)
383 174 : osPrefix = "infoAssociation";
384 : else
385 174 : osPrefix = "featureAssociation";
386 : }
387 537 : if (!osPrefix.empty())
388 : {
389 455 : if (bMultipleFields)
390 : {
391 194 : osPrefix += '[';
392 194 : osPrefix += std::to_string(iField + 1);
393 194 : osPrefix += ']';
394 : }
395 455 : osPrefix += '_';
396 : }
397 537 : osAttrName = osPrefix + osAttrName;
398 : }
399 :
400 2301 : 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 1989 : 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 1989 : 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 osDefinition{};
428 : std::string osFieldDomainName{};
429 : };
430 :
431 : using PathVectorAndAttrIdx = std::pair<PathVector, int>;
432 3978 : std::map<PathVectorAndAttrIdx, OGRAttrDef> oMapFieldTypes;
433 :
434 3978 : std::vector<S101AttrDef> asS101AttrDefs;
435 3978 : std::map<PathVectorAndAttrIdx, int> mapPathToCount;
436 1989 : bool bFoundValidAssocField = false;
437 :
438 : // Iterate over the records (in the index of interest) to fill the
439 : // oMapFieldTypes map object that will be afterwards translated as
440 : // OGR feature definition
441 : // If anRecordIndices is not empty, it defines the subset of record
442 : // indices to iterate over. This is used for geometry records that are
443 : // dispatched to different OGR layers depending on the CRS.
444 1989 : const int nRecords = anRecordIndices.empty()
445 3198 : ? oIndex.GetCount()
446 1209 : : static_cast<int>(anRecordIndices.size());
447 1989 : int nMaxFieldRepeat = 1;
448 4779 : for (int iter = 0; iter < nRecords; ++iter)
449 : {
450 : const int iRecord =
451 2806 : anRecordIndices.empty() ? iter : anRecordIndices[iter];
452 :
453 19 : const auto GetErrorContext = [pszIDFieldName, iRecord]()
454 : {
455 19 : return CPLSPrintf("Record index=%d of %s", iRecord, pszIDFieldName);
456 2806 : };
457 :
458 2806 : const auto poRecord = oIndex.GetByIndex(iRecord);
459 2806 : CPLAssert(poRecord);
460 :
461 : const int nRUIN =
462 2806 : poRecord->GetIntSubfield(pszIDFieldName, 0, RUIN_SUBFIELD, 0);
463 2806 : if (nRUIN != INSTRUCTION_INSERT)
464 : {
465 0 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf("%s: wrong value %d for RUIN "
466 : "subfield of %s field.",
467 : GetErrorContext(), nRUIN,
468 : pszIDFieldName)))
469 : {
470 16 : return false;
471 : }
472 0 : continue;
473 : }
474 :
475 2806 : if (!EQUAL(pszAttrFieldName, ATTR_FIELD))
476 : {
477 2173 : const auto apoFields = poRecord->GetFields(pszAttrFieldName);
478 2173 : bool bSkipRecord = false;
479 2173 : nMaxFieldRepeat =
480 2173 : std::max(nMaxFieldRepeat, static_cast<int>(apoFields.size()));
481 2173 : if (!apoFields.empty())
482 : {
483 284 : const DDFField *poField = apoFields[0];
484 :
485 : const RecordName nRRNM =
486 284 : poRecord->GetIntSubfield(poField, RRNM_SUBFIELD, 0);
487 284 : const RecordName nExpectedRRNM =
488 284 : bIsINAS ? RECORD_NAME_INFORMATION_TYPE
489 : : RECORD_NAME_FEATURE_TYPE;
490 284 : if (nRRNM != nExpectedRRNM)
491 : {
492 8 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
493 : "%s: Invalid value for RRNM subfield of %s field: "
494 : "got %d, expected %d.",
495 : GetErrorContext(), pszAttrFieldName,
496 : static_cast<int>(nRRNM),
497 : static_cast<int>(nExpectedRRNM))))
498 : {
499 7 : return false;
500 : }
501 : }
502 :
503 : const int nRRID =
504 280 : poRecord->GetIntSubfield(poField, RRID_SUBFIELD, 0);
505 516 : if ((bIsINAS &&
506 558 : !m_oInformationTypeRecordIndex.FindRecord(nRRID)) ||
507 278 : (!bIsINAS && !m_oFeatureTypeRecordIndex.FindRecord(nRRID)))
508 : {
509 2 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
510 : "%s: Invalid value %d for RRID subfield of %s "
511 : "field: "
512 : "does not match the record identifier of an "
513 : "existing "
514 : "%s record.",
515 : GetErrorContext(), static_cast<int>(nRRID),
516 : pszAttrFieldName,
517 : bIsINAS ? "InformationType" : "FeatureType")))
518 : {
519 1 : return false;
520 : }
521 : }
522 :
523 279 : if (bIsINAS)
524 : {
525 : const InfoAssocCode nNIAC =
526 235 : poRecord->GetIntSubfield(poField, NIAC_SUBFIELD, 0);
527 237 : if (!cpl::contains(m_informationAssociationCodes, nNIAC) &&
528 2 : !EMIT_ERROR_OR_WARNING(CPLSPrintf(
529 : "%s: cannot find attribute code %d in IACS field "
530 : "of the Dataset General Information Record.",
531 : GetErrorContext(), static_cast<int>(nNIAC))))
532 : {
533 1 : return false;
534 : }
535 : }
536 : else
537 : {
538 : const FeatureAssocCode nNFAC =
539 44 : poRecord->GetIntSubfield(poField, NFAC_SUBFIELD, 0);
540 45 : if (!cpl::contains(m_featureAssociationCodes, nNFAC) &&
541 1 : !EMIT_ERROR_OR_WARNING(CPLSPrintf(
542 : "%s: cannot find attribute code %d in NFAC field "
543 : "of the Dataset General Information Record.",
544 : GetErrorContext(), static_cast<int>(nNFAC))))
545 : {
546 0 : return false;
547 : }
548 : }
549 :
550 : const AssocRoleCode nNARC =
551 278 : poRecord->GetIntSubfield(poField, NARC_SUBFIELD, 0);
552 282 : if (!cpl::contains(m_associationRoleCodes, nNARC) &&
553 4 : !EMIT_ERROR_OR_WARNING(CPLSPrintf(
554 : "%s: cannot find attribute code %d in ARCS field "
555 : "of the Dataset General Information Record.",
556 : GetErrorContext(), static_cast<int>(nNARC))))
557 : {
558 1 : return false;
559 : }
560 :
561 277 : const char *pszSubFieldName =
562 : bIsINAS ? IUIN_SUBFIELD : FAUI_SUBFIELD;
563 : int nInstruction =
564 277 : poRecord->GetIntSubfield(poField, pszSubFieldName, 0);
565 277 : if (nInstruction == 0)
566 : {
567 : // For 101GB00GB302045.000, non conformant
568 0 : nInstruction = poRecord->GetIntSubfield(poField, "APUI", 0);
569 : }
570 277 : if (nInstruction != INSTRUCTION_INSERT)
571 : {
572 0 : if (!EMIT_ERROR_OR_WARNING(
573 : CPLSPrintf("%s: wrong value %d for %s "
574 : "subfield of %s field.",
575 : GetErrorContext(), nInstruction,
576 : pszSubFieldName, pszAttrFieldName)))
577 : {
578 0 : return false;
579 : }
580 0 : bSkipRecord = true;
581 : }
582 : else
583 : {
584 277 : bFoundValidAssocField = true;
585 : }
586 : }
587 2166 : if (bSkipRecord)
588 0 : continue;
589 : }
590 :
591 : // First (inner) pass over attributes of the current record
592 : // to fill asS101AttrDefs, and do all needed sanity checks
593 2799 : if (!IngestAttributes(poRecord, iRecord, pszIDFieldName,
594 : pszAttrFieldName, asS101AttrDefs))
595 8 : return false;
596 :
597 2791 : mapPathToCount.clear();
598 :
599 : // Update oMapFieldTypes with attributes found in this record
600 3541 : for (const auto &sAttrDef : asS101AttrDefs)
601 : {
602 751 : if (sAttrDef.bIsParent || sAttrDef.oReversedPath.empty())
603 160 : continue;
604 :
605 : // Check that the top-level part of the attribute is expected for
606 : // that feature type (using feature catalog)
607 591 : if (psFeatureType)
608 : {
609 : const auto oIterNATC =
610 0 : m_attributeCodes.find(sAttrDef.oReversedPath.back().first);
611 0 : if (oIterNATC != m_attributeCodes.end())
612 : {
613 0 : const std::string &osAttrCode = oIterNATC->second;
614 0 : if (!cpl::contains(psFeatureType->attributeBindings,
615 : osAttrCode))
616 : {
617 0 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
618 : "%s: attribute code %s not expected in feature "
619 : "type %s",
620 : GetErrorContext(), osAttrCode.c_str(),
621 : psFeatureType->code.c_str())))
622 : {
623 0 : return false;
624 : }
625 : }
626 : }
627 : }
628 :
629 : const auto key =
630 591 : std::make_pair(sAttrDef.oReversedPath, sAttrDef.iField);
631 591 : ++mapPathToCount[key];
632 :
633 : // Must be kept in that scope to create a OGR attribute even if
634 : // there is no field value
635 591 : auto &sOGRAttrDef = oMapFieldTypes[key];
636 :
637 591 : std::string typeFromCatalog;
638 591 : if (m_poFeatureCatalog)
639 : {
640 : auto oIterNATC =
641 591 : m_attributeCodes.find(sAttrDef.oReversedPath.front().first);
642 591 : if (oIterNATC != m_attributeCodes.end())
643 : {
644 : const auto &oMap =
645 583 : m_poFeatureCatalog->GetSimpleAttributes();
646 583 : const std::string &osAttrCode = oIterNATC->second;
647 583 : const auto oIterAttr = oMap.find(osAttrCode);
648 583 : if (oIterAttr != oMap.end())
649 : {
650 574 : const auto &attrDef = oIterAttr->second;
651 574 : typeFromCatalog = attrDef.type;
652 :
653 574 : sOGRAttrDef.osLongerName = attrDef.name;
654 574 : sOGRAttrDef.osDefinition = attrDef.definition;
655 :
656 574 : if (typeFromCatalog ==
657 : OGRS101FeatureCatalog::VALUE_TYPE_ENUMERATION)
658 : {
659 67 : sOGRAttrDef.osFieldDomainName = osAttrCode;
660 :
661 67 : if (!sAttrDef.osVal.empty())
662 : {
663 : // Checks that the coded value is an allowed code.
664 67 : const int nCode = atoi(sAttrDef.osVal.c_str());
665 67 : if (!cpl::contains(attrDef.enumeratedValues,
666 : nCode))
667 : {
668 2 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
669 : "%s: value %s does not belong to "
670 : "enumeration of attribute code %s",
671 : GetErrorContext(),
672 : sAttrDef.osVal.c_str(),
673 : osAttrCode.c_str())))
674 : {
675 1 : return false;
676 : }
677 : }
678 : }
679 :
680 : // Check if field domain exists. If not, create it.
681 66 : if (!cpl::contains(oMapFieldDomains, osAttrCode))
682 : {
683 90 : std::vector<OGRCodedValue> asValues;
684 1170 : for (const auto &[code, value] :
685 1215 : attrDef.enumeratedValues)
686 : {
687 : OGRCodedValue codedValue;
688 585 : codedValue.pszCode =
689 585 : CPLStrdup(CPLSPrintf("%d", code));
690 585 : codedValue.pszValue =
691 585 : CPLStrdup(value.c_str());
692 585 : asValues.push_back(std::move(codedValue));
693 : }
694 : auto poFieldDomain =
695 : std::make_unique<OGRCodedFieldDomain>(
696 0 : osAttrCode, attrDef.name, OFTString,
697 90 : OFSTNone, std::move(asValues));
698 45 : oMapFieldDomains[osAttrCode] =
699 90 : std::move(poFieldDomain);
700 : }
701 : }
702 : }
703 : }
704 :
705 952 : for (size_t i = 1; i < sAttrDef.oReversedPath.size(); ++i)
706 : {
707 : oIterNATC =
708 362 : m_attributeCodes.find(sAttrDef.oReversedPath[i].first);
709 362 : if (oIterNATC != m_attributeCodes.end())
710 : {
711 : const auto &oMap =
712 348 : m_poFeatureCatalog->GetComplexAttributes();
713 348 : const std::string &osAttrCode = oIterNATC->second;
714 348 : const auto oIterAttr = oMap.find(osAttrCode);
715 348 : if (oIterAttr != oMap.end())
716 : {
717 348 : const auto &attrDef = oIterAttr->second;
718 348 : sOGRAttrDef.osDefinition += ' ';
719 348 : sOGRAttrDef.osDefinition += oIterNATC->second;
720 348 : sOGRAttrDef.osDefinition += '=';
721 348 : sOGRAttrDef.osDefinition += attrDef.definition;
722 : }
723 : }
724 : }
725 : }
726 :
727 590 : sOGRAttrDef.bMultipleFields = sAttrDef.bMultipleFields;
728 :
729 590 : if (!sAttrDef.osVal.empty())
730 : {
731 581 : const bool bNewAttrIsMultiValued = mapPathToCount[key] > 1;
732 581 : if (bNewAttrIsMultiValued)
733 24 : sOGRAttrDef.bIsMultiValued = true;
734 581 : const auto eCPLType = CPLGetValueType(sAttrDef.osVal.c_str());
735 708 : auto eOGRType = eCPLType == CPL_VALUE_STRING ? OFTString
736 127 : : eCPLType == CPL_VALUE_INTEGER ? OFTInteger
737 : : OFTReal;
738 :
739 : // Is it YYYYMMDD date ?
740 85 : if (eOGRType == OFTInteger && sAttrDef.osVal.size() == 8 &&
741 666 : sAttrDef.osVal[4] <= '1' && sAttrDef.osVal[6] <= '3')
742 : {
743 : const auto it = m_attributeCodes.find(
744 3 : sAttrDef.oReversedPath.front().first);
745 3 : if (it != m_attributeCodes.end())
746 : {
747 3 : if (cpl::ends_with(it->second, "Date") ||
748 6 : cpl::ends_with(it->second, "dateStart") ||
749 3 : cpl::ends_with(it->second, "dateEnd"))
750 : {
751 3 : eOGRType = OFTDate;
752 : }
753 : }
754 : }
755 : // Is it YYYY---- truncated date ?
756 454 : else if (eOGRType == OFTString && sAttrDef.osVal.size() == 8 &&
757 3 : sAttrDef.osVal[4] == '-' && sAttrDef.osVal[5] == '-' &&
758 1032 : sAttrDef.osVal[6] == '-' && sAttrDef.osVal[7] == '-')
759 : {
760 : const auto it = m_attributeCodes.find(
761 3 : sAttrDef.oReversedPath.front().first);
762 3 : if (it != m_attributeCodes.end())
763 : {
764 3 : if (cpl::ends_with(it->second, "Date") ||
765 3 : cpl::ends_with(it->second, "dateStart") ||
766 0 : cpl::ends_with(it->second, "dateEnd"))
767 : {
768 3 : eOGRType = OFTDate;
769 : }
770 : }
771 : }
772 : // Is it time format ? ("094500", "094500", "094500+0100")
773 1026 : else if ((eOGRType == OFTInteger || eOGRType == OFTString) &&
774 932 : sAttrDef.osVal.size() >= 6 &&
775 399 : sAttrDef.osVal.size() <= 11 &&
776 231 : std::all_of(sAttrDef.osVal.begin(),
777 231 : sAttrDef.osVal.begin() + 6, [](char c)
778 1419 : { return c >= '0' && c <= '9'; }) &&
779 10 : (sAttrDef.osVal.size() == 6 ||
780 3 : (sAttrDef.osVal.size() == 7 &&
781 3 : sAttrDef.osVal[6] == 'Z') ||
782 3 : (sAttrDef.osVal.size() == 11 &&
783 3 : (sAttrDef.osVal[6] == '+' ||
784 0 : sAttrDef.osVal[6] == '-'))))
785 : {
786 : const auto it = m_attributeCodes.find(
787 7 : sAttrDef.oReversedPath.front().first);
788 7 : if (it != m_attributeCodes.end())
789 : {
790 7 : if (cpl::starts_with(it->second, "time"))
791 : {
792 6 : eOGRType = OFTTime;
793 : }
794 : }
795 : }
796 :
797 581 : if (!sOGRAttrDef.oeType.has_value())
798 : {
799 551 : sOGRAttrDef.oeType = eOGRType;
800 607 : if (eOGRType == OFTInteger &&
801 56 : typeFromCatalog ==
802 : OGRS101FeatureCatalog::VALUE_TYPE_BOOLEAN)
803 : {
804 0 : sOGRAttrDef.eSubType = OFSTBoolean;
805 : }
806 : }
807 36 : else if (eOGRType == OFTString &&
808 6 : *sOGRAttrDef.oeType != OFTString)
809 : {
810 0 : sOGRAttrDef.oeType = OFTString;
811 0 : sOGRAttrDef.eSubType = OFSTNone;
812 : }
813 31 : else if (eOGRType == OFTReal &&
814 1 : *sOGRAttrDef.oeType == OFTInteger)
815 : {
816 1 : sOGRAttrDef.oeType = OFTReal;
817 1 : sOGRAttrDef.eSubType = OFSTNone;
818 : }
819 : }
820 : }
821 : }
822 :
823 1973 : if (bFoundValidAssocField)
824 : {
825 561 : for (int i = 0; i < nMaxFieldRepeat; ++i)
826 : {
827 : const std::string osSuffix =
828 580 : nMaxFieldRepeat > 1 ? CPLSPrintf("[%d]", i + 1) : "";
829 :
830 290 : if (!bIsINAS)
831 : {
832 : OGRFieldDefn oFieldDefn(
833 48 : (OGR_FIELD_NAME_REF_FEAT_LAYER_NAME + osSuffix).c_str(),
834 96 : OFTString);
835 48 : oFeatureDefn.AddFieldDefn(&oFieldDefn);
836 : }
837 :
838 : {
839 : OGRFieldDefn oFieldDefn(
840 : ((bIsINAS ? OGR_FIELD_NAME_REF_INFO_RID
841 290 : : OGR_FIELD_NAME_REF_FEAT_RID) +
842 : osSuffix)
843 : .c_str(),
844 580 : OFTInteger);
845 290 : oFeatureDefn.AddFieldDefn(&oFieldDefn);
846 : }
847 : {
848 : OGRFieldDefn oFieldDefn(
849 290 : ((bIsINAS ? OGR_FIELD_NAME_NIAC : OGR_FIELD_NAME_NFAC) +
850 : osSuffix)
851 : .c_str(),
852 580 : OFTString);
853 290 : oFeatureDefn.AddFieldDefn(&oFieldDefn);
854 : }
855 : {
856 : OGRFieldDefn oFieldDefn(
857 : ((bIsINAS ? OGR_FIELD_NAME_NARC
858 290 : : OGR_FIELD_NAME_FEATURE_NARC) +
859 : osSuffix)
860 : .c_str(),
861 580 : OFTString);
862 290 : oFeatureDefn.AddFieldDefn(&oFieldDefn);
863 : }
864 : }
865 : }
866 :
867 : // Final pass to transform oMapFieldTypes into OGRField instances.
868 2524 : for (const auto &[oReversedPathAndFieldIndex, sOGRAttrDef] : oMapFieldTypes)
869 : {
870 551 : const auto &oReversedPath = oReversedPathAndFieldIndex.first;
871 551 : const int iField = oReversedPathAndFieldIndex.second;
872 :
873 : const std::string osAttrName =
874 : BuildFieldName(oReversedPath, pszAttrFieldName, iField,
875 1102 : sOGRAttrDef.bMultipleFields, pszIDFieldName);
876 :
877 551 : OGRFieldType eType = OFTString;
878 551 : if (sOGRAttrDef.oeType.has_value())
879 : {
880 544 : if (sOGRAttrDef.bIsMultiValued)
881 : {
882 25 : eType = (*sOGRAttrDef.oeType) == OFTInteger ? OFTIntegerList
883 1 : : (*sOGRAttrDef.oeType) == OFTReal ? OFTRealList
884 : : OFTStringList;
885 : }
886 : else
887 : {
888 520 : eType = *sOGRAttrDef.oeType;
889 : }
890 : }
891 7 : else if (sOGRAttrDef.bIsMultiValued)
892 : {
893 0 : eType = OFTStringList;
894 : }
895 :
896 551 : if (oFeatureDefn.GetFieldIndex(osAttrName.c_str()) >= 0)
897 : {
898 0 : if (osAttrName == OGR_FIELD_NAME_SMIN ||
899 0 : osAttrName == OGR_FIELD_NAME_SMAX)
900 : {
901 : // 101FR00368570.000 has scaleMinimum as an ATTR, and
902 : // doesn't define SPAS.SMIN
903 : }
904 : else
905 : {
906 0 : CPLError(CE_Warning, CPLE_AppDefined,
907 : "Layer %s: %s field already exists",
908 0 : oFeatureDefn.GetName(), osAttrName.c_str());
909 : }
910 : }
911 : else
912 : {
913 1102 : OGRFieldDefn oFieldDefn(osAttrName.c_str(), eType);
914 551 : oFieldDefn.SetSubType(sOGRAttrDef.eSubType);
915 551 : if (!sOGRAttrDef.osLongerName.empty() && oReversedPath.size() == 1)
916 367 : oFieldDefn.SetAlternativeName(sOGRAttrDef.osLongerName.c_str());
917 551 : if (!sOGRAttrDef.osDefinition.empty())
918 538 : oFieldDefn.SetComment(sOGRAttrDef.osDefinition.c_str());
919 551 : if (!sOGRAttrDef.osFieldDomainName.empty())
920 44 : oFieldDefn.SetDomainName(sOGRAttrDef.osFieldDomainName.c_str());
921 551 : oFeatureDefn.AddFieldDefn(&oFieldDefn);
922 : }
923 : }
924 :
925 1973 : return true;
926 : }
927 :
928 : /************************************************************************/
929 : /* FillFeatureAttributes() */
930 : /************************************************************************/
931 :
932 : /** Fill attribute fields of the provided feature.
933 : */
934 6503 : bool OGRS101Reader::FillFeatureAttributes(const DDFRecordIndex &oIndex,
935 : int iRecord,
936 : const char *pszAttrFieldName,
937 : OGRFeature &oFeature) const
938 : {
939 6503 : const auto poRecord = oIndex.GetByIndex(iRecord);
940 6503 : if (!poRecord)
941 : {
942 0 : return EMIT_ERROR("Invalid record number");
943 : }
944 :
945 6503 : const auto poIDField = poRecord->GetField(0);
946 6503 : CPLAssert(poIDField);
947 6503 : const char *pszIDFieldName = poIDField->GetFieldDefn()->GetName();
948 :
949 : const int nRCID =
950 6503 : poRecord->GetIntSubfield(pszIDFieldName, 0, RCID_SUBFIELD, 0);
951 6503 : if (nRCID < 1 && !EMIT_ERROR_OR_WARNING(CPLSPrintf(
952 : "Wrong value %d for RCID subfield of %s field.", nRCID,
953 : pszIDFieldName)))
954 : {
955 0 : return false;
956 : }
957 6503 : oFeature.SetField(OGR_FIELD_NAME_RECORD_ID, nRCID);
958 :
959 : const int nRVER =
960 6503 : poRecord->GetIntSubfield(pszIDFieldName, 0, RVER_SUBFIELD, 0);
961 6503 : oFeature.SetField(OGR_FIELD_NAME_RECORD_VERSION, nRVER);
962 :
963 : const int nRUIN =
964 6503 : poRecord->GetIntSubfield(pszIDFieldName, 0, RUIN_SUBFIELD, 0);
965 6503 : if (nRUIN != INSTRUCTION_INSERT)
966 : {
967 0 : return EMIT_ERROR_OR_WARNING(
968 : CPLSPrintf("Wrong value %d for RUIN subfield of %s field.", nRUIN,
969 : pszIDFieldName));
970 : }
971 :
972 6503 : const auto poFeatureDefn = oFeature.GetDefnRef();
973 :
974 : // First pass to detect which attribute entries correspond to parent nodes
975 : // that don't directly hold a field value.
976 13006 : std::vector<S101AttrDef> asS101AttrDefs;
977 6503 : if (!IngestAttributes(poRecord, iRecord, pszIDFieldName, pszAttrFieldName,
978 : asS101AttrDefs))
979 0 : return false;
980 :
981 : struct OGRFieldIndexTag
982 : {
983 : };
984 :
985 : using OGRFieldIndex = cpl::IntWrapper<OGRFieldIndexTag>;
986 :
987 13006 : std::map<OGRFieldIndex, CPLStringList> stringListAttrs;
988 13006 : std::map<OGRFieldIndex, std::vector<int>> intListAttrs;
989 13006 : std::map<OGRFieldIndex, std::vector<double>> doubleListAttrs;
990 : // Second pass to set single-valued attributes, or store multi-valued
991 : // attributes in the 3 above maps.
992 8905 : for (const auto &sAttrDef : asS101AttrDefs)
993 : {
994 2402 : if (sAttrDef.bIsParent || sAttrDef.oReversedPath.empty())
995 652 : continue;
996 :
997 : const std::string osAttrName = BuildFieldName(
998 1750 : sAttrDef.oReversedPath, pszAttrFieldName, sAttrDef.iField,
999 1750 : sAttrDef.bMultipleFields, pszIDFieldName);
1000 :
1001 : const int iOGRFieldIdx =
1002 1750 : poFeatureDefn->GetFieldIndex(osAttrName.c_str());
1003 : // Shouldn't normally happen given all preceding run logic
1004 1750 : CPLAssert(iOGRFieldIdx >= 0);
1005 1750 : const auto eType = poFeatureDefn->GetFieldDefn(iOGRFieldIdx)->GetType();
1006 1750 : const char *const pszATVL = sAttrDef.osVal.c_str();
1007 1750 : switch (eType)
1008 : {
1009 279 : case OFTInteger:
1010 : case OFTIntegerList:
1011 : {
1012 279 : if (pszATVL[0])
1013 : {
1014 279 : const char *const last = pszATVL + sAttrDef.osVal.size();
1015 279 : int nVal = -1;
1016 279 : auto [ptr, ec] = std::from_chars(pszATVL, last, nVal);
1017 279 : if (ec == std::errc() && ptr == last)
1018 : {
1019 278 : if (eType == OFTInteger)
1020 112 : oFeature.SetField(iOGRFieldIdx, nVal);
1021 : else
1022 166 : intListAttrs[iOGRFieldIdx].push_back(nVal);
1023 : }
1024 1 : else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
1025 : "Record index=%d of %s, attribute %s: "
1026 : "non integer value '%s'.",
1027 : iRecord, pszIDFieldName, osAttrName.c_str(),
1028 : pszATVL)))
1029 : {
1030 0 : return false;
1031 : }
1032 : }
1033 0 : else if (eType == OFTIntegerList)
1034 : {
1035 0 : intListAttrs[iOGRFieldIdx].push_back(
1036 0 : std::numeric_limits<int>::min());
1037 : }
1038 279 : break;
1039 : }
1040 :
1041 140 : case OFTReal:
1042 : case OFTRealList:
1043 : {
1044 140 : if (pszATVL[0])
1045 : {
1046 140 : const char *const last = pszATVL + sAttrDef.osVal.size();
1047 140 : double dfVal = -1;
1048 140 : const fast_float::parse_options options{
1049 : fast_float::chars_format::general, '.'};
1050 : auto [ptr, ec] = fast_float::from_chars_advanced(
1051 140 : pszATVL, last, dfVal, options);
1052 140 : if (ec == std::errc() && ptr == last)
1053 : {
1054 139 : if (eType == OFTReal)
1055 137 : oFeature.SetField(iOGRFieldIdx, dfVal);
1056 : else
1057 2 : doubleListAttrs[iOGRFieldIdx].push_back(dfVal);
1058 : }
1059 1 : else if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
1060 : "Record index=%d of %s, attribute %s: "
1061 : "non double value '%s'.",
1062 : iRecord, pszIDFieldName, osAttrName.c_str(),
1063 : pszATVL)))
1064 : {
1065 0 : return false;
1066 : }
1067 : }
1068 0 : else if (eType == OFTRealList)
1069 : {
1070 0 : doubleListAttrs[iOGRFieldIdx].push_back(
1071 0 : std::numeric_limits<double>::quiet_NaN());
1072 : }
1073 140 : break;
1074 : }
1075 :
1076 1227 : case OFTString:
1077 : case OFTStringList:
1078 : {
1079 0 : std::unique_ptr<char, VSIFreeReleaser> pszTmpStr;
1080 1227 : const char *pszStr = pszATVL;
1081 1227 : if (!CPLIsUTF8(pszATVL,
1082 1227 : static_cast<int>(sAttrDef.osVal.size())))
1083 : {
1084 : // Not supposed to happen in compliant products
1085 1 : if (!EMIT_ERROR_OR_WARNING(CPLSPrintf(
1086 : "Record index=%d of %s, attribute %s: non "
1087 : "UTF-8 string '%s'.",
1088 : iRecord, pszIDFieldName, osAttrName.c_str(),
1089 : pszATVL)))
1090 : {
1091 0 : return false;
1092 : }
1093 1 : pszTmpStr.reset(CPLUTF8ForceToASCII(pszATVL, '_'));
1094 1 : pszStr = pszTmpStr.get();
1095 : }
1096 1227 : if (eType == OFTString)
1097 1227 : oFeature.SetField(iOGRFieldIdx, pszStr);
1098 : else
1099 0 : stringListAttrs[iOGRFieldIdx].push_back(pszStr);
1100 1227 : break;
1101 : }
1102 :
1103 52 : case OFTDate:
1104 : {
1105 104 : if (sAttrDef.osVal.size() == 8 &&
1106 52 : std::all_of(sAttrDef.osVal.begin(), sAttrDef.osVal.end(),
1107 338 : [](char c) { return c >= '0' && c <= '9'; }))
1108 : {
1109 26 : const int nYear = (sAttrDef.osVal[0] - '0') * 1000 +
1110 26 : (sAttrDef.osVal[1] - '0') * 100 +
1111 26 : (sAttrDef.osVal[2] - '0') * 10 +
1112 26 : (sAttrDef.osVal[3] - '0');
1113 26 : const int nMonth = (sAttrDef.osVal[4] - '0') * 10 +
1114 26 : (sAttrDef.osVal[5] - '0');
1115 26 : const int nDay = (sAttrDef.osVal[6] - '0') * 10 +
1116 26 : (sAttrDef.osVal[7] - '0');
1117 26 : oFeature.SetField(iOGRFieldIdx, nYear, nMonth, nDay);
1118 : }
1119 26 : else if (sAttrDef.osVal.size() == 8 &&
1120 26 : sAttrDef.osVal[0] >= '0' && sAttrDef.osVal[0] <= '9' &&
1121 26 : sAttrDef.osVal[1] >= '0' && sAttrDef.osVal[1] <= '9' &&
1122 26 : sAttrDef.osVal[2] >= '0' && sAttrDef.osVal[2] <= '9' &&
1123 26 : sAttrDef.osVal[3] >= '0' && sAttrDef.osVal[3] <= '9' &&
1124 26 : sAttrDef.osVal[4] == '-' && sAttrDef.osVal[5] == '-' &&
1125 52 : sAttrDef.osVal[6] == '-' && sAttrDef.osVal[7] == '-')
1126 : {
1127 26 : const int nYear = (sAttrDef.osVal[0] - '0') * 1000 +
1128 26 : (sAttrDef.osVal[1] - '0') * 100 +
1129 26 : (sAttrDef.osVal[2] - '0') * 10 +
1130 26 : (sAttrDef.osVal[3] - '0');
1131 26 : if (cpl::ends_with(osAttrName, "dateEnd"))
1132 : {
1133 0 : oFeature.SetField(iOGRFieldIdx, nYear, 12, 31);
1134 : }
1135 : else
1136 : {
1137 26 : oFeature.SetField(iOGRFieldIdx, nYear, 1, 1);
1138 : }
1139 : }
1140 52 : break;
1141 : }
1142 :
1143 52 : case OFTTime:
1144 : {
1145 104 : if (sAttrDef.osVal.size() >= 6 &&
1146 52 : std::all_of(sAttrDef.osVal.begin(),
1147 104 : sAttrDef.osVal.begin() + 6,
1148 312 : [](char c) { return c >= '0' && c <= '9'; }))
1149 : {
1150 52 : const int nHour = (sAttrDef.osVal[0] - '0') * 10 +
1151 52 : (sAttrDef.osVal[1] - '0');
1152 52 : const int nMin = (sAttrDef.osVal[2] - '0') * 10 +
1153 52 : (sAttrDef.osVal[3] - '0');
1154 52 : const int nSec = (sAttrDef.osVal[4] - '0') * 10 +
1155 52 : (sAttrDef.osVal[5] - '0');
1156 52 : int nTZFlag = OGR_TZFLAG_UNKNOWN;
1157 52 : if (sAttrDef.osVal.size() == 7 && sAttrDef.osVal[6] == 'Z')
1158 0 : nTZFlag = OGR_TZFLAG_UTC;
1159 52 : else if (sAttrDef.osVal.size() == 11)
1160 : {
1161 26 : const int nTZHour = (sAttrDef.osVal[7] - '0') * 10 +
1162 26 : (sAttrDef.osVal[8] - '0');
1163 26 : const int nTZMin = (sAttrDef.osVal[9] - '0') * 10 +
1164 26 : (sAttrDef.osVal[10] - '0');
1165 26 : const int n15Minutes = (nTZHour * 60 + nTZMin) / 15;
1166 26 : if (sAttrDef.osVal[6] == '+')
1167 26 : nTZFlag = OGR_TZFLAG_UTC + n15Minutes;
1168 : else
1169 0 : nTZFlag = OGR_TZFLAG_UTC - n15Minutes;
1170 : }
1171 52 : oFeature.SetField(iOGRFieldIdx, 0, 0, 0, nHour, nMin,
1172 : static_cast<float>(nSec), nTZFlag);
1173 : }
1174 52 : break;
1175 : }
1176 :
1177 0 : default:
1178 0 : CPLAssert(false);
1179 : }
1180 : }
1181 :
1182 : // Set multi-valued fields.
1183 6503 : for (const auto &[iOGRFieldIdx, aosStrings] : stringListAttrs)
1184 0 : oFeature.SetField(static_cast<int>(iOGRFieldIdx), aosStrings.List());
1185 :
1186 6586 : for (const auto &[iOGRFieldIdx, anVals] : intListAttrs)
1187 166 : oFeature.SetField(static_cast<int>(iOGRFieldIdx),
1188 83 : static_cast<int>(anVals.size()), anVals.data());
1189 :
1190 6504 : for (const auto &[iOGRFieldIdx, adfVals] : doubleListAttrs)
1191 2 : oFeature.SetField(static_cast<int>(iOGRFieldIdx),
1192 1 : static_cast<int>(adfVals.size()), adfVals.data());
1193 :
1194 6503 : return true;
1195 : }
1196 :
1197 : /************************************************************************/
1198 : /* AttrNode */
1199 : /************************************************************************/
1200 :
1201 : namespace
1202 : {
1203 : using AttrCode = OGRS101Reader::AttrCode;
1204 : using AttrRepeat = OGRS101Reader::AttrRepeat;
1205 : using AttrIndex = OGRS101Reader::AttrIndex;
1206 :
1207 : struct AttrNode
1208 : {
1209 : AttrCode code = 0;
1210 : AttrRepeat indexOfSameCode = 0;
1211 : std::string value{};
1212 : std::vector<std::shared_ptr<AttrNode>> children{};
1213 :
1214 37 : std::string Encode() const
1215 : {
1216 37 : std::string s;
1217 37 : AttrIndex thisIdx = 0;
1218 37 : AttrIndex curIdx = 0;
1219 79 : for (const auto &child : children)
1220 : {
1221 42 : child->Encode(s, thisIdx, curIdx, "");
1222 : }
1223 74 : return s;
1224 : }
1225 :
1226 : private:
1227 : void Encode(std::string &s, AttrIndex parentIdx, AttrIndex &curIdx,
1228 : const std::string &indent);
1229 : };
1230 :
1231 107 : void AttrNode::Encode(std::string &s, AttrIndex parentIdx, AttrIndex &curIdx,
1232 : const std::string &indent)
1233 : {
1234 107 : ++curIdx;
1235 : if constexpr (false)
1236 : {
1237 : const int idx = static_cast<int>(curIdx);
1238 : CPLDebug("S101", "%s[%d].code = %d", indent.c_str(), idx,
1239 : static_cast<int>(code));
1240 : CPLDebug("S101", "%s[%d].indexOfSameCode = %d\n", indent.c_str(), idx,
1241 : static_cast<int>(indexOfSameCode));
1242 : CPLDebug("S101", "%s[%d].parentIdx = %d", indent.c_str(), idx,
1243 : static_cast<int>(parentIdx));
1244 : CPLDebug("S101", "%s[%d].value = %s", indent.c_str(), idx,
1245 : value.c_str());
1246 : }
1247 107 : OGRS101Reader::AppendUInt16(s,
1248 107 : static_cast<uint16_t>(static_cast<int>(code)));
1249 107 : OGRS101Reader::AppendUInt16(
1250 107 : s, static_cast<uint16_t>(static_cast<int>(indexOfSameCode)));
1251 107 : OGRS101Reader::AppendUInt16(
1252 107 : s, static_cast<uint16_t>(static_cast<int>(parentIdx)));
1253 107 : OGRS101Reader::AppendUInt8(s, static_cast<uint8_t>(INSTRUCTION_INSERT));
1254 107 : s.append(value);
1255 107 : OGRS101Reader::AppendUInt8(s, static_cast<uint8_t>(DDF_UNIT_TERMINATOR));
1256 :
1257 107 : if (!children.empty())
1258 : {
1259 30 : const auto thisIdx = curIdx;
1260 60 : const std::string newIndent = indent + " ";
1261 95 : for (const auto &child : children)
1262 : {
1263 65 : child->Encode(s, thisIdx, curIdx, newIndent);
1264 : }
1265 : }
1266 107 : }
1267 :
1268 : } // namespace
1269 :
1270 : /************************************************************************/
1271 : /* ProcessUpdateAttributeLikeField() */
1272 : /************************************************************************/
1273 :
1274 : /** Implement update of ATTR/INAS/FASC field described in 10a-5.1.2
1275 : * "Updating of the Attribute field"
1276 : */
1277 47 : bool OGRS101Reader::ProcessUpdateAttributeLikeField(
1278 : const DDFRecord *poUpdateRecord, const DDFField *poUpdateField,
1279 : DDFRecord *poTargetRecord, DDFField *poTargetField,
1280 : int iFieldInstance) const
1281 : {
1282 47 : const char *pszAttrFieldName = poUpdateField->GetFieldDefn()->GetName();
1283 47 : CPLAssert(EQUAL(pszAttrFieldName, ATTR_FIELD) ||
1284 : EQUAL(pszAttrFieldName, INAS_FIELD) ||
1285 : EQUAL(pszAttrFieldName, FASC_FIELD));
1286 :
1287 47 : const auto poIDField = poUpdateRecord->GetField(0);
1288 47 : CPLAssert(poIDField);
1289 47 : const char *pszIDFieldName = poIDField->GetFieldDefn()->GetName();
1290 47 : CPLAssert(pszIDFieldName);
1291 :
1292 : // Record name
1293 : const RecordName nRCNM =
1294 47 : poUpdateRecord->GetIntSubfield(pszIDFieldName, 0, RCNM_SUBFIELD, 0);
1295 :
1296 : // Record identifier
1297 : const int nRCID =
1298 47 : poUpdateRecord->GetIntSubfield(pszIDFieldName, 0, RCID_SUBFIELD, 0);
1299 :
1300 94 : AttrNode root;
1301 :
1302 47 : auto poActualTargetField = poTargetField->GetParts().size() == 2
1303 47 : ? poTargetField->GetParts()[1].get()
1304 47 : : poTargetField;
1305 47 : const int nTargetRepeatCount = poActualTargetField->GetRepeatCount();
1306 :
1307 47 : const auto poActualUpdateField = poUpdateField->GetParts().size() == 2
1308 47 : ? poUpdateField->GetParts()[1].get()
1309 47 : : poUpdateField;
1310 47 : const int nUpdateRepeatCount = poActualUpdateField->GetRepeatCount();
1311 :
1312 47 : constexpr int PASS_TARGET = 0;
1313 47 : constexpr int PASS_UPDATE = 1;
1314 131 : for (int iPass = PASS_TARGET; iPass <= PASS_UPDATE; ++iPass)
1315 : {
1316 94 : const auto poCurRecord =
1317 94 : (iPass == PASS_TARGET) ? poTargetRecord : poUpdateRecord;
1318 94 : const auto poCurField =
1319 94 : (iPass == PASS_TARGET) ? poTargetField : poUpdateField;
1320 94 : const int nRepeatCount =
1321 94 : (iPass == PASS_TARGET) ? nTargetRepeatCount : nUpdateRepeatCount;
1322 :
1323 : // mapAttributeIndexToNode and oSetNATC_ATIX_PAIX do need to be reset
1324 : // at each pass
1325 94 : std::map<AttrIndex, std::weak_ptr<AttrNode>> mapAttributeIndexToNode;
1326 : std::set<std::tuple<AttrCode, AttrRepeat, AttrIndex>>
1327 94 : oSetNATC_ATIX_PAIX;
1328 :
1329 413 : for (int i = 0; i < nRepeatCount; ++i)
1330 : {
1331 329 : const AttrIndex curIdx = i + 1;
1332 : const int nInstruction =
1333 329 : poCurRecord->GetIntSubfield(poCurField, ATIN_SUBFIELD, i);
1334 329 : if (nInstruction != INSTRUCTION_INSERT &&
1335 9 : nInstruction != INSTRUCTION_UPDATE &&
1336 : nInstruction != INSTRUCTION_DELETE)
1337 : {
1338 10 : return EMIT_ERROR_OR_WARNING(CPLSPrintf(
1339 : "%s, RCNM=%d, RCID=%d, %s field, instance %d, entry %d: "
1340 : "invalid ATIN=%d",
1341 : m_osFilename.c_str(), static_cast<int>(nRCNM), nRCID,
1342 : pszAttrFieldName, iFieldInstance, i, nInstruction));
1343 : }
1344 :
1345 327 : auto poNode = std::make_shared<AttrNode>();
1346 327 : poNode->code =
1347 : poCurRecord->GetIntSubfield(poCurField, NATC_SUBFIELD, i);
1348 327 : poNode->indexOfSameCode =
1349 : poCurRecord->GetIntSubfield(poCurField, ATIX_SUBFIELD, i);
1350 : const AttrIndex parentIndex =
1351 327 : poCurRecord->GetIntSubfield(poCurField, PAIX_SUBFIELD, i);
1352 :
1353 : // (NATC,ATIX,PAIX) tuple should be unique within a record
1354 327 : if (!oSetNATC_ATIX_PAIX
1355 : .insert(
1356 327 : {poNode->code, poNode->indexOfSameCode, parentIndex})
1357 327 : .second)
1358 : {
1359 2 : return EMIT_ERROR_OR_WARNING(
1360 : CPLSPrintf("%s, RCNM=%d, RCID=%d, %s field, instance %d: "
1361 : "entry %d refers to (NATC,ATIX,PAIX)=(%d,%d,%d) "
1362 : "already encountered.",
1363 : m_osFilename.c_str(), static_cast<int>(nRCNM),
1364 : nRCID, pszAttrFieldName, iFieldInstance, i,
1365 : static_cast<int>(poNode->code),
1366 : static_cast<int>(poNode->indexOfSameCode),
1367 : static_cast<int>(parentIndex)));
1368 : }
1369 :
1370 : const char *pszATVL =
1371 325 : poCurRecord->GetStringSubfield(poCurField, ATVL_SUBFIELD, i);
1372 325 : if (pszATVL)
1373 325 : poNode->value = pszATVL;
1374 :
1375 : // Find the parent node (which is the root node if no parent)
1376 0 : std::shared_ptr<AttrNode> poParentNodeSharedPtr;
1377 325 : AttrNode *poParentNode = &root;
1378 325 : if (parentIndex > 0)
1379 : {
1380 214 : auto oIter = mapAttributeIndexToNode.find(parentIndex);
1381 214 : if (oIter == mapAttributeIndexToNode.end())
1382 : {
1383 2 : return EMIT_ERROR_OR_WARNING(CPLSPrintf(
1384 : "%s, RCNM=%d, RCID=%d, %s field, instance %d: entry %d "
1385 : "refers to a PAIX=%d that does not exist",
1386 : m_osFilename.c_str(), static_cast<int>(nRCNM), nRCID,
1387 : pszAttrFieldName, iFieldInstance, i,
1388 : static_cast<int>(parentIndex)));
1389 : }
1390 212 : poParentNodeSharedPtr = oIter->second.lock();
1391 212 : if (!poParentNodeSharedPtr)
1392 : {
1393 : // I don't think that can happen given the
1394 : // (NATC,ATIX,PAIX) unicity check
1395 0 : return EMIT_ERROR_OR_WARNING(CPLSPrintf(
1396 : "%s, RCNM=%d, RCID=%d, %s field, instance %d: entry %d "
1397 : "refers to a PAIX=%d that has been deleted",
1398 : m_osFilename.c_str(), static_cast<int>(nRCNM), nRCID,
1399 : pszAttrFieldName, iFieldInstance, i,
1400 : static_cast<int>(parentIndex)));
1401 : }
1402 212 : poParentNode = poParentNodeSharedPtr.get();
1403 : }
1404 :
1405 323 : if (nInstruction == INSTRUCTION_INSERT)
1406 : {
1407 : // Find a sibling of same code and whose index is just one before
1408 : // the one to insert.
1409 248 : auto iterInsertion = poParentNode->children.begin();
1410 400 : for (; iterInsertion != poParentNode->children.end();
1411 152 : ++iterInsertion)
1412 : {
1413 201 : if ((*iterInsertion)->code == poNode->code &&
1414 42 : (*iterInsertion)->indexOfSameCode >=
1415 42 : poNode->indexOfSameCode)
1416 : {
1417 7 : break;
1418 : }
1419 : }
1420 :
1421 248 : if (iterInsertion != poParentNode->children.end())
1422 : {
1423 7 : if (poCurRecord == poTargetRecord)
1424 : {
1425 0 : const auto iterNext = std::next(iterInsertion);
1426 0 : if (iterNext != poParentNode->children.end() &&
1427 0 : (*iterNext)->code == poNode->code &&
1428 0 : (*iterNext)->indexOfSameCode ==
1429 0 : poNode->indexOfSameCode)
1430 : {
1431 0 : return EMIT_ERROR_OR_WARNING(CPLSPrintf(
1432 : "%s, RCNM=%d, RCID=%d, %s field, instance %d: "
1433 : "entry %d collides with another entry of same "
1434 : "(NATC, ATIX)=(%d,%d)",
1435 : m_osFilename.c_str(), static_cast<int>(nRCNM),
1436 : nRCID, pszAttrFieldName, iFieldInstance, i,
1437 : static_cast<int>(poNode->code),
1438 : static_cast<int>(poNode->indexOfSameCode)));
1439 : }
1440 : }
1441 : else
1442 : {
1443 : // Renumber indexOfSameCode of children right to the inserted one
1444 28 : for (auto iterChild = iterInsertion;
1445 28 : iterChild != poParentNode->children.end();
1446 21 : ++iterChild)
1447 : {
1448 28 : if ((*iterChild)->code == poNode->code &&
1449 7 : (*iterChild)->indexOfSameCode >=
1450 7 : poNode->indexOfSameCode)
1451 : {
1452 7 : ++((*iterChild)->indexOfSameCode);
1453 : }
1454 : }
1455 : }
1456 : }
1457 :
1458 248 : mapAttributeIndexToNode[curIdx] = poNode;
1459 :
1460 248 : poParentNode->children.insert(iterInsertion, poNode);
1461 : }
1462 : else
1463 : {
1464 : // Identify child with desired (code, indexOfSameCode)
1465 75 : auto iterChild = poParentNode->children.begin();
1466 94 : for (; iterChild != poParentNode->children.end(); ++iterChild)
1467 : {
1468 161 : if ((*iterChild)->code == poNode->code &&
1469 71 : (*iterChild)->indexOfSameCode ==
1470 71 : poNode->indexOfSameCode)
1471 : {
1472 71 : break;
1473 : }
1474 : }
1475 75 : if (iterChild == poParentNode->children.end())
1476 : {
1477 4 : return EMIT_ERROR_OR_WARNING(CPLSPrintf(
1478 : "%s, RCNM=%d, RCID=%d, %s field, instance %d: entry %d "
1479 : "references unexisting entry (NATC, ATIX)=(%d,%d)",
1480 : m_osFilename.c_str(), static_cast<int>(nRCNM), nRCID,
1481 : pszAttrFieldName, iFieldInstance, i,
1482 : static_cast<int>(poNode->code),
1483 : static_cast<int>(poNode->indexOfSameCode)));
1484 : }
1485 :
1486 71 : if (nInstruction == INSTRUCTION_DELETE)
1487 : {
1488 7 : poParentNode->children.erase(iterChild);
1489 : // No need to explicitly modify mapAttributeIndexToNode
1490 : // As it contains weak pointers, if the removed node or
1491 : // one of its children was in the map, the weak pointer
1492 : // will be invalidated.
1493 : }
1494 : else
1495 : {
1496 64 : const auto &poModifiedNode = *iterChild;
1497 64 : mapAttributeIndexToNode[curIdx] = poModifiedNode;
1498 64 : poModifiedNode->value = poNode->value;
1499 : }
1500 : }
1501 : }
1502 : }
1503 :
1504 74 : std::string s;
1505 37 : if (EQUAL(pszAttrFieldName, INAS_FIELD) ||
1506 19 : EQUAL(pszAttrFieldName, FASC_FIELD))
1507 : {
1508 25 : constexpr int SIZE_OF_NON_REPEATED_FIELDS = 1 + 4 + 2 + 2 + 1;
1509 25 : if (poUpdateField->GetDataSize() < SIZE_OF_NON_REPEATED_FIELDS)
1510 : {
1511 : // Should probably not occur given earlier checks, but...
1512 0 : return EMIT_ERROR_OR_WARNING(
1513 : CPLSPrintf("%s, RCNM=%d, RCID=%d, %s field, instance %d: "
1514 : "invalid update field",
1515 : m_osFilename.c_str(), static_cast<int>(nRCNM), nRCID,
1516 : pszAttrFieldName, iFieldInstance));
1517 : }
1518 25 : s.append(poUpdateField->GetData(), SIZE_OF_NON_REPEATED_FIELDS - 1);
1519 25 : AppendUInt8(s, INSTRUCTION_INSERT);
1520 : }
1521 37 : s += root.Encode();
1522 37 : AppendUInt8(s, DDF_FIELD_TERMINATOR);
1523 37 : poTargetRecord->SetFieldRaw(poTargetField, s.data(),
1524 37 : static_cast<int>(s.size()));
1525 :
1526 37 : return true;
1527 : }
1528 :
1529 : /************************************************************************/
1530 : /* ProcessUpdateATTR() */
1531 : /************************************************************************/
1532 :
1533 : /** Update all instances of ATTR field
1534 : */
1535 33 : bool OGRS101Reader::ProcessUpdateATTR(const DDFRecord *poUpdateRecord,
1536 : DDFRecord *poTargetRecord) const
1537 : {
1538 33 : const auto poIDField = poUpdateRecord->GetField(0);
1539 33 : CPLAssert(poIDField);
1540 33 : const char *pszIDFieldName = poIDField->GetFieldDefn()->GetName();
1541 33 : CPLAssert(pszIDFieldName);
1542 :
1543 : // Record name
1544 : const RecordName nRCNM =
1545 33 : poUpdateRecord->GetIntSubfield(pszIDFieldName, 0, RCNM_SUBFIELD, 0);
1546 :
1547 : // Record identifier
1548 : const int nRCID =
1549 33 : poUpdateRecord->GetIntSubfield(pszIDFieldName, 0, RCID_SUBFIELD, 0);
1550 :
1551 66 : auto apoUpdateFields = poUpdateRecord->GetFields(ATTR_FIELD);
1552 33 : if (apoUpdateFields.empty())
1553 12 : return true;
1554 42 : auto apoTargetFields = poTargetRecord->GetFields(ATTR_FIELD);
1555 21 : if (apoTargetFields.size() != apoUpdateFields.size())
1556 : {
1557 0 : return EMIT_ERROR_OR_WARNING(CPLSPrintf(
1558 : "%s, RCNM=%d, RCID=%d, %s field: target record has %d field "
1559 : "instances, whereas update record has %d",
1560 : m_osFilename.c_str(), static_cast<int>(nRCNM), nRCID, ATTR_FIELD,
1561 : static_cast<int>(apoTargetFields.size()),
1562 : static_cast<int>(apoUpdateFields.size())));
1563 : }
1564 :
1565 38 : for (size_t i = 0; i < apoUpdateFields.size(); ++i)
1566 : {
1567 21 : if (!ProcessUpdateAttributeLikeField(poUpdateRecord, apoUpdateFields[i],
1568 21 : poTargetRecord, apoTargetFields[i],
1569 : static_cast<int>(i)))
1570 : {
1571 4 : return false;
1572 : }
1573 : }
1574 :
1575 17 : return true;
1576 : }
1577 :
1578 : /************************************************************************/
1579 : /* ProcessUpdateINASOrFASC() */
1580 : /************************************************************************/
1581 :
1582 : /** Update all instances of INAS or FASC field
1583 : */
1584 156 : bool OGRS101Reader::ProcessUpdateINASOrFASC(const DDFRecord *poUpdateRecord,
1585 : DDFRecord *poTargetRecord,
1586 : const char *pszFieldName) const
1587 : {
1588 156 : CPLAssert(EQUAL(pszFieldName, INAS_FIELD) ||
1589 : EQUAL(pszFieldName, FASC_FIELD));
1590 :
1591 156 : const auto poIDField = poUpdateRecord->GetField(0);
1592 156 : CPLAssert(poIDField);
1593 :
1594 : // Record name
1595 : const RecordName nRCNM =
1596 156 : poUpdateRecord->GetIntSubfield(poIDField, RCNM_SUBFIELD, 0);
1597 :
1598 : // Record identifier
1599 : const int nRCID =
1600 156 : poUpdateRecord->GetIntSubfield(poIDField, RCID_SUBFIELD, 0);
1601 :
1602 312 : auto apoUpdateFields = poUpdateRecord->GetFields(pszFieldName);
1603 156 : if (apoUpdateFields.empty())
1604 126 : return true;
1605 :
1606 60 : auto apoTargetFields = poTargetRecord->GetFields(pszFieldName);
1607 :
1608 80 : for (int iUpdate = 0; iUpdate < static_cast<int>(apoUpdateFields.size());
1609 : ++iUpdate)
1610 : {
1611 54 : const auto poUpdateField = apoUpdateFields[iUpdate];
1612 54 : const int nInstruction = poUpdateRecord->GetIntSubfield(
1613 : poUpdateField,
1614 54 : EQUAL(pszFieldName, INAS_FIELD) ? IUIN_SUBFIELD : FAUI_SUBFIELD, 0);
1615 54 : if (nInstruction == INSTRUCTION_INSERT)
1616 : {
1617 : const auto poINASFieldDefn =
1618 12 : m_oMainModule.FindFieldDefn(pszFieldName);
1619 12 : if (!poINASFieldDefn)
1620 : {
1621 0 : return EMIT_ERROR(CPLSPrintf("Cannot find %s field definition",
1622 : pszFieldName));
1623 : }
1624 12 : auto poFieldTarget = poTargetRecord->AddField(poINASFieldDefn);
1625 12 : CPLAssert(poFieldTarget);
1626 :
1627 12 : poTargetRecord->SetFieldRaw(poFieldTarget, poUpdateField->GetData(),
1628 : poUpdateField->GetDataSize());
1629 12 : apoTargetFields.push_back(poFieldTarget);
1630 : }
1631 42 : else if (nInstruction == INSTRUCTION_UPDATE ||
1632 : nInstruction == INSTRUCTION_DELETE)
1633 : {
1634 : const RecordName RRNM =
1635 40 : poUpdateRecord->GetIntSubfield(poUpdateField, RRNM_SUBFIELD, 0);
1636 : const int RRID =
1637 40 : poUpdateRecord->GetIntSubfield(poUpdateField, RRID_SUBFIELD, 0);
1638 :
1639 40 : bool bMatchFound = false;
1640 56 : for (size_t iTarget = 0; iTarget < apoTargetFields.size();
1641 : ++iTarget)
1642 : {
1643 54 : auto poTargetField = apoTargetFields[iTarget];
1644 : const RecordName RRNMTarget = poTargetRecord->GetIntSubfield(
1645 54 : poTargetField, RRNM_SUBFIELD, 0);
1646 54 : const int RRIDTarget = poTargetRecord->GetIntSubfield(
1647 : poTargetField, RRID_SUBFIELD, 0);
1648 54 : if (RRNM == RRNMTarget && RRID == RRIDTarget)
1649 : {
1650 38 : bMatchFound = true;
1651 38 : if (nInstruction == INSTRUCTION_DELETE)
1652 : {
1653 12 : poTargetRecord->DeleteField(poTargetField);
1654 0 : apoTargetFields.erase(apoTargetFields.begin() +
1655 12 : iTarget);
1656 : }
1657 : else
1658 : {
1659 26 : if (!ProcessUpdateAttributeLikeField(
1660 : poUpdateRecord, poUpdateField, poTargetRecord,
1661 : poTargetField, iUpdate))
1662 : {
1663 0 : return false;
1664 : }
1665 : }
1666 38 : break;
1667 : }
1668 : }
1669 40 : if (!bMatchFound)
1670 : {
1671 2 : return EMIT_ERROR_OR_WARNING(CPLSPrintf(
1672 : "%s, RCNM=%d, RCID=%d, %s field, %d instance: found no "
1673 : "matching (RRNM,RRID)=(%d,%d) to %s",
1674 : m_osFilename.c_str(), static_cast<int>(nRCNM), nRCID,
1675 : pszFieldName, iUpdate, static_cast<int>(RRNM), RRID,
1676 : nInstruction == INSTRUCTION_UPDATE ? "update" : "delete"));
1677 38 : }
1678 : }
1679 : else
1680 : {
1681 2 : return EMIT_ERROR_OR_WARNING(
1682 : CPLSPrintf("%s, RCNM=%d, RCID=%d, %s field, %d instance: "
1683 : "invalid instruction = %d",
1684 : m_osFilename.c_str(), static_cast<int>(nRCNM), nRCID,
1685 : pszFieldName, iUpdate, nInstruction));
1686 : }
1687 : }
1688 :
1689 26 : return true;
1690 : }
|