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