Line data Source code
1 : /******************************************************************************
2 : * Project: OGR
3 : * Purpose: OGRGMLASDriver implementation
4 : * Author: Even Rouault, <even dot rouault at spatialys dot com>
5 : *
6 : * Initial development funded by the European Earth observation programme
7 : * Copernicus
8 : *
9 : ******************************************************************************
10 : * Copyright (c) 2016, Even Rouault, <even dot rouault at spatialys dot com>
11 : *
12 : * SPDX-License-Identifier: MIT
13 : ****************************************************************************/
14 :
15 : #include "xercesc_headers.h"
16 :
17 : // Hack to avoid bool, possibly redefined to pedantic bool class, being later
18 : // used
19 180 : static XSModel *getGrammarPool(XMLGrammarPool *pool)
20 : {
21 : bool changed;
22 360 : return pool->getXSModel(changed);
23 : }
24 :
25 : #include "ogr_gmlas.h"
26 : #include "ogr_pgdump.h"
27 :
28 : #include <algorithm>
29 : #include <list>
30 :
31 : static OGRwkbGeometryType GetOGRGeometryType(XSTypeDefinition *poTypeDef);
32 :
33 : /************************************************************************/
34 : /* IsCompatibleOfArray() */
35 : /************************************************************************/
36 :
37 5322 : static bool IsCompatibleOfArray(GMLASFieldType eType)
38 : {
39 1007 : return eType == GMLAS_FT_STRING || eType == GMLAS_FT_BOOLEAN ||
40 915 : eType == GMLAS_FT_SHORT || eType == GMLAS_FT_INT32 ||
41 623 : eType == GMLAS_FT_INT64 || eType == GMLAS_FT_FLOAT ||
42 6329 : eType == GMLAS_FT_DOUBLE || eType == GMLAS_FT_DECIMAL ||
43 5322 : eType == GMLAS_FT_ANYURI;
44 : }
45 :
46 : /************************************************************************/
47 : /* GMLASPrefixMappingHander */
48 : /************************************************************************/
49 :
50 : class GMLASPrefixMappingHander : public DefaultHandler
51 : {
52 : std::map<CPLString, CPLString> &m_oMapURIToPrefix;
53 : const std::map<CPLString, CPLString> &m_oMapDocNSURIToPrefix;
54 : CPLString &m_osGMLVersionFound;
55 :
56 : public:
57 1294 : GMLASPrefixMappingHander(
58 : std::map<CPLString, CPLString> &oMapURIToPrefix,
59 : const std::map<CPLString, CPLString> &oMapDocNSURIToPrefix,
60 : CPLString &osGMLVersionFound)
61 1294 : : m_oMapURIToPrefix(oMapURIToPrefix),
62 : m_oMapDocNSURIToPrefix(oMapDocNSURIToPrefix),
63 1294 : m_osGMLVersionFound(osGMLVersionFound)
64 : {
65 1294 : }
66 :
67 : virtual void startElement(const XMLCh *const uri,
68 : const XMLCh *const localname,
69 : const XMLCh *const qname,
70 : const Attributes &attrs) override;
71 :
72 : virtual void startPrefixMapping(const XMLCh *const prefix,
73 : const XMLCh *const uri) override;
74 : };
75 :
76 : /************************************************************************/
77 : /* startElement() */
78 : /************************************************************************/
79 :
80 889983 : void GMLASPrefixMappingHander::startElement(const XMLCh *const uri,
81 : const XMLCh *const localname,
82 : const XMLCh *const /*qname*/,
83 : const Attributes &attrs)
84 : {
85 889983 : if (!m_osGMLVersionFound.empty())
86 64381 : return;
87 :
88 1651200 : const CPLString osURI(transcode(uri));
89 1651200 : const CPLString osLocalname(transcode(localname));
90 825602 : if (osURI == szXS_URI && osLocalname == "schema")
91 : {
92 521 : bool bIsGML = false;
93 1042 : std::string osVersion;
94 2288 : for (unsigned int i = 0; i < attrs.getLength(); i++)
95 : {
96 3534 : const std::string osAttrLocalName(transcode(attrs.getLocalName(i)));
97 1767 : if (osAttrLocalName == "targetNamespace")
98 : {
99 488 : bIsGML = transcode(attrs.getValue(i)) == szGML_URI;
100 : }
101 1279 : else if (osAttrLocalName == "version")
102 : {
103 286 : osVersion = transcode(attrs.getValue(i));
104 : }
105 : }
106 521 : if (bIsGML && !osVersion.empty())
107 : {
108 0 : m_osGMLVersionFound = std::move(osVersion);
109 : }
110 : }
111 : }
112 :
113 : /************************************************************************/
114 : /* startPrefixMapping() */
115 : /************************************************************************/
116 :
117 3730 : void GMLASPrefixMappingHander::startPrefixMapping(const XMLCh *const prefix,
118 : const XMLCh *const uri)
119 : {
120 7460 : const CPLString osURI(transcode(uri));
121 7460 : CPLString osPrefix(transcode(prefix));
122 3730 : if (osPrefix.empty())
123 : {
124 542 : const auto oIter = m_oMapDocNSURIToPrefix.find(osURI);
125 542 : if (oIter != m_oMapDocNSURIToPrefix.end())
126 : {
127 38 : osPrefix = oIter->second;
128 : }
129 : }
130 3730 : if (!osPrefix.empty())
131 : {
132 3226 : const auto oIter = m_oMapURIToPrefix.find(osURI);
133 3226 : if (oIter == m_oMapURIToPrefix.end())
134 : {
135 595 : m_oMapURIToPrefix[osURI] = osPrefix;
136 595 : CPLDebug("GMLAS", "Registering prefix=%s for uri=%s",
137 : osPrefix.c_str(), osURI.c_str());
138 : }
139 2631 : else if (oIter->second != osPrefix)
140 : {
141 326 : CPLDebug("GMLAS",
142 : "Existing prefix=%s for uri=%s (new prefix %s not used)",
143 163 : oIter->second.c_str(), osURI.c_str(), osPrefix.c_str());
144 : }
145 : }
146 3730 : }
147 :
148 : /************************************************************************/
149 : /* CollectNamespacePrefixes() */
150 : /************************************************************************/
151 :
152 1294 : static void CollectNamespacePrefixes(
153 : const char *pszXSDFilename, const std::shared_ptr<VSIVirtualHandle> &fpXSD,
154 : std::map<CPLString, CPLString> &oMapURIToPrefix,
155 : const std::map<CPLString, CPLString> &oMapDocNSURIToPrefix,
156 : CPLString &osGMLVersionFound)
157 : {
158 2588 : GMLASInputSource oSource(pszXSDFilename, fpXSD);
159 : // This is a bit silly but the startPrefixMapping() callback only gets
160 : // called when using SAX2XMLReader::parse(), and not when using
161 : // loadGrammar(), so we have to parse the doc twice.
162 1294 : SAX2XMLReader *poReader = XMLReaderFactory::createXMLReader();
163 :
164 : GMLASPrefixMappingHander contentHandler(
165 2588 : oMapURIToPrefix, oMapDocNSURIToPrefix, osGMLVersionFound);
166 1294 : poReader->setContentHandler(&contentHandler);
167 :
168 2588 : GMLASErrorHandler oErrorHandler;
169 1294 : poReader->setErrorHandler(&oErrorHandler);
170 :
171 1294 : poReader->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution, true);
172 :
173 2588 : std::string osErrorMsg;
174 : try
175 : {
176 1294 : poReader->parse(oSource);
177 : }
178 0 : catch (const SAXException &e)
179 : {
180 0 : osErrorMsg += transcode(e.getMessage());
181 : }
182 0 : catch (const XMLException &e)
183 : {
184 0 : osErrorMsg += transcode(e.getMessage());
185 : }
186 0 : catch (const OutOfMemoryException &e)
187 : {
188 0 : if (strstr(CPLGetLastErrorMsg(), "configuration option") == nullptr)
189 : {
190 0 : osErrorMsg += transcode(e.getMessage());
191 : }
192 : }
193 0 : catch (const DOMException &e)
194 : {
195 0 : osErrorMsg += transcode(e.getMessage());
196 : }
197 1294 : if (!osErrorMsg.empty())
198 : {
199 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrorMsg.c_str());
200 : }
201 1294 : delete poReader;
202 1294 : }
203 :
204 : /************************************************************************/
205 : /* GMLASAnalyzerEntityResolver */
206 : /************************************************************************/
207 :
208 : class GMLASAnalyzerEntityResolver final : public GMLASBaseEntityResolver
209 : {
210 : std::map<CPLString, CPLString> &m_oMapURIToPrefix;
211 : const std::map<CPLString, CPLString> &m_oMapDocNSURIToPrefix;
212 :
213 : public:
214 190 : GMLASAnalyzerEntityResolver(
215 : const CPLString &osBasePath,
216 : std::map<CPLString, CPLString> &oMapURIToPrefix,
217 : const std::map<CPLString, CPLString> &oMapDocNSURIToPrefix,
218 : GMLASXSDCache &oCache)
219 190 : : GMLASBaseEntityResolver(osBasePath, oCache),
220 : m_oMapURIToPrefix(oMapURIToPrefix),
221 190 : m_oMapDocNSURIToPrefix(oMapDocNSURIToPrefix)
222 : {
223 190 : }
224 :
225 : void DoExtraSchemaProcessing(
226 : const CPLString &osFilename,
227 : const std::shared_ptr<VSIVirtualHandle> &fp) override;
228 : };
229 :
230 : /************************************************************************/
231 : /* DoExtraSchemaProcessing() */
232 : /************************************************************************/
233 :
234 1294 : void GMLASAnalyzerEntityResolver::DoExtraSchemaProcessing(
235 : const CPLString &osFilename, const std::shared_ptr<VSIVirtualHandle> &fp)
236 : {
237 1294 : CollectNamespacePrefixes(osFilename, fp, m_oMapURIToPrefix,
238 1294 : m_oMapDocNSURIToPrefix, m_osGMLVersionFound);
239 1294 : fp->Seek(0, SEEK_SET);
240 1294 : }
241 :
242 : /************************************************************************/
243 : /* GMLASSchemaAnalyzer() */
244 : /************************************************************************/
245 :
246 194 : GMLASSchemaAnalyzer::GMLASSchemaAnalyzer(
247 : GMLASXPathMatcher &oIgnoredXPathMatcher,
248 : GMLASXPathMatcher &oChildrenElementsConstraintsXPathMatcher,
249 : const std::map<CPLString, std::vector<CPLString>>
250 : &oMapChildrenElementsConstraints,
251 : GMLASXPathMatcher &oForcedFlattenedXPathMatcher,
252 194 : GMLASXPathMatcher &oDisabledFlattenedXPathMatcher)
253 : : m_oIgnoredXPathMatcher(oIgnoredXPathMatcher),
254 : m_oChildrenElementsConstraintsXPathMatcher(
255 : oChildrenElementsConstraintsXPathMatcher),
256 : m_oForcedFlattenedXPathMatcher(oForcedFlattenedXPathMatcher),
257 : m_oDisabledFlattenedXPathMatcher(oDisabledFlattenedXPathMatcher),
258 : m_oMapChildrenElementsConstraints(oMapChildrenElementsConstraints),
259 : m_bUseArrays(true), m_bUseNullState(false),
260 : m_bInstantiateGMLFeaturesOnly(true), m_nIdentifierMaxLength(0),
261 : m_bCaseInsensitiveIdentifier(CASE_INSENSITIVE_IDENTIFIER_DEFAULT),
262 : m_bPGIdentifierLaundering(PG_IDENTIFIER_LAUNDERING_DEFAULT),
263 : m_nMaximumFieldsForFlattening(MAXIMUM_FIELDS_FLATTENING_DEFAULT),
264 194 : m_bAlwaysGenerateOGRId(ALWAYS_GENERATE_OGR_ID_DEFAULT)
265 : {
266 : // A few hardcoded namespace uri->prefix mappings
267 194 : m_oMapURIToPrefix[szXMLNS_URI] = szXMLNS_PREFIX;
268 194 : m_oMapURIToPrefix[szXSI_URI] = szXSI_PREFIX;
269 194 : }
270 :
271 : /************************************************************************/
272 : /* GetPrefix() */
273 : /************************************************************************/
274 :
275 456560 : CPLString GMLASSchemaAnalyzer::GetPrefix(const CPLString &osNamespaceURI)
276 : {
277 456560 : if (osNamespaceURI.empty())
278 32278 : return "";
279 424282 : const auto oIter = m_oMapURIToPrefix.find(osNamespaceURI);
280 424282 : if (oIter != m_oMapURIToPrefix.end())
281 424279 : return oIter->second;
282 3 : else if (!osNamespaceURI.empty())
283 : {
284 : // If the schema doesn't define a xmlns:MYPREFIX=myuri, then forge a
285 : // fake prefix for conveniency
286 6 : CPLString osPrefix;
287 3 : if (osNamespaceURI.find(szOPENGIS_URL) == 0)
288 1 : osPrefix = osNamespaceURI.substr(strlen(szOPENGIS_URL));
289 2 : else if (osNamespaceURI.find("http://") == 0)
290 1 : osPrefix = osNamespaceURI.substr(strlen("http://"));
291 : else
292 1 : osPrefix = osNamespaceURI;
293 28 : for (size_t i = 0; i < osPrefix.size(); i++)
294 : {
295 25 : if (!isalnum(static_cast<unsigned char>(osPrefix[i])))
296 4 : osPrefix[i] = '_';
297 : }
298 3 : m_oMapURIToPrefix[osNamespaceURI] = osPrefix;
299 3 : CPLDebug("GMLAS", "Cannot find prefix for ns='%s'. Forging %s",
300 : osNamespaceURI.c_str(), osPrefix.c_str());
301 3 : return osPrefix;
302 : }
303 : else
304 : {
305 0 : CPLDebug("GMLAS", "Cannot find prefix for ns='%s'.",
306 : osNamespaceURI.c_str());
307 0 : return "";
308 : }
309 : }
310 :
311 : /************************************************************************/
312 : /* MakeXPath() */
313 : /************************************************************************/
314 :
315 439361 : CPLString GMLASSchemaAnalyzer::MakeXPath(const CPLString &osNamespaceURI,
316 : const CPLString &osName)
317 : {
318 878722 : const CPLString osPrefix(GetPrefix(osNamespaceURI));
319 439361 : if (osPrefix.empty())
320 32274 : return osName;
321 814174 : return osPrefix + ":" + osName;
322 : }
323 :
324 : /************************************************************************/
325 : /* GetNSOfLastXPathComponent() */
326 : /************************************************************************/
327 :
328 : // Return the namespace (if any) of the last component of the XPath
329 186 : static CPLString GetNSOfLastXPathComponent(const CPLString &osXPath)
330 : {
331 186 : size_t nPos = osXPath.rfind('@');
332 186 : if (nPos != std::string::npos)
333 93 : nPos++;
334 : else
335 : {
336 93 : nPos = osXPath.rfind('/');
337 93 : if (nPos != std::string::npos)
338 0 : nPos++;
339 : else
340 93 : nPos = 0;
341 : }
342 186 : size_t nPosColumn = osXPath.find(':', nPos);
343 186 : if (nPosColumn == std::string::npos)
344 47 : return CPLString();
345 278 : return CPLString(osXPath.substr(nPos, nPosColumn - nPos));
346 : }
347 :
348 : /************************************************************************/
349 : /* LaunderFieldNames() */
350 : /************************************************************************/
351 :
352 : // Make sure that field names are unique within the class
353 7618 : bool GMLASSchemaAnalyzer::LaunderFieldNames(GMLASFeatureClass &oClass)
354 : {
355 7618 : std::vector<GMLASField> &aoFields = oClass.GetFields();
356 :
357 : // Duplicates can happen if a class has both an element and an attribute
358 : // with same name, and/or attributes/elements with same name in different
359 : // namespaces.
360 :
361 : // Detect duplicated field names
362 15236 : std::map<CPLString, std::list<int>> oMapNameToFieldIndex;
363 219014 : for (int i = 0; i < static_cast<int>(aoFields.size()); i++)
364 : {
365 211396 : if (aoFields[i].GetCategory() == GMLASField::REGULAR)
366 : {
367 181913 : oMapNameToFieldIndex[aoFields[i].GetName()].push_back(i);
368 : }
369 : }
370 :
371 15236 : std::set<CPLString> oSetDuplicates;
372 189438 : for (const auto &oIter : oMapNameToFieldIndex)
373 : {
374 : // Has it duplicates ?
375 181820 : const size_t nOccurrences = oIter.second.size();
376 181820 : if (nOccurrences > 1)
377 : {
378 48 : oSetDuplicates.insert(oIter.first);
379 : }
380 : }
381 :
382 7711 : while (!oSetDuplicates.empty())
383 : {
384 : // Iterate over the unique names
385 93 : auto oIterSet = oSetDuplicates.begin();
386 186 : while (oIterSet != oSetDuplicates.end())
387 : {
388 93 : auto oIterSetNext = oIterSet;
389 93 : ++oIterSetNext;
390 :
391 93 : auto oIterMap = oMapNameToFieldIndex.find(*oIterSet);
392 93 : CPLAssert(oIterMap != oMapNameToFieldIndex.end());
393 93 : auto &list = oIterMap->second;
394 :
395 : const CPLString oClassNS =
396 93 : GetNSOfLastXPathComponent(oClass.GetXPath());
397 93 : bool bHasDoneRenamingForThatCase = false;
398 :
399 93 : auto oIterList = list.begin();
400 :
401 : // Update oMapNameToFieldIndex and oSetDuplicates with the
402 : // new field name, and removing the old one.
403 : const auto updateSetAndMapWithNewName =
404 93 : [&oIterList, &list, &oMapNameToFieldIndex,
405 186 : &oSetDuplicates](int nFieldIdx, const std::string &osNewName)
406 : {
407 93 : list.erase(oIterList);
408 93 : auto &newList = oMapNameToFieldIndex[osNewName];
409 93 : newList.push_back(nFieldIdx);
410 93 : if (newList.size() > 1)
411 0 : oSetDuplicates.insert(osNewName);
412 186 : };
413 :
414 93 : while (oIterList != list.end())
415 : {
416 93 : auto oIterListNext = oIterList;
417 93 : ++oIterListNext;
418 :
419 93 : const int nFieldIdx = *oIterList;
420 93 : GMLASField &oField = aoFields[nFieldIdx];
421 : // CPLDebug("GMLAS", "%s", oField.GetXPath().c_str() );
422 : const CPLString oNS(
423 93 : GetNSOfLastXPathComponent(oField.GetXPath()));
424 : // If the field has a namespace that is not the one of its
425 : // class, then prefix its name with its namespace
426 139 : if (!oNS.empty() && oNS != oClassNS &&
427 139 : !STARTS_WITH(oField.GetName(), (oNS + "_").c_str()))
428 : {
429 46 : bHasDoneRenamingForThatCase = true;
430 92 : const auto osNewName = oNS + "_" + oField.GetName();
431 46 : oField.SetName(osNewName);
432 46 : updateSetAndMapWithNewName(nFieldIdx, osNewName);
433 46 : break;
434 : }
435 : // If it is an attribute without a particular namespace,
436 : // then suffix with _attr
437 94 : else if (oNS.empty() &&
438 94 : oField.GetXPath().find('@') != std::string::npos &&
439 47 : oField.GetName().find("_attr") == std::string::npos)
440 : {
441 47 : bHasDoneRenamingForThatCase = true;
442 94 : const auto osNewName = oField.GetName() + "_attr";
443 47 : oField.SetName(osNewName);
444 47 : updateSetAndMapWithNewName(nFieldIdx, osNewName);
445 47 : break;
446 : }
447 :
448 0 : oIterList = oIterListNext;
449 : }
450 :
451 : // If none of the above renaming strategies have worked, then
452 : // append a counter to the duplicates.
453 93 : if (!bHasDoneRenamingForThatCase)
454 : {
455 0 : int i = 0;
456 0 : oIterList = list.begin();
457 0 : while (oIterList != list.end())
458 : {
459 0 : auto oIterListNext = oIterList;
460 0 : ++oIterListNext;
461 :
462 0 : const int nFieldIdx = *oIterList;
463 0 : GMLASField &oField = aoFields[nFieldIdx];
464 0 : if (i > 0)
465 : {
466 : const auto osNewName =
467 0 : oField.GetName() +
468 0 : CPLSPrintf("%d", static_cast<int>(i) + 1);
469 0 : oField.SetName(osNewName);
470 0 : updateSetAndMapWithNewName(nFieldIdx, osNewName);
471 : }
472 :
473 0 : ++i;
474 0 : oIterList = oIterListNext;
475 : }
476 : }
477 :
478 : // Update oSetDuplicates and oMapNameToFieldIndex if we have
479 : // no longer duplicates for the current name
480 93 : if (list.size() <= 1)
481 : {
482 48 : if (list.empty())
483 : {
484 0 : oMapNameToFieldIndex.erase(oIterMap);
485 : }
486 48 : oSetDuplicates.erase(oIterSet);
487 : }
488 :
489 93 : oIterSet = oIterSetNext;
490 : }
491 : }
492 :
493 : #ifdef DEBUG
494 : {
495 : // Check that the above algorithm managed to deduplicate names
496 15236 : std::set<CPLString> oSetNames;
497 219014 : for (const auto &oField : aoFields)
498 : {
499 211396 : if (oField.GetCategory() == GMLASField::REGULAR)
500 : {
501 181913 : const auto &osName = oField.GetName();
502 181913 : CPLAssert(oSetNames.find(osName) == oSetNames.end());
503 181913 : oSetNames.insert(osName);
504 : }
505 : }
506 : }
507 : #endif
508 :
509 : // Now check if we must truncate names
510 7618 : if (m_nIdentifierMaxLength >= MIN_VALUE_OF_MAX_IDENTIFIER_LENGTH)
511 : {
512 23762 : for (size_t i = 0; i < aoFields.size(); i++)
513 : {
514 21938 : int nNameSize = static_cast<int>(aoFields[i].GetName().size());
515 : /* Somewhat arbitrary limitation to avoid performance issues in */
516 : /* OGRGMLASTruncateIdentifier() */
517 21938 : if (nNameSize > 1024)
518 : {
519 0 : CPLError(CE_Failure, CPLE_NotSupported,
520 : "Field name with excessive length (%d) found",
521 : nNameSize);
522 0 : return false;
523 : }
524 21938 : if (nNameSize > m_nIdentifierMaxLength)
525 : {
526 6904 : aoFields[i].SetName(OGRGMLASTruncateIdentifier(
527 3452 : aoFields[i].GetName(), m_nIdentifierMaxLength));
528 : }
529 : }
530 : }
531 :
532 7618 : if (m_bPGIdentifierLaundering)
533 : {
534 213405 : for (size_t i = 0; i < aoFields.size(); i++)
535 : {
536 : char *pszLaundered =
537 206143 : OGRPGCommonLaunderName(aoFields[i].GetName(), "GMLAS", false);
538 206143 : aoFields[i].SetName(pszLaundered);
539 206143 : CPLFree(pszLaundered);
540 : }
541 : }
542 :
543 : // Detect duplicated field names
544 15236 : std::map<CPLString, std::vector<int>> oSetNames;
545 219014 : for (int i = 0; i < static_cast<int>(aoFields.size()); i++)
546 : {
547 211396 : if (aoFields[i].GetCategory() == GMLASField::REGULAR)
548 : {
549 363826 : CPLString osName(aoFields[i].GetName());
550 181913 : if (m_bCaseInsensitiveIdentifier)
551 181913 : osName.toupper();
552 181913 : oSetNames[osName].push_back(i);
553 : }
554 : }
555 :
556 : // Iterate over the unique names
557 189522 : for (const auto &oIter : oSetNames)
558 : {
559 : // Has it duplicates ?
560 181904 : const size_t nOccurrences = oIter.second.size();
561 181904 : if (nOccurrences > 1)
562 : {
563 27 : for (size_t i = 0; i < nOccurrences; i++)
564 : {
565 18 : GMLASField &oField = aoFields[oIter.second[i]];
566 18 : oField.SetName(OGRGMLASAddSerialNumber(
567 18 : oField.GetName(), static_cast<int>(i + 1), nOccurrences,
568 : m_nIdentifierMaxLength));
569 : }
570 : }
571 : }
572 :
573 : // Recursively process nested classes
574 7618 : std::vector<GMLASFeatureClass> &aoNestedClasses = oClass.GetNestedClasses();
575 11763 : for (size_t i = 0; i < aoNestedClasses.size(); i++)
576 : {
577 4145 : if (!LaunderFieldNames(aoNestedClasses[i]))
578 0 : return false;
579 : }
580 7618 : return true;
581 : }
582 :
583 : /************************************************************************/
584 : /* CollectClassesReferences() */
585 : /************************************************************************/
586 :
587 18045 : void GMLASSchemaAnalyzer::CollectClassesReferences(
588 : GMLASFeatureClass &oClass, std::vector<GMLASFeatureClass *> &aoClasses)
589 : {
590 18045 : aoClasses.push_back(&oClass);
591 18045 : std::vector<GMLASFeatureClass> &aoNestedClasses = oClass.GetNestedClasses();
592 22190 : for (size_t i = 0; i < aoNestedClasses.size(); i++)
593 : {
594 4145 : CollectClassesReferences(aoNestedClasses[i], aoClasses);
595 : }
596 18045 : }
597 :
598 : /************************************************************************/
599 : /* LaunderClassNames() */
600 : /************************************************************************/
601 :
602 179 : void GMLASSchemaAnalyzer::LaunderClassNames()
603 : {
604 358 : std::vector<GMLASFeatureClass *> aoClasses;
605 14079 : for (size_t i = 0; i < m_aoClasses.size(); i++)
606 : {
607 13900 : CollectClassesReferences(m_aoClasses[i], aoClasses);
608 : }
609 :
610 179 : if (m_nIdentifierMaxLength >= MIN_VALUE_OF_MAX_IDENTIFIER_LENGTH)
611 : {
612 3068 : for (size_t i = 0; i < aoClasses.size(); i++)
613 : {
614 2973 : int nNameSize = static_cast<int>(aoClasses[i]->GetName().size());
615 2973 : if (nNameSize > m_nIdentifierMaxLength)
616 : {
617 2046 : aoClasses[i]->SetName(OGRGMLASTruncateIdentifier(
618 1023 : aoClasses[i]->GetName(), m_nIdentifierMaxLength));
619 : }
620 : }
621 : }
622 :
623 179 : if (m_bPGIdentifierLaundering)
624 : {
625 17828 : for (size_t i = 0; i < aoClasses.size(); i++)
626 : {
627 : char *pszLaundered =
628 17652 : OGRPGCommonLaunderName(aoClasses[i]->GetName(), "GMLAS", false);
629 17652 : aoClasses[i]->SetName(pszLaundered);
630 17652 : CPLFree(pszLaundered);
631 : }
632 : }
633 :
634 : // Detect duplicated names. This should normally not happen in normal
635 : // conditions except if you have classes like
636 : // prefix_foo, prefix:foo, other_prefix:foo
637 : // or if names have been truncated in the previous step
638 358 : std::map<CPLString, std::vector<int>> oSetNames;
639 18224 : for (int i = 0; i < static_cast<int>(aoClasses.size()); i++)
640 : {
641 36090 : CPLString osName(aoClasses[i]->GetName());
642 18045 : if (m_bCaseInsensitiveIdentifier)
643 18045 : osName.toupper();
644 18045 : oSetNames[osName].push_back(i);
645 : }
646 :
647 : // Iterate over the unique names
648 18177 : for (const auto &oIter : oSetNames)
649 : {
650 : // Has it duplicates ?
651 17998 : const size_t nOccurrences = oIter.second.size();
652 17998 : if (nOccurrences > 1)
653 : {
654 97 : for (size_t i = 0; i < nOccurrences; i++)
655 : {
656 72 : GMLASFeatureClass *poClass = aoClasses[oIter.second[i]];
657 72 : poClass->SetName(OGRGMLASAddSerialNumber(
658 72 : poClass->GetName(), static_cast<int>(i + 1), nOccurrences,
659 : m_nIdentifierMaxLength));
660 : }
661 : }
662 : }
663 179 : }
664 :
665 : /************************************************************************/
666 : /* GMLASUniquePtr() */
667 : /************************************************************************/
668 :
669 : // Poor-man std::unique_ptr
670 : template <class T> class GMLASUniquePtr
671 : {
672 : T *m_p;
673 :
674 : GMLASUniquePtr(const GMLASUniquePtr &);
675 : GMLASUniquePtr &operator=(const GMLASUniquePtr &);
676 :
677 : public:
678 474 : explicit GMLASUniquePtr(T *p) : m_p(p)
679 : {
680 474 : }
681 :
682 474 : ~GMLASUniquePtr()
683 : {
684 474 : delete m_p;
685 474 : }
686 :
687 2272 : T *operator->() const
688 : {
689 2272 : CPLAssert(m_p);
690 2272 : return m_p;
691 : }
692 :
693 748 : T *get() const
694 : {
695 748 : return m_p;
696 : }
697 :
698 : T *release()
699 : {
700 : T *ret = m_p;
701 : m_p = NULL;
702 : return ret;
703 : }
704 : };
705 :
706 : /************************************************************************/
707 : /* GetTopElementDeclarationFromXPath() */
708 : /************************************************************************/
709 :
710 : XSElementDeclaration *
711 3494 : GMLASSchemaAnalyzer::GetTopElementDeclarationFromXPath(const CPLString &osXPath,
712 : XSModel *poModel)
713 : {
714 3494 : const char *pszTypename = osXPath.c_str();
715 3494 : const char *pszColon = strrchr(pszTypename, ':');
716 3494 : XSElementDeclaration *poEltDecl = nullptr;
717 3494 : if (pszColon != nullptr)
718 : {
719 6918 : CPLString osNSPrefix = pszTypename;
720 3459 : osNSPrefix.resize(pszColon - pszTypename);
721 6918 : CPLString osName = pszColon + 1;
722 6918 : CPLString osNSURI;
723 :
724 12688 : for (const auto &oIterNS : m_oMapURIToPrefix)
725 : {
726 12688 : const CPLString &osIterNSURI(oIterNS.first);
727 12688 : const CPLString &osIterNSPrefix(oIterNS.second);
728 12688 : if (osNSPrefix == osIterNSPrefix)
729 : {
730 3459 : osNSURI = osIterNSURI;
731 3459 : break;
732 : }
733 : }
734 3459 : XMLCh *xmlNS = nullptr;
735 3459 : XMLCh *xmlName = nullptr;
736 : try
737 : {
738 3459 : xmlNS = XMLString::transcode(osNSURI);
739 3459 : xmlName = XMLString::transcode(osName);
740 3459 : poEltDecl = poModel->getElementDeclaration(xmlName, xmlNS);
741 : }
742 0 : catch (const TranscodingException &e)
743 : {
744 0 : CPLError(CE_Failure, CPLE_AppDefined, "TranscodingException: %s",
745 0 : transcode(e.getMessage()).c_str());
746 : }
747 3459 : XMLString::release(&xmlNS);
748 3459 : XMLString::release(&xmlName);
749 : }
750 : else
751 : {
752 : try
753 : {
754 35 : XMLCh *xmlName = XMLString::transcode(pszTypename);
755 35 : poEltDecl = poModel->getElementDeclaration(xmlName, nullptr);
756 35 : XMLString::release(&xmlName);
757 : }
758 0 : catch (const TranscodingException &e)
759 : {
760 0 : CPLError(CE_Failure, CPLE_AppDefined, "TranscodingException: %s",
761 0 : transcode(e.getMessage()).c_str());
762 : }
763 : }
764 3494 : return poEltDecl;
765 : }
766 :
767 : /************************************************************************/
768 : /* IsEltCompatibleOfFC() */
769 : /************************************************************************/
770 :
771 : static XSComplexTypeDefinition *
772 83510 : IsEltCompatibleOfFC(XSElementDeclaration *poEltDecl)
773 : {
774 83510 : XSTypeDefinition *poTypeDef = poEltDecl->getTypeDefinition();
775 165941 : if (poTypeDef->getTypeCategory() == XSTypeDefinition::COMPLEX_TYPE &&
776 165941 : transcode(poEltDecl->getName()) != szFEATURE_COLLECTION)
777 : {
778 82295 : XSComplexTypeDefinition *poCT =
779 : reinterpret_cast<XSComplexTypeDefinition *>(poTypeDef);
780 : XSComplexTypeDefinition::CONTENT_TYPE eContentType(
781 82295 : poCT->getContentType());
782 82295 : if (eContentType == XSComplexTypeDefinition::CONTENTTYPE_ELEMENT ||
783 : eContentType == XSComplexTypeDefinition::CONTENTTYPE_MIXED)
784 : {
785 60523 : return poCT;
786 : }
787 : }
788 22987 : return nullptr;
789 : }
790 :
791 : /************************************************************************/
792 : /* DerivesFromGMLFeature() */
793 : /************************************************************************/
794 :
795 1218 : bool GMLASSchemaAnalyzer::DerivesFromGMLFeature(XSElementDeclaration *poEltDecl)
796 : {
797 1218 : XSElementDeclaration *poIter = poEltDecl;
798 : while (true)
799 : {
800 : XSElementDeclaration *poSubstGroup =
801 2676 : poIter->getSubstitutionGroupAffiliation();
802 2676 : if (poSubstGroup == nullptr)
803 924 : break;
804 1752 : const CPLString osSubstNS(transcode(poSubstGroup->getNamespace()));
805 1752 : const CPLString osSubstName(transcode(poSubstGroup->getName()));
806 1752 : if (IsGMLNamespace(osSubstNS) && osSubstName == "_FeatureCollection")
807 : {
808 6 : return false;
809 : }
810 4148 : if (IsGMLNamespace(osSubstNS) &&
811 2402 : (osSubstName == "AbstractFeature" || osSubstName == "_Feature"))
812 : {
813 288 : return true;
814 : }
815 1458 : poIter = poSubstGroup;
816 1458 : }
817 924 : return false;
818 : }
819 :
820 : /************************************************************************/
821 : /* Analyze() */
822 : /************************************************************************/
823 :
824 190 : bool GMLASSchemaAnalyzer::Analyze(GMLASXSDCache &oCache,
825 : const CPLString &osBaseDirname,
826 : std::vector<PairURIFilename> &aoXSDs,
827 : bool bSchemaFullChecking,
828 : bool bHandleMultipleImports)
829 : {
830 : GMLASUniquePtr<XMLGrammarPool> poGrammarPool(
831 380 : (new XMLGrammarPoolImpl(XMLPlatformUtils::fgMemoryManager)));
832 :
833 380 : std::vector<CPLString> aoNamespaces;
834 : GMLASAnalyzerEntityResolver oXSDEntityResolver(
835 380 : CPLString(), m_oMapURIToPrefix, m_oMapDocNSURIToPrefix, oCache);
836 :
837 : // In this first pass we load the schemas that are directly pointed by
838 : // the user with the XSD open option, or that we found in the
839 : // xsi:schemaLocation attribute The namespaces of those schemas are the
840 : // "first choice" namespaces from which we will try to find elements to turn
841 : // them into layers
842 190 : aoNamespaces.push_back("");
843 464 : for (size_t i = 0; i < aoXSDs.size(); i++)
844 : {
845 284 : const CPLString osURI(aoXSDs[i].first);
846 284 : const CPLString osXSDFilename(aoXSDs[i].second);
847 :
848 : GMLASUniquePtr<SAX2XMLReader> poParser(
849 : XMLReaderFactory::createXMLReader(XMLPlatformUtils::fgMemoryManager,
850 284 : poGrammarPool.get()));
851 :
852 : // Commonly useful configuration.
853 : //
854 284 : poParser->setFeature(XMLUni::fgSAX2CoreNameSpaces, true);
855 284 : poParser->setFeature(XMLUni::fgSAX2CoreNameSpacePrefixes, true);
856 284 : poParser->setFeature(XMLUni::fgSAX2CoreValidation, true);
857 :
858 : // Enable validation.
859 : //
860 284 : poParser->setFeature(XMLUni::fgXercesSchema, true);
861 :
862 : #ifndef __COVERITY__
863 : // coverity[unsafe_xml_parse_config]
864 284 : poParser->setFeature(XMLUni::fgXercesValidationErrorAsFatal, false);
865 : #endif
866 :
867 : // Use the loaded grammar during parsing.
868 : //
869 284 : poParser->setFeature(XMLUni::fgXercesUseCachedGrammarInParse, true);
870 :
871 : // Don't load schemas from any other source (e.g., from XML document's
872 : // xsi:schemaLocation attributes).
873 : //
874 284 : poParser->setFeature(XMLUni::fgXercesLoadSchema, false);
875 :
876 284 : poParser->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution,
877 284 : true);
878 :
879 284 : Grammar *poGrammar = nullptr;
880 284 : if (!GMLASReader::LoadXSDInParser(
881 : poParser.get(), oCache, oXSDEntityResolver, osBaseDirname,
882 : osXSDFilename, &poGrammar, bSchemaFullChecking,
883 : bHandleMultipleImports))
884 : {
885 10 : return false;
886 : }
887 :
888 : // Some .xsd like
889 : // http://www.opengis.net/gwml-main/2.1 ->
890 : // https://wfspoc.brgm-rec.fr/constellation/WS/wfs/BRGM:GWML2?request=DescribeFeatureType&version=2.0.0&service=WFS&namespace=xmlns(ns1=http://www.opengis.net/gwml-main/2.1)&typenames=ns1:GW_Aquifer
891 : // do not have a declared targetNamespace, so use the one of the
892 : // schemaLocation if the grammar returns an empty namespace.
893 548 : CPLString osGrammarURI(transcode(poGrammar->getTargetNamespace()));
894 274 : if (osGrammarURI.empty())
895 : {
896 28 : if (!osURI.empty())
897 1 : osGrammarURI = osURI;
898 : }
899 274 : if (!osGrammarURI.empty())
900 : {
901 : // Patch back the aoXSDs element in case we didn't know the
902 : // namespace URI initially
903 247 : if (osURI.empty())
904 27 : aoXSDs[i].first = osGrammarURI;
905 247 : aoNamespaces.push_back(std::move(osGrammarURI));
906 : }
907 : }
908 :
909 180 : m_osGMLVersionFound = oXSDEntityResolver.GetGMLVersionFound();
910 180 : m_oSetSchemaURLs = oXSDEntityResolver.GetSchemaURLS();
911 :
912 180 : m_oIgnoredXPathMatcher.SetDocumentMapURIToPrefix(m_oMapURIToPrefix);
913 180 : m_oChildrenElementsConstraintsXPathMatcher.SetDocumentMapURIToPrefix(
914 180 : m_oMapURIToPrefix);
915 180 : m_oForcedFlattenedXPathMatcher.SetDocumentMapURIToPrefix(m_oMapURIToPrefix);
916 180 : m_oDisabledFlattenedXPathMatcher.SetDocumentMapURIToPrefix(
917 180 : m_oMapURIToPrefix);
918 :
919 180 : XSModel *poModel = getGrammarPool(poGrammarPool.get());
920 180 : CPLAssert(poModel); // should not be null according to doc
921 :
922 : #if 0
923 : XSNamespaceItem* nsItem = poModel->getNamespaceItem(
924 : loadedGrammar->getTargetNamespace());
925 : if( nsItem == NULL )
926 : {
927 : CPLError(CE_Failure, CPLE_AppDefined,
928 : "getNamespaceItem(%s) failed",
929 : transcode(loadedGrammar->getTargetNamespace()).c_str());
930 : return false;
931 : }
932 : #endif
933 :
934 180 : bool bFoundGMLFeature = false;
935 :
936 : // Second pass, in all namespaces, to figure out inheritance relationships
937 : // and group models that have names
938 360 : std::map<CPLString, CPLString> oMapURIToPrefixWithEmpty(m_oMapURIToPrefix);
939 180 : oMapURIToPrefixWithEmpty[""] = "";
940 1305 : for (const auto &oIterNS : oMapURIToPrefixWithEmpty)
941 : {
942 1125 : const CPLString &osNSURI(oIterNS.first);
943 2837 : if (osNSURI == szXS_URI || osNSURI == szXSI_URI ||
944 2837 : osNSURI == szXMLNS_URI || osNSURI == szXLINK_URI)
945 : {
946 546 : continue;
947 : }
948 :
949 579 : XMLCh *xmlNamespace = nullptr;
950 : try
951 : {
952 579 : xmlNamespace = XMLString::transcode(osNSURI.c_str());
953 : }
954 0 : catch (const TranscodingException &e)
955 : {
956 0 : CPLError(CE_Failure, CPLE_AppDefined, "TranscodingException: %s",
957 0 : transcode(e.getMessage()).c_str());
958 0 : return false;
959 : }
960 :
961 : XSNamedMap<XSObject> *poMapModelGroupDefinition =
962 579 : poModel->getComponentsByNamespace(
963 : XSConstants::MODEL_GROUP_DEFINITION, xmlNamespace);
964 :
965 : // Remember group models that have names
966 1472 : for (XMLSize_t i = 0; poMapModelGroupDefinition != nullptr &&
967 659 : i < poMapModelGroupDefinition->getLength();
968 : i++)
969 : {
970 : XSModelGroupDefinition *modelGroupDefinition =
971 : reinterpret_cast<XSModelGroupDefinition *>(
972 234 : poMapModelGroupDefinition->item(i));
973 234 : m_oMapModelGroupToMGD[modelGroupDefinition->getModelGroup()] =
974 : modelGroupDefinition;
975 : }
976 :
977 579 : CPLDebug("GMLAS", "Discovering substitutions of %s (%s)",
978 : oIterNS.second.c_str(), osNSURI.c_str());
979 :
980 579 : XSNamedMap<XSObject> *poMapElements = poModel->getComponentsByNamespace(
981 : XSConstants::ELEMENT_DECLARATION, xmlNamespace);
982 :
983 6367 : for (XMLSize_t i = 0;
984 6367 : poMapElements != nullptr && i < poMapElements->getLength(); i++)
985 : {
986 : XSElementDeclaration *poEltDecl =
987 : reinterpret_cast<XSElementDeclaration *>(
988 5788 : poMapElements->item(i));
989 : XSElementDeclaration *poSubstGroup =
990 5788 : poEltDecl->getSubstitutionGroupAffiliation();
991 : const CPLString osEltXPath(
992 11576 : MakeXPath(transcode(poEltDecl->getNamespace()),
993 17364 : transcode(poEltDecl->getName())));
994 5788 : m_oMapXPathToEltDecl[osEltXPath] = poEltDecl;
995 5788 : if (poSubstGroup)
996 : {
997 1793 : m_oMapParentEltToChildElt[poSubstGroup].push_back(poEltDecl);
998 : #ifdef DEBUG_VERBOSE
999 : CPLString osParentType(
1000 : MakeXPath(transcode(poSubstGroup->getNamespace()),
1001 : transcode(poSubstGroup->getName())));
1002 : CPLDebug("GMLAS", "%s is a substitution for %s",
1003 : osEltXPath.c_str(), osParentType.c_str());
1004 : #endif
1005 :
1006 : // Check if this element derives from
1007 : // gml:_Feature/AbstractFeature
1008 326 : if (!bFoundGMLFeature && m_bInstantiateGMLFeaturesOnly &&
1009 2341 : !IsGMLNamespace(osNSURI) &&
1010 222 : DerivesFromGMLFeature(poEltDecl))
1011 : {
1012 36 : CPLDebug("GMLAS",
1013 : "Restricting (in first pass) top level "
1014 : "elements to those deriving from "
1015 : "gml:_Feature/gml:AbstractFeature (due "
1016 : "to %s found)",
1017 : osEltXPath.c_str());
1018 36 : bFoundGMLFeature = true;
1019 : }
1020 : }
1021 : }
1022 :
1023 579 : XMLString::release(&xmlNamespace);
1024 : }
1025 :
1026 : // Check that we can find elements in the namespaces pointed in the
1027 : // xsi:schemaLocation of the document, then fallback to namespaces
1028 : // that might be indirectly imported by those first level namespaces
1029 180 : bool bFoundElementsInFirstChoiceNamespaces = false;
1030 512 : for (size_t iNS = 0;
1031 512 : !bFoundElementsInFirstChoiceNamespaces && iNS < aoNamespaces.size();
1032 : iNS++)
1033 : {
1034 332 : XMLCh *xmlNamespace = nullptr;
1035 : try
1036 : {
1037 332 : xmlNamespace = XMLString::transcode(aoNamespaces[iNS].c_str());
1038 : }
1039 0 : catch (const TranscodingException &e)
1040 : {
1041 0 : CPLError(CE_Failure, CPLE_AppDefined, "TranscodingException: %s",
1042 0 : transcode(e.getMessage()).c_str());
1043 0 : return false;
1044 : }
1045 :
1046 332 : XSNamedMap<XSObject> *poMapElements = poModel->getComponentsByNamespace(
1047 : XSConstants::ELEMENT_DECLARATION, xmlNamespace);
1048 332 : bFoundElementsInFirstChoiceNamespaces =
1049 332 : poMapElements != nullptr && poMapElements->getLength() > 0;
1050 332 : XMLString::release(&xmlNamespace);
1051 : }
1052 180 : if (!bFoundElementsInFirstChoiceNamespaces)
1053 : {
1054 1 : CPLDebug("GMLAS", "Did not find element in 'first choice' namespaces. "
1055 : "Falling back to the namespaces they import");
1056 1 : aoNamespaces.clear();
1057 6 : for (const auto &oIterNS : oMapURIToPrefixWithEmpty)
1058 : {
1059 5 : const CPLString &osNSURI(oIterNS.first);
1060 12 : if (osNSURI == szXS_URI || osNSURI == szXSI_URI ||
1061 7 : osNSURI == szXMLNS_URI || osNSURI == szXLINK_URI ||
1062 6 : osNSURI == szWFS_URI || osNSURI == szWFS20_URI ||
1063 11 : osNSURI == szGML_URI || osNSURI == szGML32_URI)
1064 : {
1065 : // Skip all boring namespaces
1066 3 : continue;
1067 : }
1068 2 : aoNamespaces.push_back(osNSURI);
1069 : }
1070 : }
1071 :
1072 : // Find which elements must be top levels (because referenced several
1073 : // times)
1074 360 : std::set<XSElementDeclaration *> oSetVisitedEltDecl;
1075 360 : std::set<XSModelGroup *> oSetVisitedModelGroups;
1076 360 : std::vector<XSElementDeclaration *> oVectorEltsForTopClass;
1077 :
1078 : // For some reason, different XSElementDeclaration* can point to the
1079 : // same element, but we only want to instantiate a single class.
1080 : // This is the case for base:SpatialDataSet in
1081 : // inspire/geologicalunit/geologicalunit.gml test dataset.
1082 360 : std::set<CPLString> aoSetXPathEltsForTopClass;
1083 :
1084 : // Third and fourth passes
1085 540 : for (int iPass = 0; iPass < 2; ++iPass)
1086 : {
1087 1214 : for (size_t iNS = 0; iNS < aoNamespaces.size(); iNS++)
1088 : {
1089 854 : XMLCh *xmlNamespace = nullptr;
1090 : try
1091 : {
1092 854 : xmlNamespace = XMLString::transcode(aoNamespaces[iNS].c_str());
1093 : }
1094 0 : catch (const TranscodingException &e)
1095 : {
1096 0 : CPLError(CE_Failure, CPLE_AppDefined,
1097 : "TranscodingException: %s",
1098 0 : transcode(e.getMessage()).c_str());
1099 0 : return false;
1100 : }
1101 :
1102 : XSNamedMap<XSObject> *poMapElements =
1103 854 : poModel->getComponentsByNamespace(
1104 : XSConstants::ELEMENT_DECLARATION, xmlNamespace);
1105 :
1106 6768 : for (XMLSize_t i = 0;
1107 6768 : poMapElements != nullptr && i < poMapElements->getLength();
1108 : i++)
1109 : {
1110 : XSElementDeclaration *poEltDecl =
1111 : reinterpret_cast<XSElementDeclaration *>(
1112 5914 : poMapElements->item(i));
1113 5914 : XSComplexTypeDefinition *poCT = IsEltCompatibleOfFC(poEltDecl);
1114 5914 : if (!poEltDecl->getAbstract() && poCT != nullptr)
1115 : {
1116 : CPLString osXPath(
1117 9708 : MakeXPath(transcode(poEltDecl->getNamespace()),
1118 14562 : transcode(poEltDecl->getName())));
1119 4854 : if (!IsIgnoredXPath(osXPath))
1120 : {
1121 5846 : if (bFoundGMLFeature && m_bInstantiateGMLFeaturesOnly &&
1122 996 : !DerivesFromGMLFeature(poEltDecl))
1123 : {
1124 : // Do nothing
1125 : }
1126 4106 : else if (iPass == 0)
1127 : {
1128 : #ifdef DEBUG_VERBOSE
1129 : CPLDebug(
1130 : "GMLAS",
1131 : "%s (%s) must be exposed as "
1132 : "top-level (is top level in imported schemas)",
1133 : osXPath.c_str(),
1134 : transcode(
1135 : poEltDecl->getTypeDefinition()->getName())
1136 : .c_str());
1137 : #endif
1138 2053 : oSetVisitedEltDecl.insert(poEltDecl);
1139 2053 : if (aoSetXPathEltsForTopClass.find(osXPath) ==
1140 4106 : aoSetXPathEltsForTopClass.end())
1141 : {
1142 2053 : m_oSetEltsForTopClass.insert(poEltDecl);
1143 2053 : oVectorEltsForTopClass.push_back(poEltDecl);
1144 : aoSetXPathEltsForTopClass.insert(
1145 2053 : std::move(osXPath));
1146 : }
1147 : }
1148 : else
1149 : {
1150 2053 : bool bSimpleEnoughOut = true;
1151 2053 : int nSubCountSubEltOut = 0;
1152 2053 : auto poParticle = poCT->getParticle();
1153 2053 : if (poParticle)
1154 : {
1155 2052 : CPL_IGNORE_RET_VAL(
1156 2052 : FindElementsWithMustBeToLevel(
1157 : osXPath,
1158 : poParticle->getModelGroupTerm(), 0,
1159 : oSetVisitedEltDecl,
1160 : oSetVisitedModelGroups,
1161 : oVectorEltsForTopClass,
1162 : aoSetXPathEltsForTopClass, poModel,
1163 : bSimpleEnoughOut, nSubCountSubEltOut));
1164 : }
1165 : }
1166 : }
1167 : }
1168 : }
1169 :
1170 854 : XMLString::release(&xmlNamespace);
1171 : }
1172 : }
1173 :
1174 : // Find ambiguous class names
1175 : {
1176 3654 : for (const auto &oIter : m_oSetEltsForTopClass)
1177 : {
1178 3474 : CPLString osName(transcode(oIter->getName()));
1179 3474 : m_oMapEltNamesToInstanceCount[osName]++;
1180 : }
1181 : }
1182 :
1183 : // Instantiate all needed typenames
1184 3653 : for (const auto &poEltDecl : oVectorEltsForTopClass)
1185 : {
1186 6948 : const CPLString osXPath(MakeXPath(transcode(poEltDecl->getNamespace()),
1187 6948 : transcode(poEltDecl->getName())));
1188 :
1189 3474 : bool bError = false;
1190 : bool bResolvedType =
1191 3474 : InstantiateClassFromEltDeclaration(poEltDecl, poModel, bError);
1192 3474 : if (bError)
1193 : {
1194 1 : return false;
1195 : }
1196 3473 : if (!bResolvedType)
1197 : {
1198 0 : CPLError(
1199 : CE_Failure, CPLE_AppDefined, "Couldn't resolve %s (%s)",
1200 : osXPath.c_str(),
1201 0 : transcode(poEltDecl->getTypeDefinition()->getName()).c_str());
1202 0 : return false;
1203 : }
1204 : }
1205 :
1206 179 : LaunderClassNames();
1207 :
1208 179 : return true;
1209 : }
1210 :
1211 : /************************************************************************/
1212 : /* GetAnnotationDoc() */
1213 : /************************************************************************/
1214 :
1215 442805 : static CPLString GetAnnotationDoc(const XSAnnotation *annotation)
1216 : {
1217 442805 : if (!annotation)
1218 242714 : return CPLString();
1219 400182 : CPLString osAnnot(transcode(annotation->getAnnotationString()));
1220 200091 : CPLXMLNode *psRoot = CPLParseXMLString(osAnnot);
1221 200091 : CPLStripXMLNamespace(psRoot, nullptr, TRUE);
1222 400182 : CPLString osDoc(CPLGetXMLValue(psRoot, "=annotation.documentation", ""));
1223 200091 : CPLDestroyXMLNode(psRoot);
1224 200091 : return osDoc.Trim();
1225 : }
1226 :
1227 : /************************************************************************/
1228 : /* GetAnnotationDoc() */
1229 : /************************************************************************/
1230 :
1231 204169 : static CPLString GetAnnotationDoc(const XSAnnotationList *annotationList)
1232 : {
1233 204169 : if (!annotationList)
1234 7091 : return CPLString();
1235 394156 : CPLString osRet;
1236 394156 : for (size_t i = 0; i < annotationList->size(); ++i)
1237 : {
1238 394156 : CPLString osDoc(GetAnnotationDoc(annotationList->elementAt(i)));
1239 197078 : if (!osDoc.empty())
1240 : {
1241 196960 : if (!osRet.empty())
1242 0 : osRet += "\n";
1243 196960 : osRet += osDoc;
1244 : }
1245 : }
1246 197078 : return osRet;
1247 : }
1248 :
1249 : /************************************************************************/
1250 : /* GetAnnotationDoc() */
1251 : /************************************************************************/
1252 :
1253 204169 : static CPLString GetAnnotationDoc(const XSElementDeclaration *poEltDecl)
1254 : {
1255 204169 : XSTypeDefinition *poTypeDef = poEltDecl->getTypeDefinition();
1256 204169 : CPLString osDoc = GetAnnotationDoc(poEltDecl->getAnnotation());
1257 204169 : XSAnnotationList *list = nullptr;
1258 219460 : while (poTypeDef != nullptr)
1259 : {
1260 219460 : if (poTypeDef->getTypeCategory() == XSTypeDefinition::COMPLEX_TYPE)
1261 : {
1262 59298 : XSComplexTypeDefinition *poCT =
1263 : reinterpret_cast<XSComplexTypeDefinition *>(poTypeDef);
1264 59298 : list = poCT->getAnnotations();
1265 : }
1266 160162 : else if (poTypeDef->getTypeCategory() == XSTypeDefinition::SIMPLE_TYPE)
1267 : {
1268 160162 : XSSimpleTypeDefinition *poST =
1269 : reinterpret_cast<XSSimpleTypeDefinition *>(poTypeDef);
1270 160162 : list = poST->getAnnotations();
1271 : }
1272 219460 : if (list != nullptr)
1273 197078 : break;
1274 22382 : XSTypeDefinition *poNewTypeDef = poTypeDef->getBaseType();
1275 22382 : if (poNewTypeDef == poTypeDef)
1276 7091 : break;
1277 15291 : poTypeDef = poNewTypeDef;
1278 : }
1279 408338 : CPLString osDoc2 = GetAnnotationDoc(list);
1280 204169 : if (!osDoc.empty() && !osDoc2.empty())
1281 : {
1282 893 : osDoc += "\n";
1283 893 : osDoc += osDoc2;
1284 : }
1285 203276 : else if (!osDoc2.empty())
1286 196067 : osDoc = std::move(osDoc2);
1287 408338 : return osDoc;
1288 : }
1289 :
1290 : /************************************************************************/
1291 : /* InstantiateClassFromEltDeclaration() */
1292 : /************************************************************************/
1293 :
1294 3474 : bool GMLASSchemaAnalyzer::InstantiateClassFromEltDeclaration(
1295 : XSElementDeclaration *poEltDecl, XSModel *poModel, bool &bError)
1296 : {
1297 3474 : bError = false;
1298 3474 : XSComplexTypeDefinition *poCT = IsEltCompatibleOfFC(poEltDecl);
1299 3474 : if (!poEltDecl->getAbstract() && poCT != nullptr)
1300 : {
1301 6948 : GMLASFeatureClass oClass;
1302 6948 : const CPLString osEltName(transcode(poEltDecl->getName()));
1303 : const CPLString osXPath(
1304 6948 : MakeXPath(transcode(poEltDecl->getNamespace()), osEltName));
1305 :
1306 3474 : if (IsIgnoredXPath(osXPath))
1307 : {
1308 : #ifdef DEBUG_VERBOSE
1309 : CPLDebug("GMLAS", "%s is in ignored xpaths", osXPath.c_str());
1310 : #endif
1311 0 : return false;
1312 : }
1313 :
1314 3474 : if (m_oMapEltNamesToInstanceCount[osEltName] > 1)
1315 : {
1316 90 : CPLString osLaunderedXPath(osXPath);
1317 45 : osLaunderedXPath.replaceAll(':', '_');
1318 45 : oClass.SetName(osLaunderedXPath);
1319 : }
1320 : else
1321 3429 : oClass.SetName(osEltName);
1322 :
1323 : #ifdef DEBUG_VERBOSE
1324 : CPLDebug("GMLAS", "Instantiating element %s", osXPath.c_str());
1325 : #endif
1326 3474 : oClass.SetXPath(osXPath);
1327 3474 : oClass.SetIsTopLevelElt(
1328 3474 : GetTopElementDeclarationFromXPath(osXPath, poModel) != nullptr);
1329 :
1330 6948 : std::set<XSModelGroup *> oSetVisitedModelGroups;
1331 :
1332 3474 : oClass.SetDocumentation(GetAnnotationDoc(poEltDecl));
1333 :
1334 : // might be NULL on swe:values for example
1335 3474 : if (poCT->getParticle() != nullptr)
1336 : {
1337 3435 : std::map<CPLString, int> oMapCountOccurrencesOfSameName;
1338 3435 : BuildMapCountOccurrencesOfSameName(
1339 : poCT->getParticle()->getModelGroupTerm(),
1340 : oMapCountOccurrencesOfSameName);
1341 :
1342 3435 : OGRwkbGeometryType eGeomType = wkbUnknown;
1343 3506 : if (IsGMLNamespace(transcode(poCT->getNamespace())) &&
1344 71 : (eGeomType = GetOGRGeometryType(poCT)) != wkbNone)
1345 : {
1346 2 : GMLASField oField;
1347 1 : oField.SetName("geometry");
1348 1 : oField.SetMinOccurs(1);
1349 1 : oField.SetMaxOccurs(1);
1350 1 : oField.SetType(GMLAS_FT_GEOMETRY, szFAKEXS_GEOMETRY);
1351 1 : oField.SetGeomType(eGeomType);
1352 1 : oField.SetXPath(osXPath + szMATCH_ALL);
1353 1 : oField.SetIncludeThisEltInBlob(true);
1354 :
1355 1 : oClass.AddField(oField);
1356 : }
1357 3434 : else if (!ExploreModelGroup(
1358 : poCT->getParticle()->getModelGroupTerm(),
1359 : poCT->getAttributeUses(), oClass, 0,
1360 : oSetVisitedModelGroups, poModel,
1361 : oMapCountOccurrencesOfSameName))
1362 : {
1363 1 : bError = true;
1364 1 : return false;
1365 : }
1366 : }
1367 : else
1368 : {
1369 : // TODO ?
1370 : }
1371 :
1372 3473 : if (!LaunderFieldNames(oClass))
1373 0 : return false;
1374 :
1375 3473 : m_aoClasses.push_back(std::move(oClass));
1376 3473 : return true;
1377 : }
1378 0 : return false;
1379 : }
1380 :
1381 : /************************************************************************/
1382 : /* SetFieldTypeAndWidthFromDefinition() */
1383 : /************************************************************************/
1384 :
1385 : /* static */
1386 212756 : void GMLASSchemaAnalyzer::SetFieldTypeAndWidthFromDefinition(
1387 : XSSimpleTypeDefinition *poST, GMLASField &oField)
1388 : {
1389 212756 : int nMaxLength = 0;
1390 368149 : while (
1391 1161810 : poST->getBaseType() != poST &&
1392 580905 : poST->getBaseType()->getTypeCategory() ==
1393 1161810 : XSTypeDefinition::SIMPLE_TYPE &&
1394 577403 : !XMLString::equals(poST->getNamespace(), PSVIUni::fgNamespaceXmlSchema))
1395 : {
1396 : const XMLCh *maxLength =
1397 368149 : poST->getLexicalFacetValue(XSSimpleTypeDefinition::FACET_LENGTH);
1398 368149 : if (maxLength == nullptr)
1399 : {
1400 368040 : maxLength = poST->getLexicalFacetValue(
1401 : XSSimpleTypeDefinition::FACET_MAXLENGTH);
1402 : }
1403 368149 : if (maxLength != nullptr)
1404 181306 : nMaxLength = std::max(nMaxLength, atoi(transcode(maxLength)));
1405 368149 : poST = reinterpret_cast<XSSimpleTypeDefinition *>(poST->getBaseType());
1406 : }
1407 :
1408 212756 : if (XMLString::equals(poST->getNamespace(), PSVIUni::fgNamespaceXmlSchema))
1409 : {
1410 425512 : CPLString osType(transcode(poST->getName()));
1411 212756 : oField.SetType(GMLASField::GetTypeFromString(osType), osType);
1412 : }
1413 : else
1414 : {
1415 0 : CPLError(CE_Warning, CPLE_AppDefined, "Base type is not a xs: one ???");
1416 : }
1417 :
1418 212756 : oField.SetWidth(nMaxLength);
1419 212756 : }
1420 :
1421 : /************************************************************************/
1422 : /* IsSame() */
1423 : /* */
1424 : /* The objects returned by different PSVI API are not always the same */
1425 : /* so do content inspection to figure out if they are equivalent. */
1426 : /************************************************************************/
1427 :
1428 420 : bool GMLASSchemaAnalyzer::IsSame(const XSModelGroup *poModelGroup1,
1429 : const XSModelGroup *poModelGroup2)
1430 : {
1431 420 : if (poModelGroup1->getCompositor() != poModelGroup2->getCompositor())
1432 128 : return false;
1433 :
1434 292 : const XSParticleList *poParticleList1 = poModelGroup1->getParticles();
1435 292 : const XSParticleList *poParticleList2 = poModelGroup2->getParticles();
1436 292 : if (poParticleList1->size() != poParticleList2->size())
1437 115 : return false;
1438 :
1439 624 : for (size_t i = 0; i < poParticleList1->size(); ++i)
1440 : {
1441 476 : const XSParticle *poParticle1 = poParticleList1->elementAt(i);
1442 476 : const XSParticle *poParticle2 = poParticleList2->elementAt(i);
1443 949 : if (poParticle1->getTermType() != poParticle2->getTermType() ||
1444 946 : poParticle1->getMinOccurs() != poParticle2->getMinOccurs() ||
1445 1422 : poParticle1->getMaxOccurs() != poParticle2->getMaxOccurs() ||
1446 473 : poParticle1->getMaxOccursUnbounded() !=
1447 473 : poParticle2->getMaxOccursUnbounded())
1448 : {
1449 3 : return false;
1450 : }
1451 473 : switch (poParticle1->getTermType())
1452 : {
1453 0 : case XSParticle::TERM_EMPTY:
1454 0 : break;
1455 :
1456 469 : case XSParticle::TERM_ELEMENT:
1457 : {
1458 : const XSElementDeclaration *poElt1 =
1459 469 : const_cast<XSParticle *>(poParticle1)->getElementTerm();
1460 : const XSElementDeclaration *poElt2 =
1461 469 : const_cast<XSParticle *>(poParticle2)->getElementTerm();
1462 : // Pointer comparison works here
1463 469 : if (poElt1 != poElt2)
1464 26 : return false;
1465 443 : break;
1466 : }
1467 :
1468 4 : case XSParticle::TERM_MODELGROUP:
1469 : {
1470 : const XSModelGroup *psSubGroup1 =
1471 4 : const_cast<XSParticle *>(poParticle1)->getModelGroupTerm();
1472 : const XSModelGroup *psSubGroup2 =
1473 4 : const_cast<XSParticle *>(poParticle2)->getModelGroupTerm();
1474 4 : if (!IsSame(psSubGroup1, psSubGroup2))
1475 0 : return false;
1476 4 : break;
1477 : }
1478 :
1479 0 : case XSParticle::TERM_WILDCARD:
1480 : {
1481 : // TODO: check that pointer comparison works
1482 : const XSWildcard *psWildcard1 =
1483 0 : const_cast<XSParticle *>(poParticle1)->getWildcardTerm();
1484 : const XSWildcard *psWildcard2 =
1485 0 : const_cast<XSParticle *>(poParticle2)->getWildcardTerm();
1486 0 : if (psWildcard1 != psWildcard2)
1487 0 : return false;
1488 0 : break;
1489 : }
1490 :
1491 0 : default:
1492 : {
1493 0 : CPLAssert(FALSE);
1494 : return false;
1495 : }
1496 : }
1497 : }
1498 :
1499 148 : return true;
1500 : }
1501 :
1502 : /************************************************************************/
1503 : /* GetGroupName() */
1504 : /* */
1505 : /* The model group object returned when exploring a high level model */
1506 : /* group isn't the same object as the one returned by model group */
1507 : /* definitions and has no name. So we have to investigate the content */
1508 : /* of model groups to figure out if they are the same. */
1509 : /************************************************************************/
1510 :
1511 : XSModelGroupDefinition *
1512 1748 : GMLASSchemaAnalyzer::GetGroupDefinition(const XSModelGroup *poModelGroup)
1513 : {
1514 2020 : for (const auto &oIter : m_oMapModelGroupToMGD)
1515 : {
1516 416 : if (IsSame(poModelGroup, oIter.first))
1517 : {
1518 144 : return oIter.second;
1519 : }
1520 : }
1521 :
1522 1604 : return nullptr;
1523 : }
1524 :
1525 : /************************************************************************/
1526 : /* IsAnyType() */
1527 : /************************************************************************/
1528 :
1529 29226 : static bool IsAnyType(XSComplexTypeDefinition *poType)
1530 : {
1531 29226 : if (XMLString::equals(poType->getBaseType()->getNamespace(),
1532 79055 : PSVIUni::fgNamespaceXmlSchema) &&
1533 49829 : transcode(poType->getBaseType()->getName()) == szXS_ANY_TYPE)
1534 : {
1535 20603 : XSParticle *poParticle = poType->getParticle();
1536 20603 : if (poParticle != nullptr)
1537 : {
1538 20247 : XSModelGroup *poGroupTerm = poParticle->getModelGroupTerm();
1539 20247 : if (poGroupTerm != nullptr)
1540 : {
1541 20247 : XSParticleList *poParticles = poGroupTerm->getParticles();
1542 20247 : if (poParticles != nullptr)
1543 : {
1544 22149 : return poParticles->size() == 1 &&
1545 1902 : poParticles->elementAt(0)->getTermType() ==
1546 20247 : XSParticle::TERM_WILDCARD;
1547 : }
1548 : }
1549 : }
1550 356 : else if (poType->getDerivationMethod() ==
1551 : XSConstants::DERIVATION_EXTENSION)
1552 : {
1553 : // swe:values case
1554 0 : return true;
1555 : }
1556 : }
1557 8979 : return false;
1558 : }
1559 :
1560 : /************************************************************************/
1561 : /* SetFieldFromAttribute() */
1562 : /************************************************************************/
1563 :
1564 40868 : bool GMLASSchemaAnalyzer::SetFieldFromAttribute(GMLASField &oField,
1565 : XSAttributeUse *poAttr,
1566 : const CPLString &osXPathPrefix,
1567 : const CPLString &osNamePrefix)
1568 : {
1569 40868 : const XSAttributeDeclaration *poAttrDecl = poAttr->getAttrDeclaration();
1570 81736 : const CPLString osNS(transcode(poAttrDecl->getNamespace()));
1571 81736 : const CPLString osName(transcode(poAttrDecl->getName()));
1572 :
1573 40868 : if (osNamePrefix.empty())
1574 19376 : oField.SetName(osName);
1575 : else
1576 21492 : oField.SetName(osNamePrefix + "_" + osName);
1577 :
1578 40868 : XSSimpleTypeDefinition *poAttrType = poAttrDecl->getTypeDefinition();
1579 40868 : if (!poAttrType)
1580 : {
1581 1 : CPLError(CE_Failure, CPLE_AppDefined,
1582 : "Cannot get type definition for attribute %s",
1583 1 : oField.GetName().c_str());
1584 1 : return false;
1585 : }
1586 :
1587 40867 : SetFieldTypeAndWidthFromDefinition(poAttrType, oField);
1588 :
1589 40867 : oField.SetXPath(osXPathPrefix + "/@" + MakeXPath(osNS, osName));
1590 40867 : if (poAttr->getRequired())
1591 : {
1592 27243 : oField.SetNotNullable(true);
1593 : }
1594 40867 : oField.SetMinOccurs(oField.IsNotNullable() ? 1 : 0);
1595 40867 : oField.SetMaxOccurs(1);
1596 40867 : if (poAttr->getConstraintType() == XSConstants::VALUE_CONSTRAINT_FIXED)
1597 : {
1598 1117 : oField.SetFixedValue(transcode(poAttr->getConstraintValue()));
1599 : }
1600 39750 : else if (poAttr->getConstraintType() ==
1601 : XSConstants::VALUE_CONSTRAINT_DEFAULT)
1602 : {
1603 265 : oField.SetDefaultValue(transcode(poAttr->getConstraintValue()));
1604 : }
1605 :
1606 : const bool bIsList =
1607 40867 : (poAttrType->getVariety() == XSSimpleTypeDefinition::VARIETY_LIST);
1608 40867 : if (bIsList)
1609 : {
1610 1653 : SetFieldTypeAndWidthFromDefinition(poAttrType->getItemType(), oField);
1611 1653 : if (m_bUseArrays && IsCompatibleOfArray(oField.GetType()))
1612 : {
1613 1652 : oField.SetList(true);
1614 1652 : oField.SetArray(true);
1615 : }
1616 : else
1617 : {
1618 : // We should probably create an auxiliary table here, but this
1619 : // is too corner case for now...
1620 1 : oField.SetType(GMLAS_FT_STRING, szXS_STRING);
1621 : }
1622 : }
1623 :
1624 40867 : oField.SetDocumentation(GetAnnotationDoc(poAttrDecl->getAnnotation()));
1625 :
1626 40867 : return true;
1627 : }
1628 :
1629 : /************************************************************************/
1630 : /* GetConcreteImplementationTypes() */
1631 : /************************************************************************/
1632 :
1633 359997 : void GMLASSchemaAnalyzer::GetConcreteImplementationTypes(
1634 : XSElementDeclaration *poParentElt,
1635 : std::vector<XSElementDeclaration *> &apoImplEltList)
1636 : {
1637 359997 : const auto oIter = m_oMapParentEltToChildElt.find(poParentElt);
1638 359997 : if (oIter != m_oMapParentEltToChildElt.end())
1639 : {
1640 29820 : for (size_t j = 0; j < oIter->second.size(); j++)
1641 : {
1642 25308 : XSElementDeclaration *poSubElt = oIter->second[j];
1643 25308 : if (IsEltCompatibleOfFC(poSubElt))
1644 : {
1645 15410 : if (!poSubElt->getAbstract())
1646 : {
1647 12891 : apoImplEltList.push_back(poSubElt);
1648 : }
1649 : }
1650 25308 : GetConcreteImplementationTypes(poSubElt, apoImplEltList);
1651 : }
1652 : }
1653 359997 : }
1654 :
1655 : /************************************************************************/
1656 : /* GetConstraintChildrenElements() */
1657 : /************************************************************************/
1658 :
1659 : std::vector<XSElementDeclaration *>
1660 334685 : GMLASSchemaAnalyzer::GetConstraintChildrenElements(const CPLString &osFullXPath)
1661 : {
1662 :
1663 334685 : std::vector<XSElementDeclaration *> oVectorRes;
1664 669370 : CPLString osMatched;
1665 334685 : if (m_oChildrenElementsConstraintsXPathMatcher.MatchesRefXPath(osFullXPath,
1666 : osMatched))
1667 : {
1668 : const std::vector<CPLString> &oVector =
1669 6 : m_oMapChildrenElementsConstraints[osMatched];
1670 : const std::map<CPLString, CPLString> &oMapPrefixToURI =
1671 6 : m_oChildrenElementsConstraintsXPathMatcher.GetMapPrefixToURI();
1672 14 : for (size_t j = 0; j < oVector.size(); ++j)
1673 : {
1674 8 : const CPLString &osSubElt(oVector[j]);
1675 16 : CPLString osSubEltPrefix;
1676 16 : CPLString osSubEltURI;
1677 16 : CPLString osSubEltType(osSubElt);
1678 8 : size_t nPos = osSubElt.find(":");
1679 8 : if (nPos != std::string::npos)
1680 : {
1681 8 : osSubEltPrefix = osSubElt.substr(0, nPos);
1682 8 : osSubEltType = osSubElt.substr(nPos + 1);
1683 :
1684 8 : const auto oIter2 = oMapPrefixToURI.find(osSubEltPrefix);
1685 8 : if (oIter2 != oMapPrefixToURI.end())
1686 : {
1687 8 : osSubEltURI = oIter2->second;
1688 : }
1689 : else
1690 : {
1691 0 : CPLError(CE_Warning, CPLE_AppDefined,
1692 : "Cannot find prefix of type constraint %s",
1693 : osSubElt.c_str());
1694 : }
1695 : }
1696 :
1697 16 : const CPLString osSubEltXPath(MakeXPath(osSubEltURI, osSubEltType));
1698 8 : const auto oIter2 = m_oMapXPathToEltDecl.find(osSubEltXPath);
1699 8 : if (oIter2 != m_oMapXPathToEltDecl.end())
1700 : {
1701 8 : XSElementDeclaration *poSubElt = oIter2->second;
1702 8 : if (IsEltCompatibleOfFC(poSubElt))
1703 : {
1704 8 : oVectorRes.push_back(poSubElt);
1705 : }
1706 : }
1707 : else
1708 : {
1709 0 : CPLError(
1710 : CE_Warning, CPLE_AppDefined,
1711 : "Cannot find element declaration of type constraint %s",
1712 : osSubElt.c_str());
1713 : }
1714 : }
1715 : }
1716 669370 : return oVectorRes;
1717 : }
1718 :
1719 : /************************************************************************/
1720 : /* GetOGRGeometryType() */
1721 : /************************************************************************/
1722 :
1723 3561 : static OGRwkbGeometryType GetOGRGeometryType(XSTypeDefinition *poTypeDef)
1724 : {
1725 : const struct MyStruct
1726 : {
1727 : const char *pszName;
1728 : OGRwkbGeometryType eType;
1729 3561 : } asArray[] = {{"GeometryPropertyType", wkbUnknown},
1730 : {"PointPropertyType", wkbPoint},
1731 : {"BoundingShapeType", wkbPolygon},
1732 : {"PolygonPropertyType", wkbPolygon},
1733 : {"LineStringPropertyType", wkbLineString},
1734 : {"MultiPointPropertyType", wkbMultiPoint},
1735 : {"MultiPolygonPropertyType", wkbMultiPolygon},
1736 : {"MultiLineStringPropertyType", wkbMultiLineString},
1737 : {"MultiGeometryPropertyType", wkbGeometryCollection},
1738 : {"MultiCurvePropertyType", wkbMultiCurve},
1739 : {"MultiSurfacePropertyType", wkbMultiSurface},
1740 : {"MultiSolidPropertyType", wkbUnknown},
1741 : // GeometryArrayPropertyType ?
1742 : {"GeometricPrimitivePropertyType", wkbUnknown},
1743 : {"CurvePropertyType", wkbCurve},
1744 : {"SurfacePropertyType", wkbSurface},
1745 : // SurfaceArrayPropertyType ?
1746 : // AbstractRingPropertyType ?
1747 : // LinearRingPropertyType ?
1748 : {"CompositeCurvePropertyType", wkbCurve},
1749 : {"CompositeSurfacePropertyType", wkbSurface},
1750 : {"CompositeSolidPropertyType", wkbUnknown},
1751 : {"GeometricComplexPropertyType", wkbUnknown},
1752 : {"SolidPropertyType", wkbPolyhedralSurface}};
1753 :
1754 7122 : CPLString osName(transcode(poTypeDef->getName()));
1755 58923 : for (size_t i = 0; i < CPL_ARRAYSIZE(asArray); ++i)
1756 : {
1757 56667 : if (osName == asArray[i].pszName)
1758 1305 : return asArray[i].eType;
1759 : }
1760 2256 : return wkbNone;
1761 :
1762 : #if 0
1763 : <complexType name="CurveSegmentArrayPropertyType">
1764 : <complexType name="KnotPropertyType">
1765 : <complexType name="SurfacePatchArrayPropertyType">
1766 : <complexType name="RingPropertyType">
1767 : <complexType name="PolygonPatchArrayPropertyType">
1768 : <complexType name="TrianglePatchArrayPropertyType">
1769 : <complexType name="LineStringSegmentArrayPropertyType">
1770 : <complexType name="SolidArrayPropertyType">
1771 : #endif
1772 : }
1773 :
1774 : /************************************************************************/
1775 : /* GetOGRGeometryTypeFromGMLEltName() */
1776 : /************************************************************************/
1777 :
1778 : static OGRwkbGeometryType
1779 2010 : GetOGRGeometryTypeFromGMLEltName(const CPLString &osEltName)
1780 : {
1781 : const struct MyStruct
1782 : {
1783 : const char *pszName;
1784 : OGRwkbGeometryType eType;
1785 2010 : } asArray[] = {
1786 : {"Point", wkbPoint},
1787 : {"Polygon", wkbPolygon},
1788 : {"Envelope", wkbPolygon},
1789 : {"LineString", wkbLineString},
1790 : {"MultiPoint", wkbMultiPoint},
1791 : {"MultiPolygon", wkbMultiPolygon},
1792 : {"MultiLineString", wkbMultiLineString},
1793 : {"MultiGeometry", wkbGeometryCollection},
1794 : {"MultiCurve", wkbMultiCurve},
1795 : {"MultiSurface", wkbMultiSurface},
1796 : {"MultiSolid", wkbUnknown},
1797 : {"Curve", wkbCurve},
1798 : {"Surface", wkbSurface},
1799 : {"CompositeCurve", wkbCurve},
1800 : {"CompositeSurface", wkbSurface},
1801 : {"CompositeSolid", wkbUnknown},
1802 : {"GeometricComplex", wkbUnknown},
1803 : };
1804 :
1805 35324 : for (size_t i = 0; i < CPL_ARRAYSIZE(asArray); ++i)
1806 : {
1807 33368 : if (osEltName == asArray[i].pszName)
1808 54 : return asArray[i].eType;
1809 : }
1810 1956 : return wkbNone;
1811 : }
1812 :
1813 : /************************************************************************/
1814 : /* CreateNonNestedRelationship() */
1815 : /************************************************************************/
1816 :
1817 17155 : void GMLASSchemaAnalyzer::CreateNonNestedRelationship(
1818 : XSElementDeclaration *poElt,
1819 : std::vector<XSElementDeclaration *> &apoImplEltList,
1820 : GMLASFeatureClass &oClass, int nMaxOccurs, bool bEltNameWillNeedPrefix,
1821 : bool bForceJunctionTable, bool bCaseOfConstraintChildren)
1822 : {
1823 34310 : const CPLString osEltPrefix(GetPrefix(transcode(poElt->getNamespace())));
1824 34310 : const CPLString osEltName(transcode(poElt->getName()));
1825 : const CPLString osOnlyElementXPath(
1826 34310 : MakeXPath(transcode(poElt->getNamespace()), osEltName));
1827 34310 : const CPLString osElementXPath(oClass.GetXPath() + "/" +
1828 34310 : osOnlyElementXPath);
1829 :
1830 17155 : if (!poElt->getAbstract() && !bCaseOfConstraintChildren)
1831 : {
1832 16771 : apoImplEltList.insert(apoImplEltList.begin(), poElt);
1833 : }
1834 :
1835 34310 : std::set<CPLString> aoSetSubEltXPath;
1836 17155 : if (nMaxOccurs == 1 && !bForceJunctionTable)
1837 : {
1838 : // If the field isn't repeated, then we can link to each
1839 : // potential realization types with a field
1840 :
1841 21990 : for (size_t j = 0; j < apoImplEltList.size(); j++)
1842 : {
1843 14578 : XSElementDeclaration *poSubElt = apoImplEltList[j];
1844 14578 : const CPLString osSubEltName(transcode(poSubElt->getName()));
1845 : const CPLString osSubEltXPath(
1846 14578 : MakeXPath(transcode(poSubElt->getNamespace()), osSubEltName));
1847 :
1848 : // For AbstractFeature_SpatialDataSet_pkid in SpatialDataSet_member
1849 14578 : if (aoSetSubEltXPath.find(osSubEltXPath) != aoSetSubEltXPath.end())
1850 : {
1851 0 : continue;
1852 : }
1853 14578 : aoSetSubEltXPath.insert(osSubEltXPath);
1854 :
1855 29156 : const CPLString osRealFullXPath(oClass.GetXPath() + "/" +
1856 : ((bCaseOfConstraintChildren)
1857 29153 : ? osOnlyElementXPath + "/"
1858 58309 : : CPLString("")) +
1859 14578 : osSubEltXPath);
1860 :
1861 14578 : if (IsIgnoredXPath(osRealFullXPath))
1862 : {
1863 : #ifdef DEBUG_VERBOSE
1864 : CPLDebug("GMLAS", "%s is in ignored xpaths",
1865 : osRealFullXPath.c_str());
1866 : #endif
1867 92 : continue;
1868 : }
1869 :
1870 28972 : GMLASField oField;
1871 14486 : if (apoImplEltList.size() > 1 || bCaseOfConstraintChildren)
1872 : {
1873 7438 : if (m_oMapEltNamesToInstanceCount[osSubEltName] > 1)
1874 : {
1875 3098 : CPLString osLaunderedXPath(osSubEltXPath);
1876 3098 : osLaunderedXPath.replaceAll(':', '_');
1877 9294 : oField.SetName(((bEltNameWillNeedPrefix) ? osEltPrefix + "_"
1878 9294 : : CPLString()) +
1879 12392 : transcode(poElt->getName()) + "_" +
1880 6196 : osLaunderedXPath + "_pkid");
1881 : }
1882 : else
1883 : {
1884 13020 : oField.SetName(((bEltNameWillNeedPrefix) ? osEltPrefix + "_"
1885 13020 : : CPLString()) +
1886 17360 : transcode(poElt->getName()) + "_" +
1887 8680 : osSubEltName + "_pkid");
1888 : }
1889 : }
1890 : else
1891 : {
1892 7048 : oField.SetName(transcode(poElt->getName()) + "_pkid");
1893 : }
1894 14486 : oField.SetXPath(osRealFullXPath);
1895 14486 : oField.SetMinOccurs(0);
1896 14486 : oField.SetMaxOccurs(nMaxOccurs);
1897 14486 : oField.SetCategory(GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK);
1898 14486 : oField.SetRelatedClassXPath(osSubEltXPath);
1899 14486 : oField.SetType(GMLAS_FT_STRING, szXS_STRING);
1900 14486 : oClass.AddField(oField);
1901 7412 : }
1902 : }
1903 : else
1904 : {
1905 : // If the field is repeated, we need to use junction
1906 : // tables
1907 20170 : for (size_t j = 0; j < apoImplEltList.size(); j++)
1908 : {
1909 10427 : XSElementDeclaration *poSubElt = apoImplEltList[j];
1910 10427 : const CPLString osSubEltName(transcode(poSubElt->getName()));
1911 : const CPLString osSubEltXPath(
1912 10427 : MakeXPath(transcode(poSubElt->getNamespace()), osSubEltName));
1913 :
1914 : // For AbstractFeature_SpatialDataSet_pkid in SpatialDataSet_member
1915 10427 : if (aoSetSubEltXPath.find(osSubEltXPath) != aoSetSubEltXPath.end())
1916 : {
1917 0 : continue;
1918 : }
1919 10427 : aoSetSubEltXPath.insert(osSubEltXPath);
1920 :
1921 : // Instantiate a junction table
1922 20854 : GMLASFeatureClass oJunctionTable;
1923 :
1924 10427 : if (m_oMapEltNamesToInstanceCount[osSubEltName] > 1)
1925 : {
1926 80 : CPLString osLaunderedXPath(osSubEltXPath);
1927 80 : osLaunderedXPath.replaceAll(':', '_');
1928 240 : oJunctionTable.SetName(oClass.GetName() + "_" +
1929 320 : transcode(poElt->getName()) + "_" +
1930 : osLaunderedXPath);
1931 : }
1932 : else
1933 : {
1934 31041 : oJunctionTable.SetName(oClass.GetName() + "_" +
1935 41388 : transcode(poElt->getName()) + "_" +
1936 : osSubEltName);
1937 : }
1938 : // Create a fake XPath binding the parent xpath (to an abstract
1939 : // element) to the child element
1940 10427 : oJunctionTable.SetXPath(
1941 20854 : BuildJunctionTableXPath(osElementXPath, osSubEltXPath));
1942 10427 : oJunctionTable.SetParentXPath(oClass.GetXPath());
1943 10427 : oJunctionTable.SetChildXPath(osSubEltXPath);
1944 10427 : m_aoClasses.push_back(std::move(oJunctionTable));
1945 :
1946 : // Add an abstract field
1947 20854 : GMLASField oField;
1948 10427 : oField.SetName(
1949 20854 : ((bEltNameWillNeedPrefix) ? osEltPrefix + "_" : CPLString()) +
1950 20854 : osEltName + "_" + osSubEltName);
1951 31281 : oField.SetXPath(oClass.GetXPath() + "/" +
1952 : ((bCaseOfConstraintChildren)
1953 31280 : ? osOnlyElementXPath + "/"
1954 41707 : : CPLString("")) +
1955 : osSubEltXPath);
1956 10427 : oField.SetMinOccurs(0);
1957 10427 : oField.SetMaxOccurs(nMaxOccurs);
1958 10427 : oField.SetAbstractElementXPath(osElementXPath);
1959 10427 : oField.SetRelatedClassXPath(osSubEltXPath);
1960 10427 : oField.SetCategory(
1961 : GMLASField::PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE);
1962 10427 : oClass.AddField(oField);
1963 : }
1964 : }
1965 :
1966 : #if 0
1967 : GMLASField oField;
1968 : oField.SetName( transcode(poElt->getName()) );
1969 : oField.SetXPath( osElementXPath );
1970 : oField.SetMinOccurs( poParticle->getMinOccurs() );
1971 : oField.SetMaxOccurs( poParticle->getMaxOccursUnbounded() ?
1972 : MAXOCCURS_UNLIMITED : poParticle->getMaxOccurs() );
1973 :
1974 : for( size_t j = 0; j < apoImplEltList.size(); j++ )
1975 : {
1976 : XSElementDeclaration* poSubElt = apoImplEltList[j];
1977 : XSTypeDefinition* poSubEltType =
1978 : poSubElt->getTypeDefinition();
1979 : XSComplexTypeDefinition* poCT =
1980 : reinterpret_cast<XSComplexTypeDefinition*>(poSubEltType);
1981 :
1982 : GMLASFeatureClass oNestedClass;
1983 : oNestedClass.SetName( oClass.GetName() + "_" +
1984 : transcode(poSubElt->getName()) );
1985 : oNestedClass.SetXPath( oClass.GetXPath() + "/" +
1986 : MakeXPath(transcode(poSubElt->getNamespace()),
1987 : transcode(poSubElt->getName())) );
1988 :
1989 : std::set<XSModelGroup*>
1990 : oSetNewVisitedModelGroups(oSetVisitedModelGroups);
1991 : if( !ExploreModelGroup(
1992 : poCT->getParticle()->getModelGroupTerm(),
1993 : NULL,
1994 : oNestedClass,
1995 : nRecursionCounter + 1,
1996 : oSetNewVisitedModelGroups ) )
1997 : {
1998 : return false;
1999 : }
2000 :
2001 : oClass.AddNestedClass( oNestedClass );
2002 : }
2003 :
2004 : if( !apoImplEltList.empty() )
2005 : {
2006 : oField.SetAbstract(true);
2007 : }
2008 : else
2009 : {
2010 : oField.SetType( GMLAS_FT_ANYTYPE, "anyType" );
2011 : oField.SetXPath( oClass.GetXPath() + "/" + "*" );
2012 : oField.SetIncludeThisEltInBlob( true );
2013 : }
2014 : oClass.AddField( oField );
2015 : #endif
2016 17155 : }
2017 :
2018 : /************************************************************************/
2019 : /* IsIgnoredXPath() */
2020 : /************************************************************************/
2021 :
2022 400698 : bool GMLASSchemaAnalyzer::IsIgnoredXPath(const CPLString &osXPath)
2023 : {
2024 801396 : CPLString osIgnored;
2025 801396 : return m_oIgnoredXPathMatcher.MatchesRefXPath(osXPath, osIgnored);
2026 : }
2027 :
2028 : /************************************************************************/
2029 : /* FindElementsWithMustBeToLevel() */
2030 : /************************************************************************/
2031 :
2032 30080 : bool GMLASSchemaAnalyzer::FindElementsWithMustBeToLevel(
2033 : const CPLString &osParentXPath, XSModelGroup *poModelGroup,
2034 : int nRecursionCounter, std::set<XSElementDeclaration *> &oSetVisitedEltDecl,
2035 : std::set<XSModelGroup *> &oSetVisitedModelGroups,
2036 : std::vector<XSElementDeclaration *> &oVectorEltsForTopClass,
2037 : std::set<CPLString> &aoSetXPathEltsForTopClass, XSModel *poModel,
2038 : bool &bSimpleEnoughOut, int &nCountSubEltsOut)
2039 : {
2040 30080 : const bool bAlreadyVisitedMG = (oSetVisitedModelGroups.find(poModelGroup) !=
2041 30080 : oSetVisitedModelGroups.end());
2042 :
2043 30080 : oSetVisitedModelGroups.insert(poModelGroup);
2044 :
2045 30080 : if (nRecursionCounter == 100)
2046 : {
2047 : // Presumably an hostile schema
2048 0 : CPLError(CE_Failure, CPLE_AppDefined,
2049 : "Schema analysis failed due to too deeply nested model");
2050 0 : return false;
2051 : }
2052 :
2053 : {
2054 60160 : CPLString osIgnored;
2055 30080 : if (m_oDisabledFlattenedXPathMatcher.MatchesRefXPath(osParentXPath,
2056 : osIgnored))
2057 : {
2058 0 : bSimpleEnoughOut = false;
2059 : }
2060 : }
2061 :
2062 30080 : XSParticleList *poParticles = poModelGroup->getParticles();
2063 159729 : for (size_t i = 0; i < poParticles->size(); ++i)
2064 : {
2065 129649 : XSParticle *poParticle = poParticles->elementAt(i);
2066 :
2067 243559 : const bool bRepeatedParticle = poParticle->getMaxOccursUnbounded() ||
2068 113910 : poParticle->getMaxOccurs() > 1;
2069 :
2070 129649 : if (poParticle->getTermType() == XSParticle::TERM_ELEMENT)
2071 : {
2072 117450 : XSElementDeclaration *poElt = poParticle->getElementTerm();
2073 117450 : XSTypeDefinition *poTypeDef = poElt->getTypeDefinition();
2074 117450 : const CPLString osEltName(transcode(poElt->getName()));
2075 117450 : const CPLString osEltNS(transcode(poElt->getNamespace()));
2076 117450 : CPLString osXPath(MakeXPath(osEltNS, osEltName));
2077 234900 : const CPLString osFullXPath(osParentXPath + "/" + osXPath);
2078 :
2079 : #ifdef DEBUG_SUPER_VERBOSE
2080 : CPLDebug("GMLAS", "FindElementsWithMustBeToLevel: %s",
2081 : osFullXPath.c_str());
2082 : #endif
2083 :
2084 117450 : if (IsIgnoredXPath(osFullXPath))
2085 : {
2086 : #ifdef DEBUG_VERBOSE
2087 : CPLDebug("GMLAS", "%s is in ignored xpaths",
2088 : osFullXPath.c_str());
2089 : #endif
2090 97 : continue;
2091 : }
2092 :
2093 : // This could be refined to detect if the repeated element might not
2094 : // be simplifiable as an array
2095 117353 : if (bSimpleEnoughOut && bRepeatedParticle)
2096 : {
2097 : #ifdef DEBUG_VERBOSE
2098 : CPLDebug("GMLAS", "%s not inlinable because %s is repeated",
2099 : osParentXPath.c_str(), osXPath.c_str());
2100 : #endif
2101 5662 : bSimpleEnoughOut = false;
2102 : }
2103 :
2104 : // We don't want to inline
2105 : // sub-classes with hundereds of attributes
2106 117353 : nCountSubEltsOut++;
2107 :
2108 117353 : std::vector<XSElementDeclaration *> apoImplEltList;
2109 117353 : GetConcreteImplementationTypes(poElt, apoImplEltList);
2110 :
2111 : std::vector<XSElementDeclaration *> apoChildrenElements =
2112 117353 : GetConstraintChildrenElements(osFullXPath);
2113 :
2114 : // Special case for a GML geometry property
2115 118933 : if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
2116 1580 : GetOGRGeometryType(poTypeDef) != wkbNone)
2117 : {
2118 : // Do nothing
2119 : }
2120 117581 : else if (IsGMLNamespace(osEltNS) &&
2121 852 : GetOGRGeometryTypeFromGMLEltName(osEltName) != wkbNone)
2122 : {
2123 : // Do nothing
2124 : }
2125 : // Any GML abstract type
2126 117222 : else if (poElt->getAbstract() && IsGMLNamespace(osEltNS) &&
2127 98 : osEltName != "_Feature" &&
2128 117216 : osEltName != "AbstractFeature" &&
2129 42 : osEltName != "AbstractTimeObject")
2130 : {
2131 : // Do nothing
2132 : }
2133 : // Are there substitution groups for this element ?
2134 116663 : else if (!apoImplEltList.empty() || !apoChildrenElements.empty())
2135 : {
2136 349 : if (!apoChildrenElements.empty())
2137 : {
2138 3 : apoImplEltList = std::move(apoChildrenElements);
2139 : }
2140 346 : else if (!poElt->getAbstract())
2141 : {
2142 124 : apoImplEltList.insert(apoImplEltList.begin(), poElt);
2143 : }
2144 2387 : for (size_t j = 0; j < apoImplEltList.size(); j++)
2145 : {
2146 2038 : XSElementDeclaration *poSubElt = apoImplEltList[j];
2147 : const CPLString osSubEltXPath(
2148 4076 : MakeXPath(transcode(poSubElt->getNamespace()),
2149 4076 : transcode(poSubElt->getName())));
2150 :
2151 2038 : if (IsIgnoredXPath(osParentXPath + "/" + osSubEltXPath))
2152 : {
2153 : #ifdef DEBUG_VERBOSE
2154 : CPLDebug("GMLAS", "%s is in ignored xpaths",
2155 : (osParentXPath + "/" + osSubEltXPath).c_str());
2156 : #endif
2157 88 : continue;
2158 : }
2159 :
2160 : // Make sure we will instantiate the referenced element
2161 1950 : if (m_oSetEltsForTopClass.find(poSubElt) ==
2162 4068 : m_oSetEltsForTopClass.end() &&
2163 168 : aoSetXPathEltsForTopClass.find(osSubEltXPath) ==
2164 2118 : aoSetXPathEltsForTopClass.end())
2165 : {
2166 : #ifdef DEBUG_VERBOSE
2167 : CPLDebug(
2168 : "GMLAS",
2169 : "%s (%s) must be exposed as "
2170 : "top-level (%s of %s)",
2171 : osSubEltXPath.c_str(),
2172 : transcode(poSubElt->getTypeDefinition()->getName())
2173 : .c_str(),
2174 : apoChildrenElements.empty() ? "derived class"
2175 : : "child",
2176 : osParentXPath.c_str());
2177 : #endif
2178 :
2179 168 : oSetVisitedEltDecl.insert(poSubElt);
2180 168 : m_oSetEltsForTopClass.insert(poSubElt);
2181 168 : oVectorEltsForTopClass.push_back(poSubElt);
2182 168 : aoSetXPathEltsForTopClass.insert(osSubEltXPath);
2183 :
2184 : XSComplexTypeDefinition *poSubEltCT =
2185 168 : IsEltCompatibleOfFC(poSubElt);
2186 331 : if (!bAlreadyVisitedMG && poSubEltCT != nullptr &&
2187 163 : poSubEltCT->getParticle() != nullptr)
2188 : {
2189 163 : bool bSubSimpleEnoughOut = true;
2190 163 : int nSubCountSubElt = 0;
2191 163 : if (!FindElementsWithMustBeToLevel(
2192 : osSubEltXPath,
2193 : poSubEltCT->getParticle()
2194 : ->getModelGroupTerm(),
2195 : nRecursionCounter + 1, oSetVisitedEltDecl,
2196 : oSetVisitedModelGroups,
2197 : oVectorEltsForTopClass,
2198 : aoSetXPathEltsForTopClass, poModel,
2199 : bSubSimpleEnoughOut, nSubCountSubElt))
2200 : {
2201 0 : return false;
2202 : }
2203 : }
2204 : }
2205 : }
2206 : }
2207 :
2208 232421 : else if (!poElt->getAbstract() &&
2209 116107 : poTypeDef->getTypeCategory() ==
2210 : XSTypeDefinition::COMPLEX_TYPE)
2211 : {
2212 48630 : nCountSubEltsOut--;
2213 :
2214 48630 : XSComplexTypeDefinition *poEltCT = IsEltCompatibleOfFC(poElt);
2215 48630 : if (poEltCT)
2216 : {
2217 : // Might be a bit extreme, but for now we don't inline
2218 : // classes that have subclasses.
2219 36057 : if (bSimpleEnoughOut)
2220 : {
2221 : #ifdef DEBUG_VERBOSE
2222 : CPLDebug("GMLAS",
2223 : "%s not inlinable because %s field is complex",
2224 : osParentXPath.c_str(), osXPath.c_str());
2225 : #endif
2226 5972 : bSimpleEnoughOut = false;
2227 : }
2228 :
2229 36057 : if (oSetVisitedEltDecl.find(poElt) !=
2230 72114 : oSetVisitedEltDecl.end())
2231 : {
2232 16888 : if (m_oSetEltsForTopClass.find(poElt) ==
2233 22663 : m_oSetEltsForTopClass.end() &&
2234 5775 : m_oSetSimpleEnoughElts.find(poElt) ==
2235 22663 : m_oSetSimpleEnoughElts.end() &&
2236 3384 : aoSetXPathEltsForTopClass.find(osXPath) ==
2237 20272 : aoSetXPathEltsForTopClass.end())
2238 : {
2239 2498 : CPLString osIgnored;
2240 1249 : if (!m_oForcedFlattenedXPathMatcher.MatchesRefXPath(
2241 : osXPath, osIgnored))
2242 : {
2243 : #ifdef DEBUG_VERBOSE
2244 : CPLDebug("GMLAS",
2245 : "%s (%s) must be exposed as "
2246 : "top-level (multiple time referenced)",
2247 : osXPath.c_str(),
2248 : transcode(poTypeDef->getNamespace())
2249 : .c_str());
2250 : #endif
2251 1249 : m_oSetEltsForTopClass.insert(poElt);
2252 1249 : oVectorEltsForTopClass.push_back(poElt);
2253 : aoSetXPathEltsForTopClass.insert(
2254 1249 : std::move(osXPath));
2255 : }
2256 : }
2257 : }
2258 : else
2259 : {
2260 19169 : oSetVisitedEltDecl.insert(poElt);
2261 :
2262 38338 : if (!bAlreadyVisitedMG &&
2263 19169 : poEltCT->getParticle() != nullptr)
2264 : {
2265 19047 : int nSubCountSubElt = 0;
2266 :
2267 : // Process attributes
2268 : XSAttributeUseList *poAttrList =
2269 19047 : poEltCT->getAttributeUses();
2270 : const size_t nAttrListSize =
2271 19047 : (poAttrList != nullptr) ? poAttrList->size()
2272 19047 : : 0;
2273 21733 : for (size_t j = 0; j < nAttrListSize; ++j)
2274 : {
2275 : XSAttributeUse *poAttr =
2276 2686 : poAttrList->elementAt(j);
2277 2686 : GMLASField oField;
2278 2686 : if (!SetFieldFromAttribute(oField, poAttr,
2279 : osFullXPath))
2280 : {
2281 0 : return false;
2282 : }
2283 4730 : if (!IsIgnoredXPath(oField.GetXPath()) &&
2284 2044 : oField.GetFixedValue().empty())
2285 : {
2286 : #ifdef DEBUG_SUPER_VERBOSE
2287 : CPLDebug(
2288 : "GMLAS",
2289 : "FindElementsWithMustBeToLevel: %s",
2290 : oField.GetXPath().c_str());
2291 : #endif
2292 1920 : nSubCountSubElt++;
2293 : }
2294 : }
2295 :
2296 19047 : bool bSubSimpleEnoughOut = true;
2297 19047 : if (!FindElementsWithMustBeToLevel(
2298 : osFullXPath,
2299 : poEltCT->getParticle()->getModelGroupTerm(),
2300 : nRecursionCounter + 1, oSetVisitedEltDecl,
2301 : oSetVisitedModelGroups,
2302 : oVectorEltsForTopClass,
2303 : aoSetXPathEltsForTopClass, poModel,
2304 : bSubSimpleEnoughOut, nSubCountSubElt))
2305 : {
2306 0 : return false;
2307 : }
2308 19047 : if (bSubSimpleEnoughOut)
2309 : {
2310 : #ifdef DEBUG_VERBOSE
2311 : CPLDebug("GMLAS", "%s is inlinable: %d fields",
2312 : osXPath.c_str(), nSubCountSubElt);
2313 : #endif
2314 7552 : m_oSetSimpleEnoughElts.insert(poElt);
2315 :
2316 7552 : nCountSubEltsOut += nSubCountSubElt;
2317 : }
2318 11495 : else if (bSimpleEnoughOut)
2319 : {
2320 : #ifdef DEBUG_VERBOSE
2321 : CPLDebug("GMLAS",
2322 : "%s not inlinable because %s is not "
2323 : "inlinable",
2324 : osParentXPath.c_str(),
2325 : osXPath.c_str());
2326 : #endif
2327 0 : bSimpleEnoughOut = false;
2328 : }
2329 : }
2330 : }
2331 : }
2332 : else
2333 : {
2334 12573 : if (transcode(poElt->getName()) != szFEATURE_COLLECTION)
2335 : {
2336 12573 : poEltCT = reinterpret_cast<XSComplexTypeDefinition *>(
2337 : poTypeDef);
2338 : // Process attributes
2339 : XSAttributeUseList *poAttrList =
2340 12573 : poEltCT->getAttributeUses();
2341 : const size_t nAttrListSize =
2342 12573 : (poAttrList != nullptr) ? poAttrList->size() : 0;
2343 24034 : for (size_t j = 0;
2344 24034 : bSimpleEnoughOut && j < nAttrListSize; ++j)
2345 : {
2346 11461 : XSAttributeUse *poAttr = poAttrList->elementAt(j);
2347 11461 : GMLASField oField;
2348 11461 : if (!SetFieldFromAttribute(oField, poAttr,
2349 : osFullXPath))
2350 : {
2351 0 : return false;
2352 : }
2353 22892 : if (!IsIgnoredXPath(oField.GetXPath()) &&
2354 11431 : oField.GetFixedValue().empty())
2355 : {
2356 : #ifdef DEBUG_SUPER_VERBOSE
2357 : CPLDebug("GMLAS",
2358 : "FindElementsWithMustBeToLevel: %s",
2359 : oField.GetXPath().c_str());
2360 : #endif
2361 11424 : nCountSubEltsOut++;
2362 : }
2363 : }
2364 : }
2365 : }
2366 :
2367 48630 : CPLString osTargetElement;
2368 48630 : if (poElt->getAnnotation() != nullptr)
2369 : {
2370 : CPLString osAnnot(transcode(
2371 1974 : poElt->getAnnotation()->getAnnotationString()));
2372 :
2373 : #ifdef DEBUG_SUPER_VERBOSE
2374 : CPLDebug("GMLAS", "Annot: %s", osAnnot.c_str());
2375 : #endif
2376 987 : CPLXMLNode *psRoot = CPLParseXMLString(osAnnot);
2377 987 : CPLStripXMLNamespace(psRoot, nullptr, TRUE);
2378 : osTargetElement = CPLGetXMLValue(
2379 987 : psRoot, "=annotation.appinfo.targetElement", "");
2380 987 : CPLDestroyXMLNode(psRoot);
2381 : #ifdef DEBUG_VERBOSE
2382 : if (!osTargetElement.empty())
2383 : CPLDebug("GMLAS", "targetElement: %s",
2384 : osTargetElement.c_str());
2385 : #endif
2386 : }
2387 :
2388 : // If we have a element of type gml:ReferenceType that has
2389 : // a targetElement in its annotation.appinfo, then create
2390 : // a dedicated field to have cross-layer relationships.
2391 97995 : if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
2392 97995 : transcode(poTypeDef->getName()) == "ReferenceType" &&
2393 43 : !osTargetElement.empty())
2394 : {
2395 : XSElementDeclaration *poTargetElt =
2396 10 : GetTopElementDeclarationFromXPath(osTargetElement,
2397 10 : poModel);
2398 : // TODO: even for non abstract we should probably
2399 : // handle substitutions
2400 10 : if (poTargetElt != nullptr && !poTargetElt->getAbstract())
2401 : {
2402 : const CPLString osTargetEltXPath(
2403 8 : MakeXPath(transcode(poTargetElt->getNamespace()),
2404 8 : transcode(poTargetElt->getName())));
2405 :
2406 4 : if (IsIgnoredXPath(osTargetEltXPath))
2407 : {
2408 : #ifdef DEBUG_VERBOSE
2409 : CPLDebug("GMLAS", "%s is in ignored xpaths",
2410 : osTargetEltXPath.c_str());
2411 : #endif
2412 0 : continue;
2413 : }
2414 :
2415 : // Make sure we will instantiate the referenced
2416 : // element
2417 4 : if (m_oSetEltsForTopClass.find(poTargetElt) ==
2418 12 : m_oSetEltsForTopClass.end() &&
2419 4 : aoSetXPathEltsForTopClass.find(osTargetEltXPath) ==
2420 8 : aoSetXPathEltsForTopClass.end())
2421 : {
2422 : #ifdef DEBUG_VERBOSE
2423 : CPLDebug(
2424 : "GMLAS", "%d: Adding %s as (%s) needed type",
2425 : __LINE__, osTargetElement.c_str(),
2426 : transcode(
2427 : poTargetElt->getTypeDefinition()->getName())
2428 : .c_str());
2429 : #endif
2430 4 : oSetVisitedEltDecl.insert(poTargetElt);
2431 4 : m_oSetEltsForTopClass.insert(poTargetElt);
2432 4 : oVectorEltsForTopClass.push_back(poTargetElt);
2433 4 : aoSetXPathEltsForTopClass.insert(osTargetEltXPath);
2434 : }
2435 :
2436 : XSComplexTypeDefinition *poTargetEltCT =
2437 4 : IsEltCompatibleOfFC(poTargetElt);
2438 8 : if (!bAlreadyVisitedMG && poTargetEltCT &&
2439 4 : poTargetEltCT->getParticle() != nullptr)
2440 : {
2441 4 : bool bSubSimpleEnoughOut = true;
2442 4 : int nSubCountSubElt = 0;
2443 4 : if (!FindElementsWithMustBeToLevel(
2444 : osTargetEltXPath,
2445 : poTargetEltCT->getParticle()
2446 : ->getModelGroupTerm(),
2447 : nRecursionCounter + 1, oSetVisitedEltDecl,
2448 : oSetVisitedModelGroups,
2449 : oVectorEltsForTopClass,
2450 : aoSetXPathEltsForTopClass, poModel,
2451 : bSubSimpleEnoughOut, nSubCountSubElt))
2452 : {
2453 0 : return false;
2454 : }
2455 : }
2456 : }
2457 : }
2458 : }
2459 : }
2460 21229 : else if (!bAlreadyVisitedMG &&
2461 9030 : poParticle->getTermType() == XSParticle::TERM_MODELGROUP)
2462 : {
2463 : // This could be refined to detect if the repeated element might not
2464 : // be simplifiable as an array
2465 8814 : if (bSimpleEnoughOut && bRepeatedParticle)
2466 : {
2467 : #ifdef DEBUG_VERBOSE
2468 : CPLDebug(
2469 : "GMLAS",
2470 : "%s not inlinable because there is a repeated particle",
2471 : osParentXPath.c_str());
2472 : #endif
2473 460 : bSimpleEnoughOut = false;
2474 : }
2475 :
2476 8814 : XSModelGroup *psSubModelGroup = poParticle->getModelGroupTerm();
2477 8814 : if (!FindElementsWithMustBeToLevel(
2478 : osParentXPath, psSubModelGroup, nRecursionCounter + 1,
2479 : oSetVisitedEltDecl, oSetVisitedModelGroups,
2480 : oVectorEltsForTopClass, aoSetXPathEltsForTopClass, poModel,
2481 : bSimpleEnoughOut, nCountSubEltsOut))
2482 : {
2483 0 : return false;
2484 : }
2485 : }
2486 : else
2487 : {
2488 : // This could be refined to detect if the repeated element might not
2489 : // be simplifiable as an array
2490 3385 : if (bSimpleEnoughOut && bRepeatedParticle)
2491 : {
2492 : #ifdef DEBUG_VERBOSE
2493 : CPLDebug(
2494 : "GMLAS",
2495 : "%s not inlinable because there is a repeated particle",
2496 : osParentXPath.c_str());
2497 : #endif
2498 424 : bSimpleEnoughOut = false;
2499 : }
2500 : }
2501 : }
2502 :
2503 30080 : if (bSimpleEnoughOut && nCountSubEltsOut > m_nMaximumFieldsForFlattening)
2504 : {
2505 1694 : CPLString osIgnored;
2506 847 : if (!m_oForcedFlattenedXPathMatcher.MatchesRefXPath(osParentXPath,
2507 : osIgnored))
2508 : {
2509 : #ifdef DEBUG_VERBOSE
2510 : CPLDebug("GMLAS",
2511 : "%s not inlinable because it has more than %d fields",
2512 : osParentXPath.c_str(), m_nMaximumFieldsForFlattening);
2513 : #endif
2514 847 : bSimpleEnoughOut = false;
2515 : }
2516 : }
2517 :
2518 30080 : return true;
2519 : }
2520 :
2521 : /************************************************************************/
2522 : /* IsGMLNamespace() */
2523 : /************************************************************************/
2524 :
2525 753935 : bool GMLASSchemaAnalyzer::IsGMLNamespace(const CPLString &osURI)
2526 : {
2527 753935 : if (osURI.find(szGML_URI) == 0)
2528 8210 : return true;
2529 : // Below is mostly for unit tests were we use xmlns:gml="http://fake_gml"
2530 745725 : const auto oIter = m_oMapURIToPrefix.find(osURI);
2531 745725 : return oIter != m_oMapURIToPrefix.end() && oIter->second == szGML_PREFIX;
2532 : }
2533 :
2534 : /************************************************************************/
2535 : /* BuildMapCountOccurrencesOfSameName() */
2536 : /************************************************************************/
2537 :
2538 50426 : void GMLASSchemaAnalyzer::BuildMapCountOccurrencesOfSameName(
2539 : XSModelGroup *poModelGroup,
2540 : std::map<CPLString, int> &oMapCountOccurrencesOfSameName)
2541 : {
2542 50426 : XSParticleList *poParticles = poModelGroup->getParticles();
2543 286892 : for (size_t i = 0; i < poParticles->size(); ++i)
2544 : {
2545 236466 : XSParticle *poParticle = poParticles->elementAt(i);
2546 236466 : if (poParticle->getTermType() == XSParticle::TERM_ELEMENT)
2547 : {
2548 217735 : XSElementDeclaration *poElt = poParticle->getElementTerm();
2549 217735 : const CPLString osEltName(transcode(poElt->getName()));
2550 217735 : oMapCountOccurrencesOfSameName[osEltName]++;
2551 : }
2552 18731 : else if (poParticle->getTermType() == XSParticle::TERM_MODELGROUP)
2553 : {
2554 18538 : XSModelGroup *psSubModelGroup = poParticle->getModelGroupTerm();
2555 18538 : BuildMapCountOccurrencesOfSameName(psSubModelGroup,
2556 : oMapCountOccurrencesOfSameName);
2557 : }
2558 : }
2559 50426 : }
2560 :
2561 : /************************************************************************/
2562 : /* ComposeMinOccurs() */
2563 : /************************************************************************/
2564 :
2565 4578 : static int ComposeMinOccurs(int nVal1, int nVal2)
2566 : {
2567 4578 : return nVal1 * nVal2;
2568 : }
2569 :
2570 : /************************************************************************/
2571 : /* ComposeMaxOccurs() */
2572 : /************************************************************************/
2573 :
2574 4578 : static int ComposeMaxOccurs(int nVal1, int nVal2)
2575 : {
2576 4578 : if (nVal1 == MAXOCCURS_UNLIMITED || nVal2 == MAXOCCURS_UNLIMITED)
2577 3860 : return MAXOCCURS_UNLIMITED;
2578 718 : return nVal1 * nVal2;
2579 : }
2580 :
2581 : /************************************************************************/
2582 : /* ExploreModelGroup() */
2583 : /************************************************************************/
2584 :
2585 50429 : bool GMLASSchemaAnalyzer::ExploreModelGroup(
2586 : XSModelGroup *poModelGroup, XSAttributeUseList *poMainAttrList,
2587 : GMLASFeatureClass &oClass, int nRecursionCounter,
2588 : std::set<XSModelGroup *> &oSetVisitedModelGroups, XSModel *poModel,
2589 : const std::map<CPLString, int> &oMapCountOccurrencesOfSameName)
2590 : {
2591 50429 : if (oSetVisitedModelGroups.find(poModelGroup) !=
2592 100858 : oSetVisitedModelGroups.end())
2593 : {
2594 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s already visited",
2595 0 : oClass.GetXPath().c_str());
2596 0 : return false;
2597 : }
2598 50429 : oSetVisitedModelGroups.insert(poModelGroup);
2599 :
2600 50429 : if (nRecursionCounter == 100)
2601 : {
2602 : // Presumably an hostile schema
2603 0 : CPLError(CE_Failure, CPLE_AppDefined,
2604 : "Schema analysis failed due to too deeply nested model");
2605 0 : return false;
2606 : }
2607 :
2608 50429 : if (poMainAttrList != nullptr)
2609 : {
2610 387 : const size_t nMainAttrListSize = poMainAttrList->size();
2611 1772 : for (size_t j = 0; j < nMainAttrListSize; ++j)
2612 : {
2613 1386 : GMLASField oField;
2614 1386 : XSAttributeUse *poAttr = poMainAttrList->elementAt(j);
2615 1386 : if (!SetFieldFromAttribute(oField, poAttr, oClass.GetXPath()))
2616 : {
2617 1 : return false;
2618 : }
2619 :
2620 1385 : if (IsIgnoredXPath(oField.GetXPath()))
2621 : {
2622 : #ifdef DEBUG_VERBOSE
2623 : CPLDebug("GMLAS", "%s is in ignored xpaths",
2624 : oField.GetXPath().c_str());
2625 : #endif
2626 430 : if (!oField.GetFixedValue().empty() ||
2627 195 : !oField.GetDefaultValue().empty())
2628 : {
2629 42 : oField.SetIgnored();
2630 : }
2631 : else
2632 : {
2633 193 : continue;
2634 : }
2635 : }
2636 :
2637 1192 : oClass.AddField(oField);
2638 : }
2639 : }
2640 :
2641 50428 : XSParticleList *poParticles = poModelGroup->getParticles();
2642 :
2643 : // Special case for GML 3.1.1 where gml:metaDataProperty should be
2644 : // a sequence of gml:_Metadata but for some reason they have used
2645 : // a sequence of any.
2646 50428 : if (oClass.GetXPath() == "gml:metaDataProperty" &&
2647 0 : poModelGroup->getCompositor() == XSModelGroup::COMPOSITOR_SEQUENCE &&
2648 50428 : poParticles->size() == 1 &&
2649 0 : poParticles->elementAt(0)->getTermType() == XSParticle::TERM_WILDCARD)
2650 : {
2651 : XSElementDeclaration *poGMLMetadata =
2652 0 : GetTopElementDeclarationFromXPath("gml:_MetaData", poModel);
2653 0 : if (poGMLMetadata != nullptr)
2654 : {
2655 0 : std::vector<XSElementDeclaration *> apoImplEltList;
2656 0 : GetConcreteImplementationTypes(poGMLMetadata, apoImplEltList);
2657 0 : CreateNonNestedRelationship(poGMLMetadata, apoImplEltList, oClass,
2658 : 1,
2659 : false, // doesn't need prefix
2660 : true, // force junction table
2661 : false // regular case
2662 : );
2663 :
2664 0 : return true;
2665 : }
2666 : }
2667 :
2668 : const bool bIsChoice =
2669 50428 : (poModelGroup->getCompositor() == XSModelGroup::COMPOSITOR_CHOICE);
2670 50428 : int nGroup = 0;
2671 :
2672 284989 : for (size_t i = 0; i < poParticles->size(); ++i)
2673 : {
2674 236094 : XSParticle *poParticle = poParticles->elementAt(i);
2675 455221 : const bool bRepeatedParticle = poParticle->getMaxOccursUnbounded() ||
2676 219127 : poParticle->getMaxOccurs() > 1;
2677 236094 : const int nMinOccurs = static_cast<int>(poParticle->getMinOccurs());
2678 : const int nMaxOccurs =
2679 236094 : poParticle->getMaxOccursUnbounded()
2680 455221 : ? MAXOCCURS_UNLIMITED
2681 219127 : : static_cast<int>(poParticle->getMaxOccurs());
2682 :
2683 236094 : if (poParticle->getTermType() == XSParticle::TERM_ELEMENT)
2684 : {
2685 217433 : XSElementDeclaration *poElt = poParticle->getElementTerm();
2686 217433 : const CPLString osEltName(transcode(poElt->getName()));
2687 :
2688 217433 : const auto oIter = oMapCountOccurrencesOfSameName.find(osEltName);
2689 : const bool bEltNameWillNeedPrefix =
2690 434866 : oIter != oMapCountOccurrencesOfSameName.end() &&
2691 217433 : oIter->second > 1;
2692 217433 : const CPLString osEltNS(transcode(poElt->getNamespace()));
2693 : const CPLString osPrefixedEltName((bEltNameWillNeedPrefix
2694 434822 : ? GetPrefix(osEltNS) + "_"
2695 869644 : : CPLString()) +
2696 217433 : osEltName);
2697 217433 : const CPLString osOnlyElementXPath(MakeXPath(osEltNS, osEltName));
2698 434866 : const CPLString osElementXPath(oClass.GetXPath() + "/" +
2699 217433 : osOnlyElementXPath);
2700 : #ifdef DEBUG_VERBOSE
2701 : CPLDebug("GMLAS", "Iterating through %s", osElementXPath.c_str());
2702 : #endif
2703 :
2704 217433 : if (IsIgnoredXPath(osElementXPath))
2705 : {
2706 : #ifdef DEBUG_VERBOSE
2707 : CPLDebug("GMLAS", "%s is in ignored xpaths",
2708 : osElementXPath.c_str());
2709 : #endif
2710 101 : continue;
2711 : }
2712 :
2713 217332 : CPLString osTargetElement;
2714 217332 : if (poElt->getAnnotation() != nullptr)
2715 : {
2716 : CPLString osAnnot(
2717 2858 : transcode(poElt->getAnnotation()->getAnnotationString()));
2718 :
2719 : #ifdef DEBUG_SUPER_VERBOSE
2720 : CPLDebug("GMLAS", "Annot: %s", osAnnot.c_str());
2721 : #endif
2722 1429 : CPLXMLNode *psRoot = CPLParseXMLString(osAnnot);
2723 1429 : CPLStripXMLNamespace(psRoot, nullptr, TRUE);
2724 : osTargetElement = CPLGetXMLValue(
2725 1429 : psRoot, "=annotation.appinfo.targetElement", "");
2726 1429 : CPLDestroyXMLNode(psRoot);
2727 : #ifdef DEBUG_VERBOSE
2728 : if (!osTargetElement.empty())
2729 : CPLDebug("GMLAS", "targetElement: %s",
2730 : osTargetElement.c_str());
2731 : #endif
2732 : }
2733 :
2734 217332 : XSTypeDefinition *poTypeDef = poElt->getTypeDefinition();
2735 :
2736 217332 : std::vector<XSElementDeclaration *> apoImplEltList;
2737 217332 : GetConcreteImplementationTypes(poElt, apoImplEltList);
2738 :
2739 : std::vector<XSElementDeclaration *> apoChildrenElements =
2740 217332 : GetConstraintChildrenElements(osElementXPath);
2741 :
2742 : // Special case for a GML geometry property
2743 217332 : OGRwkbGeometryType eGeomType =
2744 : wkbNone; // to make Visual Studio happy
2745 217332 : CPL_IGNORE_RET_VAL(eGeomType); // to make cppcheck happy
2746 :
2747 217332 : if (!apoChildrenElements.empty())
2748 : {
2749 3 : CreateNonNestedRelationship(
2750 : poElt, apoChildrenElements, oClass, nMaxOccurs,
2751 : bEltNameWillNeedPrefix,
2752 : false, // do not force junction table
2753 : true // special case for children elements
2754 : );
2755 : }
2756 :
2757 219239 : else if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
2758 1910 : (eGeomType = GetOGRGeometryType(poTypeDef)) != wkbNone)
2759 : {
2760 1360 : GMLASField oField;
2761 680 : oField.SetName(osPrefixedEltName);
2762 680 : oField.SetMinOccurs(nMinOccurs);
2763 680 : oField.SetMaxOccurs(nMaxOccurs);
2764 680 : oField.SetType(GMLAS_FT_GEOMETRY, szFAKEXS_GEOMETRY);
2765 680 : if (nMaxOccurs > 1 || nMaxOccurs == MAXOCCURS_UNLIMITED)
2766 : {
2767 : // Repeated geometry property can happen in some schemas
2768 : // like
2769 : // inspire.ec.europa.eu/schemas/ge_gp/4.0/GeophysicsCore.xsd
2770 : // or
2771 : // http://ngwd-bdnes.cits.nrcan.gc.ca/service/gwml/schemas/2.1/gwml2-flow.xsd
2772 26 : oField.SetGeomType(wkbUnknown);
2773 26 : oField.SetArray(true);
2774 : }
2775 : else
2776 654 : oField.SetGeomType(eGeomType);
2777 680 : oField.SetXPath(osElementXPath);
2778 680 : oField.SetDocumentation(GetAnnotationDoc(poElt));
2779 :
2780 680 : oClass.AddField(oField);
2781 : }
2782 :
2783 217807 : else if (IsGMLNamespace(osEltNS) &&
2784 1158 : (eGeomType = GetOGRGeometryTypeFromGMLEltName(
2785 : osEltName)) != wkbNone)
2786 : {
2787 54 : GMLASField oField;
2788 27 : oField.SetName(osPrefixedEltName);
2789 27 : oField.SetMinOccurs(nMinOccurs);
2790 27 : oField.SetMaxOccurs(nMaxOccurs);
2791 :
2792 27 : oField.SetType(GMLAS_FT_GEOMETRY, szFAKEXS_GEOMETRY);
2793 27 : oField.SetGeomType(eGeomType);
2794 27 : oField.SetArray(nMaxOccurs > 1 ||
2795 : nMaxOccurs == MAXOCCURS_UNLIMITED);
2796 :
2797 27 : oField.SetXPath(osElementXPath);
2798 27 : oField.SetIncludeThisEltInBlob(true);
2799 27 : oField.SetDocumentation(GetAnnotationDoc(poElt));
2800 :
2801 27 : oClass.AddField(oField);
2802 : }
2803 :
2804 : // Any GML abstract type
2805 217471 : else if (poElt->getAbstract() && IsGMLNamespace(osEltNS) &&
2806 275 : osEltName != "_Feature" &&
2807 217462 : osEltName != "AbstractFeature" &&
2808 126 : osEltName != "AbstractTimeObject")
2809 : {
2810 214 : GMLASField oField;
2811 107 : oField.SetName(osPrefixedEltName);
2812 107 : oField.SetMinOccurs(nMinOccurs);
2813 107 : oField.SetMaxOccurs(nMaxOccurs);
2814 107 : if (osEltName == "AbstractGeometry")
2815 : {
2816 32 : oField.SetType(GMLAS_FT_GEOMETRY, szFAKEXS_GEOMETRY);
2817 32 : oField.SetGeomType(wkbUnknown);
2818 32 : oField.SetArray(nMaxOccurs > 1 ||
2819 : nMaxOccurs == MAXOCCURS_UNLIMITED);
2820 : }
2821 : else
2822 : {
2823 75 : oField.SetType(GMLAS_FT_ANYTYPE, szXS_ANY_TYPE);
2824 : }
2825 107 : oField.SetIncludeThisEltInBlob(true);
2826 107 : oField.SetDocumentation(GetAnnotationDoc(poElt));
2827 :
2828 1918 : for (size_t j = 0; j < apoImplEltList.size(); j++)
2829 : {
2830 1811 : XSElementDeclaration *poSubElt = apoImplEltList[j];
2831 1811 : oField.AddAlternateXPath(
2832 3622 : oClass.GetXPath() + "/" +
2833 3622 : MakeXPath(transcode(poSubElt->getNamespace()),
2834 3622 : transcode(poSubElt->getName())));
2835 : }
2836 :
2837 107 : oClass.AddField(oField);
2838 : }
2839 :
2840 : // Are there substitution groups for this element ?
2841 : // or is this element already identified as being a top-level one ?
2842 449107 : else if (!apoImplEltList.empty() ||
2843 215975 : (m_oSetEltsForTopClass.find(poElt) !=
2844 232592 : m_oSetEltsForTopClass.end() &&
2845 16617 : m_oSetSimpleEnoughElts.find(poElt) ==
2846 233132 : m_oSetSimpleEnoughElts.end()))
2847 : {
2848 17152 : CreateNonNestedRelationship(
2849 : poElt, apoImplEltList, oClass, nMaxOccurs,
2850 : bEltNameWillNeedPrefix,
2851 : false, // do not force junction table
2852 : false // regular case
2853 : );
2854 : }
2855 :
2856 : // Abstract element without realizations !
2857 199363 : else if (poElt->getAbstract())
2858 : {
2859 : // Do nothing with it since it cannot be instantiated
2860 : // in a valid way.
2861 219 : CPLDebug("GMLAS",
2862 : "Ignoring %s that is abstract without realizations",
2863 : osElementXPath.c_str());
2864 : }
2865 :
2866 : // Simple type like string, int, etc...
2867 199144 : else if (poTypeDef->getTypeCategory() ==
2868 : XSTypeDefinition::SIMPLE_TYPE)
2869 : {
2870 151792 : XSSimpleTypeDefinition *poST =
2871 : reinterpret_cast<XSSimpleTypeDefinition *>(poTypeDef);
2872 303584 : GMLASField oField;
2873 151792 : SetFieldTypeAndWidthFromDefinition(poST, oField);
2874 151792 : oField.SetMinOccurs((bIsChoice) ? 0 : nMinOccurs);
2875 151792 : oField.SetMaxOccurs(nMaxOccurs);
2876 151792 : oField.SetDocumentation(GetAnnotationDoc(poElt));
2877 :
2878 151792 : bool bNeedAuxTable = false;
2879 151792 : const bool bIsList = (poST->getVariety() ==
2880 151792 : XSSimpleTypeDefinition::VARIETY_LIST);
2881 151792 : if (bIsList)
2882 : {
2883 318 : SetFieldTypeAndWidthFromDefinition(poST->getItemType(),
2884 : oField);
2885 630 : if (bRepeatedParticle || !m_bUseArrays ||
2886 312 : !IsCompatibleOfArray(oField.GetType()))
2887 : {
2888 : // Really particular case. This is a workaround
2889 70 : oField.SetType(GMLAS_FT_STRING, szXS_STRING);
2890 : }
2891 : else
2892 : {
2893 248 : oField.SetList(true);
2894 248 : oField.SetArray(true);
2895 : }
2896 : }
2897 :
2898 154911 : if (m_bUseArrays && bRepeatedParticle &&
2899 3119 : IsCompatibleOfArray(oField.GetType()))
2900 : {
2901 2842 : oField.SetArray(true);
2902 : }
2903 148950 : else if (bRepeatedParticle)
2904 : {
2905 289 : bNeedAuxTable = true;
2906 : }
2907 151792 : if (bNeedAuxTable)
2908 : {
2909 578 : GMLASFeatureClass oNestedClass;
2910 289 : oNestedClass.SetName(oClass.GetName() + "_" +
2911 : osPrefixedEltName);
2912 289 : oNestedClass.SetXPath(osElementXPath);
2913 578 : GMLASField oUniqueField;
2914 289 : oUniqueField.SetName("value");
2915 289 : oUniqueField.SetMinOccurs(1);
2916 289 : oUniqueField.SetMaxOccurs(1);
2917 289 : oUniqueField.SetXPath(osElementXPath);
2918 289 : oUniqueField.SetType(oField.GetType(),
2919 289 : oField.GetTypeName());
2920 289 : oNestedClass.AddField(oUniqueField);
2921 289 : oNestedClass.SetDocumentation(GetAnnotationDoc(poElt));
2922 :
2923 289 : oClass.AddNestedClass(oNestedClass);
2924 :
2925 289 : oField.SetType(GMLAS_FT_STRING, "");
2926 289 : oField.SetName(osPrefixedEltName);
2927 289 : oField.SetXPath(osElementXPath);
2928 289 : oField.SetCategory(
2929 : GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
2930 289 : oField.SetRelatedClassXPath(oField.GetXPath());
2931 289 : oClass.AddField(oField);
2932 : }
2933 : else
2934 : {
2935 151503 : oField.SetName(osPrefixedEltName);
2936 151503 : oField.SetXPath(osElementXPath);
2937 151503 : if (!bIsChoice && nMinOccurs > 0 && !poElt->getNillable())
2938 : {
2939 43924 : oField.SetNotNullable(true);
2940 : }
2941 151503 : oClass.AddField(oField);
2942 :
2943 : // If the element has minOccurs=0 and is nillable, then we
2944 : // need an extra field to be able to distinguish between the
2945 : // case of the missing element or the element with
2946 : // xsi:nil="true"
2947 151604 : if (nMinOccurs == 0 && poElt->getNillable() &&
2948 101 : !m_bUseNullState)
2949 : {
2950 202 : GMLASField oFieldNil;
2951 101 : oFieldNil.SetName(osPrefixedEltName + "_" + szNIL);
2952 101 : oFieldNil.SetXPath(osElementXPath + "/" + szAT_XSI_NIL);
2953 101 : oFieldNil.SetType(GMLAS_FT_BOOLEAN, "boolean");
2954 101 : oFieldNil.SetMinOccurs(0);
2955 101 : oFieldNil.SetMaxOccurs(1);
2956 101 : oClass.AddField(oFieldNil);
2957 : }
2958 : }
2959 : }
2960 :
2961 : // Complex type (element with attributes, composed element, etc...)
2962 47352 : else if (poTypeDef->getTypeCategory() ==
2963 : XSTypeDefinition::COMPLEX_TYPE)
2964 : {
2965 47352 : XSComplexTypeDefinition *poEltCT =
2966 : reinterpret_cast<XSComplexTypeDefinition *>(poTypeDef);
2967 47352 : std::vector<GMLASField> aoFields;
2968 47352 : bool bNothingMoreToDo = false;
2969 47352 : std::vector<GMLASFeatureClass> aoNestedClasses;
2970 :
2971 : const int nMinOccursEltParticle =
2972 47352 : poEltCT->getParticle()
2973 76220 : ? static_cast<int>(
2974 28868 : poEltCT->getParticle()->getMinOccurs())
2975 47352 : : -1;
2976 : const int nMaxOccursEltParticle =
2977 47352 : poEltCT->getParticle()
2978 76220 : ? (poEltCT->getParticle()->getMaxOccursUnbounded()
2979 57538 : ? MAXOCCURS_UNLIMITED
2980 : : static_cast<int>(
2981 28670 : poEltCT->getParticle()->getMaxOccurs()))
2982 47352 : : -1;
2983 :
2984 47352 : const bool bEltRepeatedParticle =
2985 47352 : nMaxOccursEltParticle > 1 ||
2986 : nMaxOccursEltParticle == MAXOCCURS_UNLIMITED;
2987 47352 : const bool bMoveNestedClassToTop =
2988 47352 : !bRepeatedParticle && !bEltRepeatedParticle;
2989 :
2990 : // Process attributes
2991 47352 : XSAttributeUseList *poAttrList = poEltCT->getAttributeUses();
2992 : const size_t nAttrListSize =
2993 47352 : (poAttrList != nullptr) ? poAttrList->size() : 0;
2994 72687 : for (size_t j = 0; j < nAttrListSize; ++j)
2995 : {
2996 25335 : XSAttributeUse *poAttr = poAttrList->elementAt(j);
2997 25335 : GMLASField oField;
2998 : CPLString osNamePrefix(bMoveNestedClassToTop
2999 : ? osPrefixedEltName
3000 25335 : : CPLString());
3001 25335 : if (!SetFieldFromAttribute(oField, poAttr, osElementXPath,
3002 : osNamePrefix))
3003 : {
3004 0 : return false;
3005 : }
3006 25335 : if (nMinOccurs == 0 || bIsChoice)
3007 : {
3008 11760 : oField.SetMinOccurs(0);
3009 11760 : oField.SetNotNullable(false);
3010 : }
3011 :
3012 25335 : if (IsIgnoredXPath(oField.GetXPath()))
3013 : {
3014 : #ifdef DEBUG_VERBOSE
3015 : CPLDebug("GMLAS", "%s is in ignored xpaths",
3016 : oField.GetXPath().c_str());
3017 : #endif
3018 4593 : if (!oField.GetFixedValue().empty() ||
3019 2088 : !oField.GetDefaultValue().empty())
3020 : {
3021 420 : oField.SetIgnored();
3022 : }
3023 : else
3024 : {
3025 2085 : continue;
3026 : }
3027 : }
3028 :
3029 23250 : aoFields.push_back(std::move(oField));
3030 : }
3031 :
3032 : // Deal with anyAttributes (or any element that also imply it)
3033 47352 : XSWildcard *poAttrWildcard = poEltCT->getAttributeWildcard();
3034 47352 : if (poAttrWildcard != nullptr)
3035 : {
3036 714 : GMLASField oField;
3037 357 : oField.SetType(GMLASField::GetTypeFromString(szXS_STRING),
3038 : szFAKEXS_JSON_DICT);
3039 357 : if (!bMoveNestedClassToTop)
3040 : {
3041 95 : oField.SetName("anyAttributes");
3042 : }
3043 : else
3044 : {
3045 262 : oField.SetName(osPrefixedEltName + "_anyAttributes");
3046 : }
3047 357 : oField.SetXPath(osElementXPath + "/" + szAT_ANY_ATTR);
3048 357 : oField.SetDocumentation(
3049 714 : GetAnnotationDoc(poAttrWildcard->getAnnotation()));
3050 :
3051 357 : aoFields.push_back(std::move(oField));
3052 : }
3053 :
3054 47352 : XSSimpleTypeDefinition *poST = poEltCT->getSimpleType();
3055 47352 : if (poST != nullptr)
3056 : {
3057 : /* Case of an element, generally with attributes */
3058 :
3059 36252 : GMLASField oField;
3060 18126 : SetFieldTypeAndWidthFromDefinition(poST, oField);
3061 340 : if (bRepeatedParticle && nAttrListSize == 0 &&
3062 18512 : m_bUseArrays && IsCompatibleOfArray(oField.GetType()) &&
3063 46 : oField.GetCategory() !=
3064 : GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK)
3065 : {
3066 : /* We have a complex type, but no attributes, and */
3067 : /* compatible of arrays, so move it to top level! */
3068 46 : oField.SetName(osPrefixedEltName);
3069 46 : oField.SetArray(true);
3070 46 : oField.SetMinOccurs(nMinOccurs);
3071 46 : oField.SetMaxOccurs(nMaxOccurs);
3072 : }
3073 18080 : else if (bRepeatedParticle)
3074 : {
3075 294 : oField.SetName("value");
3076 294 : oField.SetMinOccurs(1);
3077 294 : oField.SetMaxOccurs(1);
3078 294 : oField.SetNotNullable(true);
3079 : }
3080 : else
3081 : {
3082 17786 : if (nMinOccurs == 0)
3083 : {
3084 10985 : for (size_t j = 0; j < aoFields.size(); j++)
3085 : {
3086 5737 : aoFields[j].SetMinOccurs(0);
3087 5737 : aoFields[j].SetNotNullable(false);
3088 : }
3089 : }
3090 :
3091 17786 : oField.SetName(osPrefixedEltName);
3092 17786 : oField.SetMinOccurs((bIsChoice) ? 0 : nMinOccurs);
3093 17786 : oField.SetMaxOccurs(nMaxOccurs);
3094 :
3095 : // If the element has minOccurs=0 and is nillable, then
3096 : // we need an extra field to be able to distinguish
3097 : // between the case of the missing element or the
3098 : // element with xsi:nil="true"
3099 17994 : if (nMinOccurs == 0 && poElt->getNillable() &&
3100 208 : !m_bUseNullState)
3101 : {
3102 416 : GMLASField oFieldNil;
3103 208 : oFieldNil.SetName(osPrefixedEltName + "_" + szNIL);
3104 208 : oFieldNil.SetXPath(osElementXPath + "/" +
3105 : szAT_XSI_NIL);
3106 208 : oFieldNil.SetType(GMLAS_FT_BOOLEAN, "boolean");
3107 208 : oFieldNil.SetMinOccurs(0);
3108 208 : oFieldNil.SetMaxOccurs(1);
3109 208 : aoFields.push_back(std::move(oFieldNil));
3110 : }
3111 : }
3112 18126 : oField.SetXPath(osElementXPath);
3113 18126 : oField.SetDocumentation(GetAnnotationDoc(poElt));
3114 :
3115 18126 : aoFields.push_back(oField);
3116 18126 : if (oField.IsArray())
3117 : {
3118 46 : oClass.AddField(oField);
3119 46 : bNothingMoreToDo = true;
3120 : }
3121 : }
3122 29226 : else if (IsAnyType(poEltCT))
3123 : {
3124 830 : GMLASField oField;
3125 415 : oField.SetType(GMLAS_FT_ANYTYPE, szXS_ANY_TYPE);
3126 415 : if (bRepeatedParticle)
3127 : {
3128 154 : oField.SetName("value");
3129 154 : oField.SetMinOccurs(1);
3130 154 : oField.SetMaxOccurs(1);
3131 154 : oField.SetNotNullable(true);
3132 : }
3133 : else
3134 : {
3135 261 : if (nMinOccurs == 0)
3136 : {
3137 216 : for (size_t j = 0; j < aoFields.size(); j++)
3138 : {
3139 48 : aoFields[j].SetMinOccurs(0);
3140 48 : aoFields[j].SetNotNullable(false);
3141 : }
3142 : }
3143 :
3144 261 : oField.SetName(osPrefixedEltName);
3145 261 : oField.SetMinOccurs(nMinOccurs);
3146 261 : oField.SetMaxOccurs(nMaxOccurs);
3147 : }
3148 415 : oField.SetXPath(osElementXPath);
3149 415 : oField.SetDocumentation(GetAnnotationDoc(poElt));
3150 :
3151 415 : aoFields.push_back(std::move(oField));
3152 : }
3153 :
3154 : // Is it an element that we already visited ? (cycle)
3155 57264 : else if (poEltCT->getParticle() != nullptr &&
3156 0 : oSetVisitedModelGroups.find(
3157 28453 : poEltCT->getParticle()->getModelGroupTerm()) !=
3158 57264 : oSetVisitedModelGroups.end())
3159 : {
3160 0 : CreateNonNestedRelationship(
3161 : poElt, apoImplEltList, oClass,
3162 : bMoveNestedClassToTop ? 1 : MAXOCCURS_UNLIMITED,
3163 : bEltNameWillNeedPrefix,
3164 : true, // force junction table
3165 : false // regular case
3166 : );
3167 :
3168 0 : bNothingMoreToDo = true;
3169 : }
3170 :
3171 : else
3172 : {
3173 28811 : GMLASFeatureClass oNestedClass;
3174 28811 : oNestedClass.SetName(oClass.GetName() + "_" +
3175 : osPrefixedEltName);
3176 28811 : oNestedClass.SetXPath(osElementXPath);
3177 28811 : oNestedClass.SetDocumentation(GetAnnotationDoc(poElt));
3178 :
3179 : // NULL can happen, for example for gml:ReferenceType
3180 : // that is an empty sequence with just attributes
3181 28811 : if (poEltCT->getParticle() != nullptr)
3182 : {
3183 : #ifdef DEBUG_VERBOSE
3184 : CPLDebug("GMLAS", "Exploring %s",
3185 : osElementXPath.c_str());
3186 : #endif
3187 : std::set<XSModelGroup *> oSetNewVisitedModelGroups(
3188 28453 : oSetVisitedModelGroups);
3189 :
3190 : std::map<CPLString, int>
3191 28453 : oMapCountOccurrencesOfSameNameSub;
3192 28453 : BuildMapCountOccurrencesOfSameName(
3193 : poEltCT->getParticle()->getModelGroupTerm(),
3194 : oMapCountOccurrencesOfSameNameSub);
3195 :
3196 28453 : if (!ExploreModelGroup(
3197 : poEltCT->getParticle()->getModelGroupTerm(),
3198 : nullptr, oNestedClass, nRecursionCounter + 1,
3199 : oSetNewVisitedModelGroups, poModel,
3200 : oMapCountOccurrencesOfSameNameSub))
3201 : {
3202 0 : return false;
3203 : }
3204 : }
3205 :
3206 : // If we have a element of type gml:ReferenceType that has
3207 : // a targetElement in its annotation.appinfo, then create
3208 : // a dedicated field to have cross-layer relationships.
3209 57825 : if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
3210 57825 : transcode(poTypeDef->getName()) == "ReferenceType" &&
3211 58 : !osTargetElement.empty())
3212 : {
3213 : XSElementDeclaration *poTargetElt =
3214 10 : GetTopElementDeclarationFromXPath(osTargetElement,
3215 : poModel);
3216 : // TODO: even for non abstract we should probably
3217 : // handle substitutions
3218 18 : if (poTargetElt != nullptr &&
3219 8 : !poTargetElt->getAbstract())
3220 : {
3221 4 : bool bHasRequiredId = false;
3222 : XSComplexTypeDefinition *poTargetEltCT =
3223 4 : IsEltCompatibleOfFC(poTargetElt);
3224 4 : if (poTargetEltCT)
3225 : {
3226 : XSAttributeUseList *poTargetEltAttrList =
3227 4 : poTargetEltCT->getAttributeUses();
3228 : const size_t nTEAttrListSize =
3229 : (poTargetEltAttrList != nullptr)
3230 4 : ? poTargetEltAttrList->size()
3231 4 : : 0;
3232 6 : for (size_t j = 0; j < nTEAttrListSize; ++j)
3233 : {
3234 : XSAttributeUse *poTEAttr =
3235 4 : poTargetEltAttrList->elementAt(j);
3236 : XSAttributeDeclaration *poTEAttrDecl =
3237 4 : poTEAttr->getAttrDeclaration();
3238 : XSSimpleTypeDefinition *poTEAttrType =
3239 4 : poTEAttrDecl->getTypeDefinition();
3240 8 : if (transcode(poTEAttrType->getName()) ==
3241 12 : szXS_ID &&
3242 4 : poTEAttr->getRequired())
3243 : {
3244 2 : bHasRequiredId = true;
3245 2 : break;
3246 : }
3247 : }
3248 : }
3249 4 : if (bHasRequiredId && !m_bAlwaysGenerateOGRId)
3250 : {
3251 : // If the element is nillable, then we
3252 : // need an extra field to be able to distinguish
3253 : // between the case of the missing element or
3254 : // the element with xsi:nil="true"
3255 2 : if (poElt->getNillable() && !m_bUseNullState)
3256 : {
3257 0 : GMLASField oFieldNil;
3258 0 : oFieldNil.SetName(osPrefixedEltName + "_" +
3259 : szNIL);
3260 0 : oFieldNil.SetXPath(osElementXPath + "/" +
3261 : szAT_XSI_NIL);
3262 0 : oFieldNil.SetType(GMLAS_FT_BOOLEAN,
3263 : "boolean");
3264 0 : oFieldNil.SetMinOccurs(0);
3265 0 : oFieldNil.SetMaxOccurs(1);
3266 0 : aoFields.push_back(std::move(oFieldNil));
3267 : }
3268 :
3269 4 : GMLASField oField;
3270 : // Fake xpath
3271 2 : oField.SetXPath(
3272 : GMLASField::
3273 4 : MakePKIDFieldXPathFromXLinkHrefXPath(
3274 4 : osElementXPath + "/" +
3275 : szAT_XLINK_HREF));
3276 2 : oField.SetName(osPrefixedEltName +
3277 : szPKID_SUFFIX);
3278 2 : oField.SetMinOccurs(0);
3279 2 : oField.SetMaxOccurs(1);
3280 2 : oField.SetType(GMLAS_FT_STRING, szXS_STRING);
3281 2 : oField.SetCategory(
3282 : GMLASField::
3283 : PATH_TO_CHILD_ELEMENT_WITH_LINK);
3284 2 : oField.SetRelatedClassXPath(osTargetElement);
3285 2 : aoFields.push_back(std::move(oField));
3286 : }
3287 : }
3288 10 : else if (poTargetElt != nullptr &&
3289 4 : poTargetElt->getAbstract())
3290 : {
3291 : // If the element is nillable, then we
3292 : // need an extra field to be able to distinguish
3293 : // between the case of the missing element or the
3294 : // element with xsi:nil="true"
3295 4 : if (poElt->getNillable() && !m_bUseNullState)
3296 : {
3297 4 : GMLASField oFieldNil;
3298 2 : oFieldNil.SetName(osPrefixedEltName + "_" +
3299 : szNIL);
3300 2 : oFieldNil.SetXPath(osElementXPath + "/" +
3301 : szAT_XSI_NIL);
3302 2 : oFieldNil.SetType(GMLAS_FT_BOOLEAN, "boolean");
3303 2 : oFieldNil.SetMinOccurs(0);
3304 2 : oFieldNil.SetMaxOccurs(1);
3305 2 : aoFields.push_back(std::move(oFieldNil));
3306 : }
3307 :
3308 : // e.g importing
3309 : // http://inspire.ec.europa.eu/schemas/ad/4.0
3310 : // references bu-base:AbstractConstruction, but
3311 : // sometimes there are no realization available for
3312 : // it, so no need to be verbose about that.
3313 : std::vector<XSElementDeclaration *>
3314 8 : apoImplTargetEltList;
3315 4 : GetConcreteImplementationTypes(
3316 : poTargetElt, apoImplTargetEltList);
3317 4 : if (!apoImplTargetEltList.empty())
3318 : {
3319 0 : CPLDebug("GMLAS",
3320 : "Not handled: targetElement %s of %s "
3321 : "is abstract but has substitutions",
3322 : osTargetElement.c_str(),
3323 : osElementXPath.c_str());
3324 : }
3325 : }
3326 : else
3327 : {
3328 : // This shouldn't happen with consistent schemas
3329 : // but as targetElement is in <annotation>, no
3330 : // general-purpose XSD validator can ensure this
3331 2 : CPLDebug("GMLAS",
3332 : "%s is a targetElement of %s, "
3333 : "but cannot be found",
3334 : osTargetElement.c_str(),
3335 : osElementXPath.c_str());
3336 : }
3337 : }
3338 :
3339 : // Can we move the nested class(es) one level up ?
3340 28811 : if (bMoveNestedClassToTop)
3341 : {
3342 : // Case of an element like
3343 : // <xs:element name="foo">
3344 : // <xs:complexType>
3345 : // <xs:sequence>
3346 :
3347 : const std::vector<GMLASField> &osNestedClassFields =
3348 24079 : oNestedClass.GetFields();
3349 : const bool bIsTimeInstantType =
3350 24079 : transcode(poTypeDef->getName()) ==
3351 : szXS_TIME_INSTANT_TYPE;
3352 427868 : for (size_t j = 0; j < osNestedClassFields.size(); j++)
3353 : {
3354 807578 : GMLASField oField(osNestedClassFields[j]);
3355 19 : if (bIsTimeInstantType &&
3356 403808 : oField.GetType() == GMLAS_FT_ANYSIMPLETYPE &&
3357 3 : oField.GetName() == "timePosition")
3358 : {
3359 1 : oField.SetType(GMLAS_FT_DATETIME,
3360 : szXS_DATETIME);
3361 : }
3362 403789 : oField.SetName(osPrefixedEltName + "_" +
3363 403789 : oField.GetName());
3364 1028550 : if (nMinOccurs == 0 ||
3365 624760 : (poEltCT->getParticle() != nullptr &&
3366 312380 : poEltCT->getParticle()->getMinOccurs() == 0))
3367 : {
3368 91625 : oField.SetMinOccurs(0);
3369 91625 : oField.SetNotNullable(false);
3370 : }
3371 :
3372 403789 : aoFields.push_back(std::move(oField));
3373 : }
3374 :
3375 24079 : aoNestedClasses = oNestedClass.GetNestedClasses();
3376 : }
3377 : else
3378 : {
3379 : // Case of an element like
3380 : // <xs:element name="foo">
3381 : // <xs:complexType>
3382 : // <xs:sequence maxOccurs="unbounded">
3383 : // or
3384 : // <xs:element name="foo" maxOccurs="unbounded">
3385 : // <xs:complexType>
3386 : // <xs:sequence>
3387 : // or even
3388 : // <xs:element name="foo" maxOccurs="unbounded">
3389 : // <xs:complexType>
3390 : // <xs:sequence maxOccurs="unbounded">
3391 8966 : if (m_bUseArrays && nAttrListSize == 0 &&
3392 7557 : oNestedClass.GetNestedClasses().empty() &&
3393 3462 : oNestedClass.GetFields().size() == 1 &&
3394 147 : IsCompatibleOfArray(
3395 9603 : oNestedClass.GetFields()[0].GetType()) &&
3396 53 : oNestedClass.GetFields()[0].GetCategory() !=
3397 : GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK)
3398 : {
3399 : // In the case the sequence has a single element,
3400 : // compatible of array type, and no attribute and
3401 : // no nested classes, then add an array attribute
3402 : // at the top-level
3403 104 : GMLASField oField(oNestedClass.GetFields()[0]);
3404 52 : oField.SetName(osPrefixedEltName + "_" +
3405 52 : oField.GetName());
3406 102 : if (oField.GetMaxOccurs() == 1 &&
3407 102 : bEltRepeatedParticle &&
3408 50 : poEltCT->getParticle() != nullptr)
3409 : {
3410 50 : oField.SetMaxOccurs(nMaxOccursEltParticle);
3411 : }
3412 52 : oField.SetArray(true);
3413 52 : oClass.AddField(oField);
3414 : }
3415 : else
3416 : {
3417 4680 : if (!aoFields.empty() && bEltRepeatedParticle)
3418 : {
3419 : // We have attributes and the sequence is
3420 : // repeated
3421 : // <xs:element name="foo"
3422 : // maxOccurs="unbounded">
3423 : // <xs:complexType>
3424 : // <xs:sequence maxOccurs="unbounded">
3425 : // ...
3426 : // </xs:sequence>
3427 : // <xs:attribute .../>
3428 : // </xs:complexType>
3429 : // </xs:element>
3430 : // So we need to create an
3431 : // intermediate class to store them
3432 106 : GMLASFeatureClass oIntermediateNestedClass;
3433 53 : oIntermediateNestedClass.SetName(
3434 106 : oClass.GetName() + "_" + osPrefixedEltName);
3435 53 : oIntermediateNestedClass.SetXPath(
3436 : osElementXPath);
3437 :
3438 53 : oIntermediateNestedClass.PrependFields(
3439 : aoFields);
3440 :
3441 159 : oNestedClass.SetName(oClass.GetName() + "_" +
3442 106 : osPrefixedEltName +
3443 : "_sequence");
3444 106 : oNestedClass.SetXPath(oNestedClass.GetXPath() +
3445 106 : szEXTRA_SUFFIX +
3446 : "sequence");
3447 53 : oNestedClass.SetIsRepeatedSequence(true);
3448 :
3449 106 : GMLASField oField;
3450 53 : oField.SetXPath(osElementXPath);
3451 53 : oField.SetCategory(
3452 : GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
3453 53 : if (nMaxOccursEltParticle != 1)
3454 53 : oField.SetRepetitionOnSequence(true);
3455 53 : oField.SetMinOccurs(nMinOccursEltParticle);
3456 53 : oField.SetMaxOccurs(nMaxOccursEltParticle);
3457 53 : oField.SetRelatedClassXPath(
3458 : oNestedClass.GetXPath());
3459 53 : oIntermediateNestedClass.AddField(oField);
3460 :
3461 53 : oIntermediateNestedClass.AddNestedClass(
3462 : oNestedClass);
3463 :
3464 53 : oClass.AddNestedClass(oIntermediateNestedClass);
3465 : }
3466 : else
3467 : {
3468 4627 : oNestedClass.SetIsRepeatedSequence(
3469 : bEltRepeatedParticle);
3470 4627 : oNestedClass.PrependFields(aoFields);
3471 :
3472 4627 : oClass.AddNestedClass(oNestedClass);
3473 : }
3474 :
3475 9360 : GMLASField oField;
3476 4680 : oField.SetName(osPrefixedEltName);
3477 4680 : oField.SetXPath(osElementXPath);
3478 4680 : if (bRepeatedParticle)
3479 : {
3480 4579 : if (poEltCT->getParticle() != nullptr)
3481 : {
3482 4532 : oField.SetMinOccurs(ComposeMinOccurs(
3483 : nMinOccurs, nMinOccursEltParticle));
3484 4532 : oField.SetMaxOccurs(ComposeMaxOccurs(
3485 : nMaxOccurs, nMaxOccursEltParticle));
3486 : }
3487 : else
3488 : {
3489 47 : oField.SetMinOccurs(nMinOccurs);
3490 47 : oField.SetMaxOccurs(nMaxOccurs);
3491 : }
3492 : }
3493 101 : else if (poEltCT->getParticle() != nullptr)
3494 : {
3495 101 : if (nMaxOccursEltParticle != 1)
3496 101 : oField.SetRepetitionOnSequence(true);
3497 101 : oField.SetMinOccurs(nMinOccursEltParticle);
3498 101 : oField.SetMaxOccurs(nMaxOccursEltParticle);
3499 : }
3500 4680 : oField.SetCategory(
3501 : GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
3502 4680 : oField.SetRelatedClassXPath(oField.GetXPath());
3503 4680 : oClass.AddField(oField);
3504 : }
3505 :
3506 4732 : bNothingMoreToDo = true;
3507 : }
3508 : }
3509 :
3510 47352 : if (bNothingMoreToDo)
3511 : {
3512 : // Nothing to do
3513 : }
3514 42574 : else if (bRepeatedParticle)
3515 : {
3516 896 : GMLASFeatureClass oNestedClass;
3517 448 : oNestedClass.SetName(oClass.GetName() + "_" +
3518 : osPrefixedEltName);
3519 448 : oNestedClass.SetXPath(osElementXPath);
3520 448 : oNestedClass.AppendFields(aoFields);
3521 448 : oNestedClass.SetDocumentation(GetAnnotationDoc(poElt));
3522 448 : oClass.AddNestedClass(oNestedClass);
3523 :
3524 896 : GMLASField oField;
3525 448 : oField.SetName(osPrefixedEltName);
3526 448 : oField.SetXPath(osElementXPath);
3527 448 : oField.SetMinOccurs((bIsChoice) ? 0 : nMinOccurs);
3528 448 : oField.SetMaxOccurs(nMaxOccurs);
3529 448 : oField.SetCategory(
3530 : GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
3531 448 : oField.SetRelatedClassXPath(oField.GetXPath());
3532 448 : oClass.AddField(oField);
3533 : }
3534 : else
3535 : {
3536 42126 : oClass.AppendFields(aoFields);
3537 45296 : for (size_t j = 0; j < aoNestedClasses.size(); j++)
3538 : {
3539 3170 : oClass.AddNestedClass(aoNestedClasses[j]);
3540 : }
3541 : }
3542 : }
3543 : }
3544 18661 : else if (poParticle->getTermType() == XSParticle::TERM_MODELGROUP)
3545 : {
3546 18471 : XSModelGroup *psSubModelGroup = poParticle->getModelGroupTerm();
3547 18471 : if (bRepeatedParticle)
3548 : {
3549 1748 : GMLASFeatureClass oNestedClass;
3550 1748 : CPLString osGroupName;
3551 : XSModelGroupDefinition *psGroupDefinition =
3552 1748 : GetGroupDefinition(psSubModelGroup);
3553 1748 : if (psGroupDefinition != nullptr)
3554 : {
3555 144 : osGroupName = transcode(psGroupDefinition->getName());
3556 144 : oNestedClass.SetDocumentation(
3557 288 : GetAnnotationDoc(psGroupDefinition->getAnnotation()));
3558 : }
3559 : else
3560 : {
3561 : // Is it a <xs:choice maxOccurs=">1|unbounded"
3562 1604 : if (psSubModelGroup->getCompositor() ==
3563 : XSModelGroup::COMPOSITOR_CHOICE)
3564 : {
3565 : std::set<XSModelGroup *> oSetNewVisitedModelGroups(
3566 1604 : oSetVisitedModelGroups);
3567 1604 : GMLASFeatureClass oTmpClass;
3568 1604 : oTmpClass.SetName(oClass.GetName());
3569 1604 : oTmpClass.SetXPath(oClass.GetXPath());
3570 1604 : if (!ExploreModelGroup(psSubModelGroup, nullptr,
3571 : oTmpClass, nRecursionCounter + 1,
3572 : oSetNewVisitedModelGroups,
3573 : poModel,
3574 : oMapCountOccurrencesOfSameName))
3575 : {
3576 0 : return false;
3577 : }
3578 1604 : bool bHasArray = false;
3579 : std::vector<GMLASField> &oTmpFields =
3580 1604 : oTmpClass.GetFields();
3581 158206 : for (size_t j = 0; j < oTmpFields.size(); ++j)
3582 : {
3583 156673 : if (oTmpFields[j].IsArray())
3584 : {
3585 71 : bHasArray = true;
3586 71 : break;
3587 : }
3588 : }
3589 1604 : if (!bHasArray)
3590 : {
3591 157923 : for (size_t j = 0; j < oTmpFields.size(); ++j)
3592 : {
3593 156390 : oTmpFields[j].SetMayAppearOutOfOrder(true);
3594 156390 : oClass.AddField(oTmpFields[j]);
3595 : }
3596 1533 : return true;
3597 : }
3598 : }
3599 :
3600 71 : nGroup++;
3601 71 : osGroupName = CPLSPrintf("_group%d", nGroup);
3602 : }
3603 215 : oNestedClass.SetName(oClass.GetName() + "_" + osGroupName);
3604 215 : oNestedClass.SetIsGroup(true);
3605 215 : oNestedClass.SetIsRepeatedSequence(true);
3606 : // Caution: we will change it afterwards !
3607 215 : oNestedClass.SetXPath(oClass.GetXPath());
3608 : std::set<XSModelGroup *> oSetNewVisitedModelGroups(
3609 215 : oSetVisitedModelGroups);
3610 215 : if (!ExploreModelGroup(psSubModelGroup, nullptr, oNestedClass,
3611 : nRecursionCounter + 1,
3612 : oSetNewVisitedModelGroups, poModel,
3613 : oMapCountOccurrencesOfSameName))
3614 : {
3615 0 : return false;
3616 : }
3617 : // This is a nasty hack. We set a unique fake xpath *AFTER*
3618 : // processing the group, so that we can add a fake GROUP field
3619 : // pointing to the nested class
3620 215 : oNestedClass.SetXPath(oClass.GetXPath() + szEXTRA_SUFFIX +
3621 : osGroupName);
3622 :
3623 261 : if (m_bUseArrays && oNestedClass.GetFields().size() == 1 &&
3624 46 : IsCompatibleOfArray(oNestedClass.GetFields()[0].GetType()))
3625 : {
3626 92 : GMLASField oField(oNestedClass.GetFields()[0]);
3627 46 : oField.SetMinOccurs(
3628 : ComposeMinOccurs(oField.GetMinOccurs(), nMinOccurs));
3629 46 : oField.SetMaxOccurs(
3630 : ComposeMaxOccurs(oField.GetMaxOccurs(), nMaxOccurs));
3631 46 : oField.SetArray(true);
3632 46 : oClass.AddField(oField);
3633 : }
3634 : else
3635 : {
3636 169 : oClass.AddNestedClass(oNestedClass);
3637 :
3638 338 : GMLASField oField;
3639 169 : oField.SetCategory(GMLASField::GROUP);
3640 169 : oField.SetMinOccurs(nMinOccurs);
3641 169 : oField.SetMaxOccurs(nMaxOccurs);
3642 169 : oField.SetRelatedClassXPath(oNestedClass.GetXPath());
3643 169 : oClass.AddField(oField);
3644 : }
3645 : }
3646 : else
3647 : {
3648 : std::set<XSModelGroup *> oSetNewVisitedModelGroups(
3649 16723 : oSetVisitedModelGroups);
3650 16723 : if (!ExploreModelGroup(psSubModelGroup, nullptr, oClass,
3651 : nRecursionCounter + 1,
3652 : oSetNewVisitedModelGroups, poModel,
3653 : oMapCountOccurrencesOfSameName))
3654 : {
3655 0 : return false;
3656 : }
3657 : }
3658 : }
3659 190 : else if (poParticle->getTermType() == XSParticle::TERM_WILDCARD)
3660 : {
3661 : /* Special case for a layer that matches everything, as found */
3662 : /* in swe:extension */
3663 190 : XSWildcard *poWildcard = poParticle->getWildcardTerm();
3664 380 : GMLASField oField;
3665 190 : oField.SetXPath(oClass.GetXPath() + szMATCH_ALL);
3666 190 : oField.SetName("value");
3667 190 : oField.SetType(GMLAS_FT_ANYTYPE, szXS_ANY_TYPE);
3668 190 : oField.SetIncludeThisEltInBlob(true);
3669 190 : oField.SetMinOccurs(nMinOccurs);
3670 190 : oField.SetMaxOccurs(1);
3671 190 : oField.SetDocumentation(
3672 380 : GetAnnotationDoc(poWildcard->getAnnotation()));
3673 190 : oClass.AddField(oField);
3674 : }
3675 : }
3676 :
3677 48895 : return true;
3678 : }
|