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