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