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 179 : static XSModel *getGrammarPool(XMLGrammarPool *pool)
20 : {
21 : bool changed;
22 358 : 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 4602 : 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 5609 : eType == GMLAS_FT_DOUBLE || eType == GMLAS_FT_DECIMAL ||
42 4602 : 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 1132 : GMLASPrefixMappingHander(
57 : std::map<CPLString, CPLString> &oMapURIToPrefix,
58 : const std::map<CPLString, CPLString> &oMapDocNSURIToPrefix,
59 : CPLString &osGMLVersionFound)
60 1132 : : m_oMapURIToPrefix(oMapURIToPrefix),
61 : m_oMapDocNSURIToPrefix(oMapDocNSURIToPrefix),
62 1132 : m_osGMLVersionFound(osGMLVersionFound)
63 : {
64 1132 : }
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 837494 : void GMLASPrefixMappingHander::startElement(const XMLCh *const uri,
80 : const XMLCh *const localname,
81 : const XMLCh *const /*qname*/,
82 : const Attributes &attrs)
83 : {
84 837494 : if (!m_osGMLVersionFound.empty())
85 54068 : return;
86 :
87 1566850 : const CPLString osURI(transcode(uri));
88 1566850 : const CPLString osLocalname(transcode(localname));
89 783426 : if (osURI == szXS_URI && osLocalname == "schema")
90 : {
91 520 : bool bIsGML = false;
92 1040 : std::string osVersion;
93 2284 : for (unsigned int i = 0; i < attrs.getLength(); i++)
94 : {
95 3528 : const std::string osAttrLocalName(transcode(attrs.getLocalName(i)));
96 1764 : if (osAttrLocalName == "targetNamespace")
97 : {
98 487 : bIsGML = transcode(attrs.getValue(i)) == szGML_URI;
99 : }
100 1277 : else if (osAttrLocalName == "version")
101 : {
102 285 : osVersion = transcode(attrs.getValue(i));
103 : }
104 : }
105 520 : if (bIsGML && !osVersion.empty())
106 : {
107 0 : m_osGMLVersionFound = std::move(osVersion);
108 : }
109 : }
110 : }
111 :
112 : /************************************************************************/
113 : /* startPrefixMapping() */
114 : /************************************************************************/
115 :
116 3265 : void GMLASPrefixMappingHander::startPrefixMapping(const XMLCh *const prefix,
117 : const XMLCh *const uri)
118 : {
119 6530 : const CPLString osURI(transcode(uri));
120 6530 : CPLString osPrefix(transcode(prefix));
121 3265 : if (osPrefix.empty())
122 : {
123 462 : const auto oIter = m_oMapDocNSURIToPrefix.find(osURI);
124 462 : if (oIter != m_oMapDocNSURIToPrefix.end())
125 : {
126 38 : osPrefix = oIter->second;
127 : }
128 : }
129 3265 : if (!osPrefix.empty())
130 : {
131 2841 : const auto oIter = m_oMapURIToPrefix.find(osURI);
132 2841 : if (oIter == m_oMapURIToPrefix.end())
133 : {
134 585 : m_oMapURIToPrefix[osURI] = osPrefix;
135 585 : CPLDebug("GMLAS", "Registering prefix=%s for uri=%s",
136 : osPrefix.c_str(), osURI.c_str());
137 : }
138 2256 : 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 3265 : }
146 :
147 : /************************************************************************/
148 : /* CollectNamespacePrefixes() */
149 : /************************************************************************/
150 :
151 1132 : 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 2264 : 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 1132 : SAX2XMLReader *poReader = XMLReaderFactory::createXMLReader();
162 :
163 : GMLASPrefixMappingHander contentHandler(
164 2264 : oMapURIToPrefix, oMapDocNSURIToPrefix, osGMLVersionFound);
165 1132 : poReader->setContentHandler(&contentHandler);
166 :
167 2264 : GMLASErrorHandler oErrorHandler;
168 1132 : poReader->setErrorHandler(&oErrorHandler);
169 :
170 1132 : poReader->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution, true);
171 :
172 2264 : std::string osErrorMsg;
173 : try
174 : {
175 1132 : 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 1132 : if (!osErrorMsg.empty())
197 : {
198 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrorMsg.c_str());
199 : }
200 1132 : delete poReader;
201 1132 : }
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 189 : GMLASAnalyzerEntityResolver(
214 : const CPLString &osBasePath,
215 : std::map<CPLString, CPLString> &oMapURIToPrefix,
216 : const std::map<CPLString, CPLString> &oMapDocNSURIToPrefix,
217 : GMLASXSDCache &oCache)
218 189 : : GMLASBaseEntityResolver(osBasePath, oCache),
219 : m_oMapURIToPrefix(oMapURIToPrefix),
220 189 : m_oMapDocNSURIToPrefix(oMapDocNSURIToPrefix)
221 : {
222 189 : }
223 :
224 : virtual void DoExtraSchemaProcessing(
225 : const CPLString &osFilename,
226 : const std::shared_ptr<VSIVirtualHandle> &fp) override;
227 : };
228 :
229 : /************************************************************************/
230 : /* DoExtraSchemaProcessing() */
231 : /************************************************************************/
232 :
233 1132 : void GMLASAnalyzerEntityResolver::DoExtraSchemaProcessing(
234 : const CPLString &osFilename, const std::shared_ptr<VSIVirtualHandle> &fp)
235 : {
236 1132 : CollectNamespacePrefixes(osFilename, fp, m_oMapURIToPrefix,
237 1132 : m_oMapDocNSURIToPrefix, m_osGMLVersionFound);
238 1132 : fp->Seek(0, SEEK_SET);
239 1132 : }
240 :
241 : /************************************************************************/
242 : /* GMLASSchemaAnalyzer() */
243 : /************************************************************************/
244 :
245 193 : GMLASSchemaAnalyzer::GMLASSchemaAnalyzer(
246 : GMLASXPathMatcher &oIgnoredXPathMatcher,
247 : GMLASXPathMatcher &oChildrenElementsConstraintsXPathMatcher,
248 : const std::map<CPLString, std::vector<CPLString>>
249 : &oMapChildrenElementsConstraints,
250 : GMLASXPathMatcher &oForcedFlattenedXPathMatcher,
251 193 : 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 193 : m_bAlwaysGenerateOGRId(ALWAYS_GENERATE_OGR_ID_DEFAULT)
264 : {
265 : // A few hardcoded namespace uri->prefix mappings
266 193 : m_oMapURIToPrefix[szXMLNS_URI] = szXMLNS_PREFIX;
267 193 : m_oMapURIToPrefix[szXSI_URI] = szXSI_PREFIX;
268 193 : }
269 :
270 : /************************************************************************/
271 : /* GetPrefix() */
272 : /************************************************************************/
273 :
274 429183 : CPLString GMLASSchemaAnalyzer::GetPrefix(const CPLString &osNamespaceURI)
275 : {
276 429183 : if (osNamespaceURI.empty())
277 30866 : return "";
278 398317 : const auto oIter = m_oMapURIToPrefix.find(osNamespaceURI);
279 398317 : if (oIter != m_oMapURIToPrefix.end())
280 398314 : 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 412885 : CPLString GMLASSchemaAnalyzer::MakeXPath(const CPLString &osNamespaceURI,
315 : const CPLString &osName)
316 : {
317 825770 : const CPLString osPrefix(GetPrefix(osNamespaceURI));
318 412885 : if (osPrefix.empty())
319 30862 : return osName;
320 764046 : 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 7101 : bool GMLASSchemaAnalyzer::LaunderFieldNames(GMLASFeatureClass &oClass)
353 : {
354 7101 : 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 14202 : std::map<CPLString, std::list<int>> oMapNameToFieldIndex;
362 207078 : for (int i = 0; i < static_cast<int>(aoFields.size()); i++)
363 : {
364 199977 : if (aoFields[i].GetCategory() == GMLASField::REGULAR)
365 : {
366 171880 : oMapNameToFieldIndex[aoFields[i].GetName()].push_back(i);
367 : }
368 : }
369 :
370 14202 : std::set<CPLString> oSetDuplicates;
371 178888 : for (const auto &oIter : oMapNameToFieldIndex)
372 : {
373 : // Has it duplicates ?
374 171787 : const size_t nOccurrences = oIter.second.size();
375 171787 : if (nOccurrences > 1)
376 : {
377 48 : oSetDuplicates.insert(oIter.first);
378 : }
379 : }
380 :
381 7194 : 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 14202 : std::set<CPLString> oSetNames;
496 207078 : for (const auto &oField : aoFields)
497 : {
498 199977 : if (oField.GetCategory() == GMLASField::REGULAR)
499 : {
500 171880 : const auto &osName = oField.GetName();
501 171880 : CPLAssert(oSetNames.find(osName) == oSetNames.end());
502 171880 : oSetNames.insert(osName);
503 : }
504 : }
505 : }
506 : #endif
507 :
508 : // Now check if we must truncate names
509 7101 : if (m_nIdentifierMaxLength >= MIN_VALUE_OF_MAX_IDENTIFIER_LENGTH)
510 : {
511 23676 : for (size_t i = 0; i < aoFields.size(); i++)
512 : {
513 21859 : int nNameSize = static_cast<int>(aoFields[i].GetName().size());
514 : /* Somewhat arbitrary limitation to avoid performance issues in */
515 : /* OGRGMLASTruncateIdentifier() */
516 21859 : 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 21859 : if (nNameSize > m_nIdentifierMaxLength)
524 : {
525 6904 : aoFields[i].SetName(OGRGMLASTruncateIdentifier(
526 3452 : aoFields[i].GetName(), m_nIdentifierMaxLength));
527 : }
528 : }
529 : }
530 :
531 7101 : if (m_bPGIdentifierLaundering)
532 : {
533 201469 : for (size_t i = 0; i < aoFields.size(); i++)
534 : {
535 : char *pszLaundered =
536 194724 : OGRPGCommonLaunderName(aoFields[i].GetName(), "GMLAS", false);
537 194724 : aoFields[i].SetName(pszLaundered);
538 194724 : CPLFree(pszLaundered);
539 : }
540 : }
541 :
542 : // Detect duplicated field names
543 14202 : std::map<CPLString, std::vector<int>> oSetNames;
544 207078 : for (int i = 0; i < static_cast<int>(aoFields.size()); i++)
545 : {
546 199977 : if (aoFields[i].GetCategory() == GMLASField::REGULAR)
547 : {
548 343760 : CPLString osName(aoFields[i].GetName());
549 171880 : if (m_bCaseInsensitiveIdentifier)
550 171880 : osName.toupper();
551 171880 : oSetNames[osName].push_back(i);
552 : }
553 : }
554 :
555 : // Iterate over the unique names
556 178972 : for (const auto &oIter : oSetNames)
557 : {
558 : // Has it duplicates ?
559 171871 : const size_t nOccurrences = oIter.second.size();
560 171871 : 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 7101 : std::vector<GMLASFeatureClass> &aoNestedClasses = oClass.GetNestedClasses();
574 10881 : for (size_t i = 0; i < aoNestedClasses.size(); i++)
575 : {
576 3780 : if (!LaunderFieldNames(aoNestedClasses[i]))
577 0 : return false;
578 : }
579 7101 : return true;
580 : }
581 :
582 : /************************************************************************/
583 : /* CollectClassesReferences() */
584 : /************************************************************************/
585 :
586 16958 : void GMLASSchemaAnalyzer::CollectClassesReferences(
587 : GMLASFeatureClass &oClass, std::vector<GMLASFeatureClass *> &aoClasses)
588 : {
589 16958 : aoClasses.push_back(&oClass);
590 16958 : std::vector<GMLASFeatureClass> &aoNestedClasses = oClass.GetNestedClasses();
591 20738 : for (size_t i = 0; i < aoNestedClasses.size(); i++)
592 : {
593 3780 : CollectClassesReferences(aoNestedClasses[i], aoClasses);
594 : }
595 16958 : }
596 :
597 : /************************************************************************/
598 : /* LaunderClassNames() */
599 : /************************************************************************/
600 :
601 178 : void GMLASSchemaAnalyzer::LaunderClassNames()
602 : {
603 356 : std::vector<GMLASFeatureClass *> aoClasses;
604 13356 : for (size_t i = 0; i < m_aoClasses.size(); i++)
605 : {
606 13178 : CollectClassesReferences(m_aoClasses[i], aoClasses);
607 : }
608 :
609 178 : if (m_nIdentifierMaxLength >= MIN_VALUE_OF_MAX_IDENTIFIER_LENGTH)
610 : {
611 3060 : for (size_t i = 0; i < aoClasses.size(); i++)
612 : {
613 2966 : int nNameSize = static_cast<int>(aoClasses[i]->GetName().size());
614 2966 : if (nNameSize > m_nIdentifierMaxLength)
615 : {
616 2046 : aoClasses[i]->SetName(OGRGMLASTruncateIdentifier(
617 1023 : aoClasses[i]->GetName(), m_nIdentifierMaxLength));
618 : }
619 : }
620 : }
621 :
622 178 : if (m_bPGIdentifierLaundering)
623 : {
624 16740 : for (size_t i = 0; i < aoClasses.size(); i++)
625 : {
626 : char *pszLaundered =
627 16565 : OGRPGCommonLaunderName(aoClasses[i]->GetName(), "GMLAS", false);
628 16565 : aoClasses[i]->SetName(pszLaundered);
629 16565 : 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 356 : std::map<CPLString, std::vector<int>> oSetNames;
638 17136 : for (int i = 0; i < static_cast<int>(aoClasses.size()); i++)
639 : {
640 33916 : CPLString osName(aoClasses[i]->GetName());
641 16958 : if (m_bCaseInsensitiveIdentifier)
642 16958 : osName.toupper();
643 16958 : oSetNames[osName].push_back(i);
644 : }
645 :
646 : // Iterate over the unique names
647 17089 : for (const auto &oIter : oSetNames)
648 : {
649 : // Has it duplicates ?
650 16911 : const size_t nOccurrences = oIter.second.size();
651 16911 : 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 178 : }
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 472 : explicit GMLASUniquePtr(T *p) : m_p(p)
678 : {
679 472 : }
680 :
681 472 : ~GMLASUniquePtr()
682 : {
683 472 : delete m_p;
684 472 : }
685 :
686 2264 : T *operator->() const
687 : {
688 2264 : CPLAssert(m_p);
689 2264 : return m_p;
690 : }
691 :
692 745 : T *get() const
693 : {
694 745 : 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 3342 : GMLASSchemaAnalyzer::GetTopElementDeclarationFromXPath(const CPLString &osXPath,
711 : XSModel *poModel)
712 : {
713 3342 : const char *pszTypename = osXPath.c_str();
714 3342 : const char *pszColon = strrchr(pszTypename, ':');
715 3342 : XSElementDeclaration *poEltDecl = nullptr;
716 3342 : if (pszColon != nullptr)
717 : {
718 6614 : CPLString osNSPrefix = pszTypename;
719 3307 : osNSPrefix.resize(pszColon - pszTypename);
720 6614 : CPLString osName = pszColon + 1;
721 6614 : CPLString osNSURI;
722 :
723 12080 : for (const auto &oIterNS : m_oMapURIToPrefix)
724 : {
725 12080 : const CPLString &osIterNSURI(oIterNS.first);
726 12080 : const CPLString &osIterNSPrefix(oIterNS.second);
727 12080 : if (osNSPrefix == osIterNSPrefix)
728 : {
729 3307 : osNSURI = osIterNSURI;
730 3307 : break;
731 : }
732 : }
733 3307 : XMLCh *xmlNS = nullptr;
734 3307 : XMLCh *xmlName = nullptr;
735 : try
736 : {
737 3307 : xmlNS = XMLString::transcode(osNSURI);
738 3307 : xmlName = XMLString::transcode(osName);
739 3307 : 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 3307 : XMLString::release(&xmlNS);
747 3307 : 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 3342 : return poEltDecl;
764 : }
765 :
766 : /************************************************************************/
767 : /* IsEltCompatibleOfFC() */
768 : /************************************************************************/
769 :
770 : static XSComplexTypeDefinition *
771 79987 : IsEltCompatibleOfFC(XSElementDeclaration *poEltDecl)
772 : {
773 79987 : XSTypeDefinition *poTypeDef = poEltDecl->getTypeDefinition();
774 158895 : if (poTypeDef->getTypeCategory() == XSTypeDefinition::COMPLEX_TYPE &&
775 158895 : transcode(poEltDecl->getName()) != szFEATURE_COLLECTION)
776 : {
777 78774 : XSComplexTypeDefinition *poCT =
778 : reinterpret_cast<XSComplexTypeDefinition *>(poTypeDef);
779 : XSComplexTypeDefinition::CONTENT_TYPE eContentType(
780 78774 : poCT->getContentType());
781 78774 : if (eContentType == XSComplexTypeDefinition::CONTENTTYPE_ELEMENT ||
782 : eContentType == XSComplexTypeDefinition::CONTENTTYPE_MIXED)
783 : {
784 57673 : return poCT;
785 : }
786 : }
787 22314 : return nullptr;
788 : }
789 :
790 : /************************************************************************/
791 : /* DerivesFromGMLFeature() */
792 : /************************************************************************/
793 :
794 1215 : bool GMLASSchemaAnalyzer::DerivesFromGMLFeature(XSElementDeclaration *poEltDecl)
795 : {
796 1215 : XSElementDeclaration *poIter = poEltDecl;
797 : while (true)
798 : {
799 : XSElementDeclaration *poSubstGroup =
800 2673 : poIter->getSubstitutionGroupAffiliation();
801 2673 : if (poSubstGroup == nullptr)
802 924 : break;
803 1749 : const CPLString osSubstNS(transcode(poSubstGroup->getNamespace()));
804 1749 : const CPLString osSubstName(transcode(poSubstGroup->getName()));
805 1749 : if (IsGMLNamespace(osSubstNS) && osSubstName == "_FeatureCollection")
806 : {
807 6 : return false;
808 : }
809 4142 : if (IsGMLNamespace(osSubstNS) &&
810 2399 : (osSubstName == "AbstractFeature" || osSubstName == "_Feature"))
811 : {
812 285 : return true;
813 : }
814 1458 : poIter = poSubstGroup;
815 1458 : }
816 924 : return false;
817 : }
818 :
819 : /************************************************************************/
820 : /* Analyze() */
821 : /************************************************************************/
822 :
823 189 : 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 378 : (new XMLGrammarPoolImpl(XMLPlatformUtils::fgMemoryManager)));
831 :
832 378 : std::vector<CPLString> aoNamespaces;
833 : GMLASAnalyzerEntityResolver oXSDEntityResolver(
834 378 : 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 189 : aoNamespaces.push_back("");
842 462 : for (size_t i = 0; i < aoXSDs.size(); i++)
843 : {
844 283 : const CPLString osURI(aoXSDs[i].first);
845 283 : const CPLString osXSDFilename(aoXSDs[i].second);
846 :
847 : GMLASUniquePtr<SAX2XMLReader> poParser(
848 : XMLReaderFactory::createXMLReader(XMLPlatformUtils::fgMemoryManager,
849 283 : poGrammarPool.get()));
850 :
851 : // Commonly useful configuration.
852 : //
853 283 : poParser->setFeature(XMLUni::fgSAX2CoreNameSpaces, true);
854 283 : poParser->setFeature(XMLUni::fgSAX2CoreNameSpacePrefixes, true);
855 283 : poParser->setFeature(XMLUni::fgSAX2CoreValidation, true);
856 :
857 : // Enable validation.
858 : //
859 283 : poParser->setFeature(XMLUni::fgXercesSchema, true);
860 :
861 : #ifndef __COVERITY__
862 : // coverity[unsafe_xml_parse_config]
863 283 : poParser->setFeature(XMLUni::fgXercesValidationErrorAsFatal, false);
864 : #endif
865 :
866 : // Use the loaded grammar during parsing.
867 : //
868 283 : 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 283 : poParser->setFeature(XMLUni::fgXercesLoadSchema, false);
874 :
875 283 : poParser->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution,
876 283 : true);
877 :
878 283 : Grammar *poGrammar = nullptr;
879 283 : 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 546 : CPLString osGrammarURI(transcode(poGrammar->getTargetNamespace()));
893 273 : if (osGrammarURI.empty())
894 : {
895 28 : if (!osURI.empty())
896 1 : osGrammarURI = osURI;
897 : }
898 273 : if (!osGrammarURI.empty())
899 : {
900 : // Patch back the aoXSDs element in case we didn't know the
901 : // namespace URI initially
902 246 : if (osURI.empty())
903 27 : aoXSDs[i].first = osGrammarURI;
904 246 : aoNamespaces.push_back(std::move(osGrammarURI));
905 : }
906 : }
907 :
908 179 : m_osGMLVersionFound = oXSDEntityResolver.GetGMLVersionFound();
909 179 : m_oSetSchemaURLs = oXSDEntityResolver.GetSchemaURLS();
910 :
911 179 : m_oIgnoredXPathMatcher.SetDocumentMapURIToPrefix(m_oMapURIToPrefix);
912 179 : m_oChildrenElementsConstraintsXPathMatcher.SetDocumentMapURIToPrefix(
913 179 : m_oMapURIToPrefix);
914 179 : m_oForcedFlattenedXPathMatcher.SetDocumentMapURIToPrefix(m_oMapURIToPrefix);
915 179 : m_oDisabledFlattenedXPathMatcher.SetDocumentMapURIToPrefix(
916 179 : m_oMapURIToPrefix);
917 :
918 179 : XSModel *poModel = getGrammarPool(poGrammarPool.get());
919 179 : 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 179 : bool bFoundGMLFeature = false;
934 :
935 : // Second pass, in all namespaces, to figure out inheritance relationships
936 : // and group models that have names
937 358 : std::map<CPLString, CPLString> oMapURIToPrefixWithEmpty(m_oMapURIToPrefix);
938 179 : oMapURIToPrefixWithEmpty[""] = "";
939 1291 : for (const auto &oIterNS : oMapURIToPrefixWithEmpty)
940 : {
941 1112 : const CPLString &osNSURI(oIterNS.first);
942 2801 : if (osNSURI == szXS_URI || osNSURI == szXSI_URI ||
943 2801 : osNSURI == szXMLNS_URI || osNSURI == szXLINK_URI)
944 : {
945 542 : continue;
946 : }
947 :
948 570 : XMLCh *xmlNamespace = nullptr;
949 : try
950 : {
951 570 : 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 570 : poModel->getComponentsByNamespace(
962 : XSConstants::MODEL_GROUP_DEFINITION, xmlNamespace);
963 :
964 : // Remember group models that have names
965 1441 : for (XMLSize_t i = 0; poMapModelGroupDefinition != nullptr &&
966 644 : i < poMapModelGroupDefinition->getLength();
967 : i++)
968 : {
969 : XSModelGroupDefinition *modelGroupDefinition =
970 : reinterpret_cast<XSModelGroupDefinition *>(
971 227 : poMapModelGroupDefinition->item(i));
972 227 : m_oMapModelGroupToMGD[modelGroupDefinition->getModelGroup()] =
973 : modelGroupDefinition;
974 : }
975 :
976 570 : CPLDebug("GMLAS", "Discovering substitutions of %s (%s)",
977 : oIterNS.second.c_str(), osNSURI.c_str());
978 :
979 570 : XSNamedMap<XSObject> *poMapElements = poModel->getComponentsByNamespace(
980 : XSConstants::ELEMENT_DECLARATION, xmlNamespace);
981 :
982 5634 : for (XMLSize_t i = 0;
983 5634 : poMapElements != nullptr && i < poMapElements->getLength(); i++)
984 : {
985 : XSElementDeclaration *poEltDecl =
986 : reinterpret_cast<XSElementDeclaration *>(
987 5064 : poMapElements->item(i));
988 : XSElementDeclaration *poSubstGroup =
989 5064 : poEltDecl->getSubstitutionGroupAffiliation();
990 : const CPLString osEltXPath(
991 10128 : MakeXPath(transcode(poEltDecl->getNamespace()),
992 15192 : transcode(poEltDecl->getName())));
993 5064 : m_oMapXPathToEltDecl[osEltXPath] = poEltDecl;
994 5064 : if (poSubstGroup)
995 : {
996 1481 : 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 325 : if (!bFoundGMLFeature && m_bInstantiateGMLFeaturesOnly &&
1008 2027 : !IsGMLNamespace(osNSURI) &&
1009 221 : DerivesFromGMLFeature(poEltDecl))
1010 : {
1011 35 : 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 35 : bFoundGMLFeature = true;
1018 : }
1019 : }
1020 : }
1021 :
1022 570 : 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 179 : bool bFoundElementsInFirstChoiceNamespaces = false;
1029 509 : for (size_t iNS = 0;
1030 509 : !bFoundElementsInFirstChoiceNamespaces && iNS < aoNamespaces.size();
1031 : iNS++)
1032 : {
1033 330 : XMLCh *xmlNamespace = nullptr;
1034 : try
1035 : {
1036 330 : 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 330 : XSNamedMap<XSObject> *poMapElements = poModel->getComponentsByNamespace(
1046 : XSConstants::ELEMENT_DECLARATION, xmlNamespace);
1047 330 : bFoundElementsInFirstChoiceNamespaces =
1048 330 : poMapElements != nullptr && poMapElements->getLength() > 0;
1049 330 : XMLString::release(&xmlNamespace);
1050 : }
1051 179 : 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 358 : std::set<XSElementDeclaration *> oSetVisitedEltDecl;
1074 358 : std::set<XSModelGroup *> oSetVisitedModelGroups;
1075 358 : 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 358 : std::set<CPLString> aoSetXPathEltsForTopClass;
1082 :
1083 : // Third and fourth passes
1084 537 : for (int iPass = 0; iPass < 2; ++iPass)
1085 : {
1086 1208 : for (size_t iNS = 0; iNS < aoNamespaces.size(); iNS++)
1087 : {
1088 850 : XMLCh *xmlNamespace = nullptr;
1089 : try
1090 : {
1091 850 : 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 850 : poModel->getComponentsByNamespace(
1103 : XSConstants::ELEMENT_DECLARATION, xmlNamespace);
1104 :
1105 6700 : for (XMLSize_t i = 0;
1106 6700 : poMapElements != nullptr && i < poMapElements->getLength();
1107 : i++)
1108 : {
1109 : XSElementDeclaration *poEltDecl =
1110 : reinterpret_cast<XSElementDeclaration *>(
1111 5850 : poMapElements->item(i));
1112 5850 : XSComplexTypeDefinition *poCT = IsEltCompatibleOfFC(poEltDecl);
1113 5850 : if (!poEltDecl->getAbstract() && poCT != nullptr)
1114 : {
1115 : CPLString osXPath(
1116 9584 : MakeXPath(transcode(poEltDecl->getNamespace()),
1117 14376 : transcode(poEltDecl->getName())));
1118 4792 : if (!IsIgnoredXPath(osXPath))
1119 : {
1120 5782 : if (bFoundGMLFeature && m_bInstantiateGMLFeaturesOnly &&
1121 994 : !DerivesFromGMLFeature(poEltDecl))
1122 : {
1123 : // Do nothing
1124 : }
1125 4044 : 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 2022 : oSetVisitedEltDecl.insert(poEltDecl);
1138 2022 : if (aoSetXPathEltsForTopClass.find(osXPath) ==
1139 4044 : aoSetXPathEltsForTopClass.end())
1140 : {
1141 2022 : m_oSetEltsForTopClass.insert(poEltDecl);
1142 2022 : oVectorEltsForTopClass.push_back(poEltDecl);
1143 : aoSetXPathEltsForTopClass.insert(
1144 2022 : std::move(osXPath));
1145 : }
1146 : }
1147 : else
1148 : {
1149 2022 : bool bSimpleEnoughOut = true;
1150 2022 : int nSubCountSubEltOut = 0;
1151 2022 : auto poParticle = poCT->getParticle();
1152 2022 : if (poParticle)
1153 : {
1154 2021 : CPL_IGNORE_RET_VAL(
1155 2021 : 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 850 : XMLString::release(&xmlNamespace);
1170 : }
1171 : }
1172 :
1173 : // Find ambiguous class names
1174 : {
1175 3501 : for (const auto &oIter : m_oSetEltsForTopClass)
1176 : {
1177 3322 : CPLString osName(transcode(oIter->getName()));
1178 3322 : m_oMapEltNamesToInstanceCount[osName]++;
1179 : }
1180 : }
1181 :
1182 : // Instantiate all needed typenames
1183 3500 : for (const auto &poEltDecl : oVectorEltsForTopClass)
1184 : {
1185 6644 : const CPLString osXPath(MakeXPath(transcode(poEltDecl->getNamespace()),
1186 6644 : transcode(poEltDecl->getName())));
1187 :
1188 3322 : bool bError = false;
1189 : bool bResolvedType =
1190 3322 : InstantiateClassFromEltDeclaration(poEltDecl, poModel, bError);
1191 3322 : if (bError)
1192 : {
1193 1 : return false;
1194 : }
1195 3321 : 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 178 : LaunderClassNames();
1206 :
1207 178 : return true;
1208 : }
1209 :
1210 : /************************************************************************/
1211 : /* GetAnnotationDoc() */
1212 : /************************************************************************/
1213 :
1214 417245 : static CPLString GetAnnotationDoc(const XSAnnotation *annotation)
1215 : {
1216 417245 : if (!annotation)
1217 229212 : return CPLString();
1218 376066 : CPLString osAnnot(transcode(annotation->getAnnotationString()));
1219 188033 : CPLXMLNode *psRoot = CPLParseXMLString(osAnnot);
1220 188033 : CPLStripXMLNamespace(psRoot, nullptr, TRUE);
1221 376066 : CPLString osDoc(CPLGetXMLValue(psRoot, "=annotation.documentation", ""));
1222 188033 : CPLDestroyXMLNode(psRoot);
1223 188033 : return osDoc.Trim();
1224 : }
1225 :
1226 : /************************************************************************/
1227 : /* GetAnnotationDoc() */
1228 : /************************************************************************/
1229 :
1230 192142 : static CPLString GetAnnotationDoc(const XSAnnotationList *annotationList)
1231 : {
1232 192142 : if (!annotationList)
1233 7086 : return CPLString();
1234 370112 : CPLString osRet;
1235 370112 : for (size_t i = 0; i < annotationList->size(); ++i)
1236 : {
1237 370112 : CPLString osDoc(GetAnnotationDoc(annotationList->elementAt(i)));
1238 185056 : if (!osDoc.empty())
1239 : {
1240 184946 : if (!osRet.empty())
1241 0 : osRet += "\n";
1242 184946 : osRet += osDoc;
1243 : }
1244 : }
1245 185056 : return osRet;
1246 : }
1247 :
1248 : /************************************************************************/
1249 : /* GetAnnotationDoc() */
1250 : /************************************************************************/
1251 :
1252 192142 : static CPLString GetAnnotationDoc(const XSElementDeclaration *poEltDecl)
1253 : {
1254 192142 : XSTypeDefinition *poTypeDef = poEltDecl->getTypeDefinition();
1255 192142 : CPLString osDoc = GetAnnotationDoc(poEltDecl->getAnnotation());
1256 192142 : XSAnnotationList *list = nullptr;
1257 207418 : while (poTypeDef != nullptr)
1258 : {
1259 207418 : if (poTypeDef->getTypeCategory() == XSTypeDefinition::COMPLEX_TYPE)
1260 : {
1261 56743 : XSComplexTypeDefinition *poCT =
1262 : reinterpret_cast<XSComplexTypeDefinition *>(poTypeDef);
1263 56743 : list = poCT->getAnnotations();
1264 : }
1265 150675 : else if (poTypeDef->getTypeCategory() == XSTypeDefinition::SIMPLE_TYPE)
1266 : {
1267 150675 : XSSimpleTypeDefinition *poST =
1268 : reinterpret_cast<XSSimpleTypeDefinition *>(poTypeDef);
1269 150675 : list = poST->getAnnotations();
1270 : }
1271 207418 : if (list != nullptr)
1272 185056 : break;
1273 22362 : XSTypeDefinition *poNewTypeDef = poTypeDef->getBaseType();
1274 22362 : if (poNewTypeDef == poTypeDef)
1275 7086 : break;
1276 15276 : poTypeDef = poNewTypeDef;
1277 : }
1278 384284 : CPLString osDoc2 = GetAnnotationDoc(list);
1279 192142 : if (!osDoc.empty() && !osDoc2.empty())
1280 : {
1281 883 : osDoc += "\n";
1282 883 : osDoc += osDoc2;
1283 : }
1284 191259 : else if (!osDoc2.empty())
1285 184063 : osDoc = std::move(osDoc2);
1286 384284 : return osDoc;
1287 : }
1288 :
1289 : /************************************************************************/
1290 : /* InstantiateClassFromEltDeclaration() */
1291 : /************************************************************************/
1292 :
1293 3322 : bool GMLASSchemaAnalyzer::InstantiateClassFromEltDeclaration(
1294 : XSElementDeclaration *poEltDecl, XSModel *poModel, bool &bError)
1295 : {
1296 3322 : bError = false;
1297 3322 : XSComplexTypeDefinition *poCT = IsEltCompatibleOfFC(poEltDecl);
1298 3322 : if (!poEltDecl->getAbstract() && poCT != nullptr)
1299 : {
1300 6644 : GMLASFeatureClass oClass;
1301 6644 : const CPLString osEltName(transcode(poEltDecl->getName()));
1302 : const CPLString osXPath(
1303 6644 : MakeXPath(transcode(poEltDecl->getNamespace()), osEltName));
1304 :
1305 3322 : 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 3322 : if (m_oMapEltNamesToInstanceCount[osEltName] > 1)
1314 : {
1315 90 : CPLString osLaunderedXPath(osXPath);
1316 45 : osLaunderedXPath.replaceAll(':', '_');
1317 45 : oClass.SetName(osLaunderedXPath);
1318 : }
1319 : else
1320 3277 : oClass.SetName(osEltName);
1321 :
1322 : #ifdef DEBUG_VERBOSE
1323 : CPLDebug("GMLAS", "Instantiating element %s", osXPath.c_str());
1324 : #endif
1325 3322 : oClass.SetXPath(osXPath);
1326 3322 : oClass.SetIsTopLevelElt(
1327 3322 : GetTopElementDeclarationFromXPath(osXPath, poModel) != nullptr);
1328 :
1329 6644 : std::set<XSModelGroup *> oSetVisitedModelGroups;
1330 :
1331 3322 : oClass.SetDocumentation(GetAnnotationDoc(poEltDecl));
1332 :
1333 : // might be NULL on swe:values for example
1334 3322 : if (poCT->getParticle() != nullptr)
1335 : {
1336 3283 : std::map<CPLString, int> oMapCountOccurrencesOfSameName;
1337 3283 : BuildMapCountOccurrencesOfSameName(
1338 : poCT->getParticle()->getModelGroupTerm(),
1339 : oMapCountOccurrencesOfSameName);
1340 :
1341 3283 : OGRwkbGeometryType eGeomType = wkbUnknown;
1342 3353 : if (IsGMLNamespace(transcode(poCT->getNamespace())) &&
1343 70 : (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 3282 : 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 3321 : if (!LaunderFieldNames(oClass))
1372 0 : return false;
1373 :
1374 3321 : m_aoClasses.push_back(std::move(oClass));
1375 3321 : return true;
1376 : }
1377 0 : return false;
1378 : }
1379 :
1380 : /************************************************************************/
1381 : /* SetFieldTypeAndWidthFromDefinition() */
1382 : /************************************************************************/
1383 :
1384 200972 : void GMLASSchemaAnalyzer::SetFieldTypeAndWidthFromDefinition(
1385 : XSSimpleTypeDefinition *poST, GMLASField &oField)
1386 : {
1387 200972 : int nMaxLength = 0;
1388 346268 : while (
1389 1094480 : poST->getBaseType() != poST &&
1390 547240 : poST->getBaseType()->getTypeCategory() ==
1391 1094480 : XSTypeDefinition::SIMPLE_TYPE &&
1392 543812 : !XMLString::equals(poST->getNamespace(), PSVIUni::fgNamespaceXmlSchema))
1393 : {
1394 : const XMLCh *maxLength =
1395 346268 : poST->getLexicalFacetValue(XSSimpleTypeDefinition::FACET_LENGTH);
1396 346268 : if (maxLength == nullptr)
1397 : {
1398 346159 : maxLength = poST->getLexicalFacetValue(
1399 : XSSimpleTypeDefinition::FACET_MAXLENGTH);
1400 : }
1401 346268 : if (maxLength != nullptr)
1402 168286 : nMaxLength = MAX(nMaxLength, atoi(transcode(maxLength)));
1403 346268 : poST = reinterpret_cast<XSSimpleTypeDefinition *>(poST->getBaseType());
1404 : }
1405 :
1406 200972 : if (XMLString::equals(poST->getNamespace(), PSVIUni::fgNamespaceXmlSchema))
1407 : {
1408 401944 : CPLString osType(transcode(poST->getName()));
1409 200972 : 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 200972 : oField.SetWidth(nMaxLength);
1417 200972 : }
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 408 : bool GMLASSchemaAnalyzer::IsSame(const XSModelGroup *poModelGroup1,
1427 : const XSModelGroup *poModelGroup2)
1428 : {
1429 408 : if (poModelGroup1->getCompositor() != poModelGroup2->getCompositor())
1430 126 : return false;
1431 :
1432 282 : const XSParticleList *poParticleList1 = poModelGroup1->getParticles();
1433 282 : const XSParticleList *poParticleList2 = poModelGroup2->getParticles();
1434 282 : if (poParticleList1->size() != poParticleList2->size())
1435 108 : return false;
1436 :
1437 621 : for (size_t i = 0; i < poParticleList1->size(); ++i)
1438 : {
1439 473 : const XSParticle *poParticle1 = poParticleList1->elementAt(i);
1440 473 : const XSParticle *poParticle2 = poParticleList2->elementAt(i);
1441 943 : if (poParticle1->getTermType() != poParticle2->getTermType() ||
1442 940 : poParticle1->getMinOccurs() != poParticle2->getMinOccurs() ||
1443 1413 : poParticle1->getMaxOccurs() != poParticle2->getMaxOccurs() ||
1444 470 : poParticle1->getMaxOccursUnbounded() !=
1445 470 : poParticle2->getMaxOccursUnbounded())
1446 : {
1447 3 : return false;
1448 : }
1449 470 : switch (poParticle1->getTermType())
1450 : {
1451 0 : case XSParticle::TERM_EMPTY:
1452 0 : break;
1453 :
1454 466 : case XSParticle::TERM_ELEMENT:
1455 : {
1456 : const XSElementDeclaration *poElt1 =
1457 466 : const_cast<XSParticle *>(poParticle1)->getElementTerm();
1458 : const XSElementDeclaration *poElt2 =
1459 466 : const_cast<XSParticle *>(poParticle2)->getElementTerm();
1460 : // Pointer comparison works here
1461 466 : if (poElt1 != poElt2)
1462 23 : 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 1688 : GMLASSchemaAnalyzer::GetGroupDefinition(const XSModelGroup *poModelGroup)
1511 : {
1512 1948 : for (const auto &oIter : m_oMapModelGroupToMGD)
1513 : {
1514 404 : if (IsSame(poModelGroup, oIter.first))
1515 : {
1516 144 : return oIter.second;
1517 : }
1518 : }
1519 :
1520 1544 : return nullptr;
1521 : }
1522 :
1523 : /************************************************************************/
1524 : /* IsAnyType() */
1525 : /************************************************************************/
1526 :
1527 27570 : static bool IsAnyType(XSComplexTypeDefinition *poType)
1528 : {
1529 27570 : if (XMLString::equals(poType->getBaseType()->getNamespace(),
1530 74569 : PSVIUni::fgNamespaceXmlSchema) &&
1531 46999 : transcode(poType->getBaseType()->getName()) == szXS_ANY_TYPE)
1532 : {
1533 19429 : XSParticle *poParticle = poType->getParticle();
1534 19429 : if (poParticle != nullptr)
1535 : {
1536 19075 : XSModelGroup *poGroupTerm = poParticle->getModelGroupTerm();
1537 19075 : if (poGroupTerm != nullptr)
1538 : {
1539 19075 : XSParticleList *poParticles = poGroupTerm->getParticles();
1540 19075 : if (poParticles != nullptr)
1541 : {
1542 20945 : return poParticles->size() == 1 &&
1543 1870 : poParticles->elementAt(0)->getTermType() ==
1544 19075 : XSParticle::TERM_WILDCARD;
1545 : }
1546 : }
1547 : }
1548 354 : else if (poType->getDerivationMethod() ==
1549 : XSConstants::DERIVATION_EXTENSION)
1550 : {
1551 : // swe:values case
1552 0 : return true;
1553 : }
1554 : }
1555 8495 : return false;
1556 : }
1557 :
1558 : /************************************************************************/
1559 : /* SetFieldFromAttribute() */
1560 : /************************************************************************/
1561 :
1562 39357 : bool GMLASSchemaAnalyzer::SetFieldFromAttribute(GMLASField &oField,
1563 : XSAttributeUse *poAttr,
1564 : const CPLString &osXPathPrefix,
1565 : const CPLString &osNamePrefix)
1566 : {
1567 39357 : const XSAttributeDeclaration *poAttrDecl = poAttr->getAttrDeclaration();
1568 78714 : const CPLString osNS(transcode(poAttrDecl->getNamespace()));
1569 78714 : const CPLString osName(transcode(poAttrDecl->getName()));
1570 :
1571 39357 : if (osNamePrefix.empty())
1572 18640 : oField.SetName(osName);
1573 : else
1574 20717 : oField.SetName(osNamePrefix + "_" + osName);
1575 :
1576 39357 : XSSimpleTypeDefinition *poAttrType = poAttrDecl->getTypeDefinition();
1577 39357 : 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 39356 : SetFieldTypeAndWidthFromDefinition(poAttrType, oField);
1586 :
1587 39356 : oField.SetXPath(osXPathPrefix + "/@" + MakeXPath(osNS, osName));
1588 39356 : if (poAttr->getRequired())
1589 : {
1590 25921 : oField.SetNotNullable(true);
1591 : }
1592 39356 : oField.SetMinOccurs(oField.IsNotNullable() ? 1 : 0);
1593 39356 : oField.SetMaxOccurs(1);
1594 39356 : if (poAttr->getConstraintType() == XSConstants::VALUE_CONSTRAINT_FIXED)
1595 : {
1596 1105 : oField.SetFixedValue(transcode(poAttr->getConstraintValue()));
1597 : }
1598 38251 : else if (poAttr->getConstraintType() ==
1599 : XSConstants::VALUE_CONSTRAINT_DEFAULT)
1600 : {
1601 258 : oField.SetDefaultValue(transcode(poAttr->getConstraintValue()));
1602 : }
1603 :
1604 : const bool bIsList =
1605 39356 : (poAttrType->getVariety() == XSSimpleTypeDefinition::VARIETY_LIST);
1606 39356 : if (bIsList)
1607 : {
1608 1593 : SetFieldTypeAndWidthFromDefinition(poAttrType->getItemType(), oField);
1609 1593 : if (m_bUseArrays && IsCompatibleOfArray(oField.GetType()))
1610 : {
1611 1592 : oField.SetList(true);
1612 1592 : 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 39356 : oField.SetDocumentation(GetAnnotationDoc(poAttrDecl->getAnnotation()));
1623 :
1624 39356 : return true;
1625 : }
1626 :
1627 : /************************************************************************/
1628 : /* GetConcreteImplementationTypes() */
1629 : /************************************************************************/
1630 :
1631 337879 : void GMLASSchemaAnalyzer::GetConcreteImplementationTypes(
1632 : XSElementDeclaration *poParentElt,
1633 : std::vector<XSElementDeclaration *> &apoImplEltList)
1634 : {
1635 337879 : const auto oIter = m_oMapParentEltToChildElt.find(poParentElt);
1636 337879 : if (oIter != m_oMapParentEltToChildElt.end())
1637 : {
1638 29706 : for (size_t j = 0; j < oIter->second.size(); j++)
1639 : {
1640 25227 : XSElementDeclaration *poSubElt = oIter->second[j];
1641 25227 : if (IsEltCompatibleOfFC(poSubElt))
1642 : {
1643 15329 : if (!poSubElt->getAbstract())
1644 : {
1645 12828 : apoImplEltList.push_back(poSubElt);
1646 : }
1647 : }
1648 25227 : GetConcreteImplementationTypes(poSubElt, apoImplEltList);
1649 : }
1650 : }
1651 337879 : }
1652 :
1653 : /************************************************************************/
1654 : /* GetConstraintChildrenElements() */
1655 : /************************************************************************/
1656 :
1657 : std::vector<XSElementDeclaration *>
1658 312648 : GMLASSchemaAnalyzer::GetConstraintChildrenElements(const CPLString &osFullXPath)
1659 : {
1660 :
1661 312648 : std::vector<XSElementDeclaration *> oVectorRes;
1662 625296 : CPLString osMatched;
1663 312648 : 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 625296 : return oVectorRes;
1715 : }
1716 :
1717 : /************************************************************************/
1718 : /* GetOGRGeometryType() */
1719 : /************************************************************************/
1720 :
1721 3519 : static OGRwkbGeometryType GetOGRGeometryType(XSTypeDefinition *poTypeDef)
1722 : {
1723 : const struct MyStruct
1724 : {
1725 : const char *pszName;
1726 : OGRwkbGeometryType eType;
1727 3519 : } 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 7038 : CPLString osName(transcode(poTypeDef->getName()));
1753 58041 : for (size_t i = 0; i < CPL_ARRAYSIZE(asArray); ++i)
1754 : {
1755 55827 : if (osName == asArray[i].pszName)
1756 1305 : return asArray[i].eType;
1757 : }
1758 2214 : 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 1971 : GetOGRGeometryTypeFromGMLEltName(const CPLString &osEltName)
1778 : {
1779 : const struct MyStruct
1780 : {
1781 : const char *pszName;
1782 : OGRwkbGeometryType eType;
1783 1971 : } 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 34622 : for (size_t i = 0; i < CPL_ARRAYSIZE(asArray); ++i)
1804 : {
1805 32705 : if (osEltName == asArray[i].pszName)
1806 54 : return asArray[i].eType;
1807 : }
1808 1917 : return wkbNone;
1809 : }
1810 :
1811 : /************************************************************************/
1812 : /* CreateNonNestedRelationship() */
1813 : /************************************************************************/
1814 :
1815 16254 : 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 32508 : const CPLString osEltPrefix(GetPrefix(transcode(poElt->getNamespace())));
1822 32508 : const CPLString osEltName(transcode(poElt->getName()));
1823 : const CPLString osOnlyElementXPath(
1824 32508 : MakeXPath(transcode(poElt->getNamespace()), osEltName));
1825 32508 : const CPLString osElementXPath(oClass.GetXPath() + "/" +
1826 32508 : osOnlyElementXPath);
1827 :
1828 16254 : if (!poElt->getAbstract() && !bCaseOfConstraintChildren)
1829 : {
1830 15870 : apoImplEltList.insert(apoImplEltList.begin(), poElt);
1831 : }
1832 :
1833 32508 : std::set<CPLString> aoSetSubEltXPath;
1834 16254 : 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 21327 : for (size_t j = 0; j < apoImplEltList.size(); j++)
1840 : {
1841 14246 : XSElementDeclaration *poSubElt = apoImplEltList[j];
1842 14246 : const CPLString osSubEltName(transcode(poSubElt->getName()));
1843 : const CPLString osSubEltXPath(
1844 14246 : MakeXPath(transcode(poSubElt->getNamespace()), osSubEltName));
1845 :
1846 : // For AbstractFeature_SpatialDataSet_pkid in SpatialDataSet_member
1847 14246 : if (aoSetSubEltXPath.find(osSubEltXPath) != aoSetSubEltXPath.end())
1848 : {
1849 0 : continue;
1850 : }
1851 14246 : aoSetSubEltXPath.insert(osSubEltXPath);
1852 :
1853 28492 : const CPLString osRealFullXPath(oClass.GetXPath() + "/" +
1854 : ((bCaseOfConstraintChildren)
1855 28489 : ? osOnlyElementXPath + "/"
1856 56981 : : CPLString("")) +
1857 14246 : osSubEltXPath);
1858 :
1859 14246 : if (IsIgnoredXPath(osRealFullXPath))
1860 : {
1861 : #ifdef DEBUG_VERBOSE
1862 : CPLDebug("GMLAS", "%s is in ignored xpaths",
1863 : osRealFullXPath.c_str());
1864 : #endif
1865 91 : continue;
1866 : }
1867 :
1868 28310 : GMLASField oField;
1869 14155 : if (apoImplEltList.size() > 1 || bCaseOfConstraintChildren)
1870 : {
1871 7437 : 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 13017 : oField.SetName(((bEltNameWillNeedPrefix) ? osEltPrefix + "_"
1883 13017 : : CPLString()) +
1884 17356 : transcode(poElt->getName()) + "_" +
1885 8678 : osSubEltName + "_pkid");
1886 : }
1887 : }
1888 : else
1889 : {
1890 6718 : oField.SetName(transcode(poElt->getName()) + "_pkid");
1891 : }
1892 14155 : oField.SetXPath(osRealFullXPath);
1893 14155 : oField.SetMinOccurs(0);
1894 14155 : oField.SetMaxOccurs(nMaxOccurs);
1895 14155 : oField.SetCategory(GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK);
1896 14155 : oField.SetRelatedClassXPath(osSubEltXPath);
1897 14155 : oField.SetType(GMLAS_FT_STRING, szXS_STRING);
1898 14155 : oClass.AddField(oField);
1899 7081 : }
1900 : }
1901 : else
1902 : {
1903 : // If the field is repeated, we need to use junction
1904 : // tables
1905 19030 : for (size_t j = 0; j < apoImplEltList.size(); j++)
1906 : {
1907 9857 : XSElementDeclaration *poSubElt = apoImplEltList[j];
1908 9857 : const CPLString osSubEltName(transcode(poSubElt->getName()));
1909 : const CPLString osSubEltXPath(
1910 9857 : MakeXPath(transcode(poSubElt->getNamespace()), osSubEltName));
1911 :
1912 : // For AbstractFeature_SpatialDataSet_pkid in SpatialDataSet_member
1913 9857 : if (aoSetSubEltXPath.find(osSubEltXPath) != aoSetSubEltXPath.end())
1914 : {
1915 0 : continue;
1916 : }
1917 9857 : aoSetSubEltXPath.insert(osSubEltXPath);
1918 :
1919 : // Instantiate a junction table
1920 19714 : GMLASFeatureClass oJunctionTable;
1921 :
1922 9857 : 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 29331 : oJunctionTable.SetName(oClass.GetName() + "_" +
1933 39108 : 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 9857 : oJunctionTable.SetXPath(
1939 19714 : BuildJunctionTableXPath(osElementXPath, osSubEltXPath));
1940 9857 : oJunctionTable.SetParentXPath(oClass.GetXPath());
1941 9857 : oJunctionTable.SetChildXPath(osSubEltXPath);
1942 9857 : m_aoClasses.push_back(std::move(oJunctionTable));
1943 :
1944 : // Add an abstract field
1945 19714 : GMLASField oField;
1946 9857 : oField.SetName(
1947 19714 : ((bEltNameWillNeedPrefix) ? osEltPrefix + "_" : CPLString()) +
1948 19714 : osEltName + "_" + osSubEltName);
1949 29571 : oField.SetXPath(oClass.GetXPath() + "/" +
1950 : ((bCaseOfConstraintChildren)
1951 29570 : ? osOnlyElementXPath + "/"
1952 39427 : : CPLString("")) +
1953 : osSubEltXPath);
1954 9857 : oField.SetMinOccurs(0);
1955 9857 : oField.SetMaxOccurs(nMaxOccurs);
1956 9857 : oField.SetAbstractElementXPath(osElementXPath);
1957 9857 : oField.SetRelatedClassXPath(osSubEltXPath);
1958 9857 : oField.SetCategory(
1959 : GMLASField::PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE);
1960 9857 : 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 16254 : }
2015 :
2016 : /************************************************************************/
2017 : /* IsIgnoredXPath() */
2018 : /************************************************************************/
2019 :
2020 376600 : bool GMLASSchemaAnalyzer::IsIgnoredXPath(const CPLString &osXPath)
2021 : {
2022 753200 : CPLString osIgnored;
2023 753200 : return m_oIgnoredXPathMatcher.MatchesRefXPath(osXPath, osIgnored);
2024 : }
2025 :
2026 : /************************************************************************/
2027 : /* FindElementsWithMustBeToLevel() */
2028 : /************************************************************************/
2029 :
2030 28624 : 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 28624 : const bool bAlreadyVisitedMG = (oSetVisitedModelGroups.find(poModelGroup) !=
2039 28624 : oSetVisitedModelGroups.end());
2040 :
2041 28624 : oSetVisitedModelGroups.insert(poModelGroup);
2042 :
2043 28624 : 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 57248 : CPLString osIgnored;
2053 28624 : if (m_oDisabledFlattenedXPathMatcher.MatchesRefXPath(osParentXPath,
2054 : osIgnored))
2055 : {
2056 0 : bSimpleEnoughOut = false;
2057 : }
2058 : }
2059 :
2060 28624 : XSParticleList *poParticles = poModelGroup->getParticles();
2061 148848 : for (size_t i = 0; i < poParticles->size(); ++i)
2062 : {
2063 120224 : XSParticle *poParticle = poParticles->elementAt(i);
2064 :
2065 226544 : const bool bRepeatedParticle = poParticle->getMaxOccursUnbounded() ||
2066 106320 : poParticle->getMaxOccurs() > 1;
2067 :
2068 120224 : if (poParticle->getTermType() == XSParticle::TERM_ELEMENT)
2069 : {
2070 108186 : XSElementDeclaration *poElt = poParticle->getElementTerm();
2071 108186 : XSTypeDefinition *poTypeDef = poElt->getTypeDefinition();
2072 108186 : const CPLString osEltName(transcode(poElt->getName()));
2073 108186 : const CPLString osEltNS(transcode(poElt->getNamespace()));
2074 108186 : CPLString osXPath(MakeXPath(osEltNS, osEltName));
2075 216372 : const CPLString osFullXPath(osParentXPath + "/" + osXPath);
2076 :
2077 : #ifdef DEBUG_SUPER_VERBOSE
2078 : CPLDebug("GMLAS", "FindElementsWithMustBeToLevel: %s",
2079 : osFullXPath.c_str());
2080 : #endif
2081 :
2082 108186 : if (IsIgnoredXPath(osFullXPath))
2083 : {
2084 : #ifdef DEBUG_VERBOSE
2085 : CPLDebug("GMLAS", "%s is in ignored xpaths",
2086 : osFullXPath.c_str());
2087 : #endif
2088 96 : continue;
2089 : }
2090 :
2091 : // This could be refined to detect if the repeated element might not
2092 : // be simplifiable as an array
2093 108090 : 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 4460 : bSimpleEnoughOut = false;
2100 : }
2101 :
2102 : // We don't want to inline
2103 : // sub-classes with hundereds of attributes
2104 108090 : nCountSubEltsOut++;
2105 :
2106 108090 : std::vector<XSElementDeclaration *> apoImplEltList;
2107 108090 : GetConcreteImplementationTypes(poElt, apoImplEltList);
2108 :
2109 : std::vector<XSElementDeclaration *> apoChildrenElements =
2110 108090 : GetConstraintChildrenElements(osFullXPath);
2111 :
2112 : // Special case for a GML geometry property
2113 109650 : if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
2114 1560 : GetOGRGeometryType(poTypeDef) != wkbNone)
2115 : {
2116 : // Do nothing
2117 : }
2118 108299 : else if (IsGMLNamespace(osEltNS) &&
2119 833 : GetOGRGeometryTypeFromGMLEltName(osEltName) != wkbNone)
2120 : {
2121 : // Do nothing
2122 : }
2123 : // Any GML abstract type
2124 107953 : else if (poElt->getAbstract() && IsGMLNamespace(osEltNS) &&
2125 92 : osEltName != "_Feature" &&
2126 107947 : osEltName != "AbstractFeature" &&
2127 39 : osEltName != "AbstractTimeObject")
2128 : {
2129 : // Do nothing
2130 : }
2131 : // Are there substitution groups for this element ?
2132 107403 : else if (!apoImplEltList.empty() || !apoChildrenElements.empty())
2133 : {
2134 348 : if (!apoChildrenElements.empty())
2135 : {
2136 3 : apoImplEltList = std::move(apoChildrenElements);
2137 : }
2138 345 : else if (!poElt->getAbstract())
2139 : {
2140 123 : apoImplEltList.insert(apoImplEltList.begin(), poElt);
2141 : }
2142 2384 : for (size_t j = 0; j < apoImplEltList.size(); j++)
2143 : {
2144 2036 : XSElementDeclaration *poSubElt = apoImplEltList[j];
2145 : const CPLString osSubEltXPath(
2146 4072 : MakeXPath(transcode(poSubElt->getNamespace()),
2147 4072 : transcode(poSubElt->getName())));
2148 :
2149 2036 : 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 87 : continue;
2156 : }
2157 :
2158 : // Make sure we will instantiate the referenced element
2159 1949 : if (m_oSetEltsForTopClass.find(poSubElt) ==
2160 4065 : m_oSetEltsForTopClass.end() &&
2161 167 : aoSetXPathEltsForTopClass.find(osSubEltXPath) ==
2162 2116 : 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 167 : oSetVisitedEltDecl.insert(poSubElt);
2178 167 : m_oSetEltsForTopClass.insert(poSubElt);
2179 167 : oVectorEltsForTopClass.push_back(poSubElt);
2180 167 : aoSetXPathEltsForTopClass.insert(osSubEltXPath);
2181 :
2182 : XSComplexTypeDefinition *poSubEltCT =
2183 167 : IsEltCompatibleOfFC(poSubElt);
2184 329 : if (!bAlreadyVisitedMG && poSubEltCT != nullptr &&
2185 162 : poSubEltCT->getParticle() != nullptr)
2186 : {
2187 162 : bool bSubSimpleEnoughOut = true;
2188 162 : int nSubCountSubElt = 0;
2189 162 : 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 213903 : else if (!poElt->getAbstract() &&
2207 106848 : poTypeDef->getTypeCategory() ==
2208 : XSTypeDefinition::COMPLEX_TYPE)
2209 : {
2210 45405 : nCountSubEltsOut--;
2211 :
2212 45405 : XSComplexTypeDefinition *poEltCT = IsEltCompatibleOfFC(poElt);
2213 45405 : if (poEltCT)
2214 : {
2215 : // Might be a bit extreme, but for now we don't inline
2216 : // classes that have subclasses.
2217 33503 : 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 5702 : bSimpleEnoughOut = false;
2225 : }
2226 :
2227 33503 : if (oSetVisitedEltDecl.find(poElt) !=
2228 67006 : oSetVisitedEltDecl.end())
2229 : {
2230 15597 : if (m_oSetEltsForTopClass.find(poElt) ==
2231 20651 : m_oSetEltsForTopClass.end() &&
2232 5054 : m_oSetSimpleEnoughElts.find(poElt) ==
2233 20651 : m_oSetSimpleEnoughElts.end() &&
2234 2904 : aoSetXPathEltsForTopClass.find(osXPath) ==
2235 18501 : aoSetXPathEltsForTopClass.end())
2236 : {
2237 2258 : CPLString osIgnored;
2238 1129 : 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 1129 : m_oSetEltsForTopClass.insert(poElt);
2250 1129 : oVectorEltsForTopClass.push_back(poElt);
2251 : aoSetXPathEltsForTopClass.insert(
2252 1129 : std::move(osXPath));
2253 : }
2254 : }
2255 : }
2256 : else
2257 : {
2258 17906 : oSetVisitedEltDecl.insert(poElt);
2259 :
2260 35812 : if (!bAlreadyVisitedMG &&
2261 17906 : poEltCT->getParticle() != nullptr)
2262 : {
2263 17784 : int nSubCountSubElt = 0;
2264 :
2265 : // Process attributes
2266 : XSAttributeUseList *poAttrList =
2267 17784 : poEltCT->getAttributeUses();
2268 : const size_t nAttrListSize =
2269 17784 : (poAttrList != nullptr) ? poAttrList->size()
2270 17784 : : 0;
2271 20447 : for (size_t j = 0; j < nAttrListSize; ++j)
2272 : {
2273 : XSAttributeUse *poAttr =
2274 2663 : poAttrList->elementAt(j);
2275 2663 : GMLASField oField;
2276 2663 : if (!SetFieldFromAttribute(oField, poAttr,
2277 : osFullXPath))
2278 : {
2279 0 : return false;
2280 : }
2281 4696 : if (!IsIgnoredXPath(oField.GetXPath()) &&
2282 2033 : oField.GetFixedValue().empty())
2283 : {
2284 : #ifdef DEBUG_SUPER_VERBOSE
2285 : CPLDebug(
2286 : "GMLAS",
2287 : "FindElementsWithMustBeToLevel: %s",
2288 : oField.GetXPath().c_str());
2289 : #endif
2290 1909 : nSubCountSubElt++;
2291 : }
2292 : }
2293 :
2294 17784 : bool bSubSimpleEnoughOut = true;
2295 17784 : 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 17784 : if (bSubSimpleEnoughOut)
2307 : {
2308 : #ifdef DEBUG_VERBOSE
2309 : CPLDebug("GMLAS", "%s is inlinable: %d fields",
2310 : osXPath.c_str(), nSubCountSubElt);
2311 : #endif
2312 7760 : m_oSetSimpleEnoughElts.insert(poElt);
2313 :
2314 7760 : nCountSubEltsOut += nSubCountSubElt;
2315 : }
2316 10024 : 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 11902 : if (transcode(poElt->getName()) != szFEATURE_COLLECTION)
2333 : {
2334 11902 : poEltCT = reinterpret_cast<XSComplexTypeDefinition *>(
2335 : poTypeDef);
2336 : // Process attributes
2337 : XSAttributeUseList *poAttrList =
2338 11902 : poEltCT->getAttributeUses();
2339 : const size_t nAttrListSize =
2340 11902 : (poAttrList != nullptr) ? poAttrList->size() : 0;
2341 22693 : for (size_t j = 0;
2342 22693 : bSimpleEnoughOut && j < nAttrListSize; ++j)
2343 : {
2344 10791 : XSAttributeUse *poAttr = poAttrList->elementAt(j);
2345 10791 : GMLASField oField;
2346 10791 : if (!SetFieldFromAttribute(oField, poAttr,
2347 : osFullXPath))
2348 : {
2349 0 : return false;
2350 : }
2351 21558 : if (!IsIgnoredXPath(oField.GetXPath()) &&
2352 10767 : oField.GetFixedValue().empty())
2353 : {
2354 : #ifdef DEBUG_SUPER_VERBOSE
2355 : CPLDebug("GMLAS",
2356 : "FindElementsWithMustBeToLevel: %s",
2357 : oField.GetXPath().c_str());
2358 : #endif
2359 10760 : nCountSubEltsOut++;
2360 : }
2361 : }
2362 : }
2363 : }
2364 :
2365 45405 : CPLString osTargetElement;
2366 45405 : if (poElt->getAnnotation() != nullptr)
2367 : {
2368 : CPLString osAnnot(transcode(
2369 1948 : poElt->getAnnotation()->getAnnotationString()));
2370 :
2371 : #ifdef DEBUG_SUPER_VERBOSE
2372 : CPLDebug("GMLAS", "Annot: %s", osAnnot.c_str());
2373 : #endif
2374 974 : CPLXMLNode *psRoot = CPLParseXMLString(osAnnot);
2375 974 : CPLStripXMLNamespace(psRoot, nullptr, TRUE);
2376 : osTargetElement = CPLGetXMLValue(
2377 974 : psRoot, "=annotation.appinfo.targetElement", "");
2378 974 : 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 91530 : if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
2390 91530 : transcode(poTypeDef->getName()) == "ReferenceType" &&
2391 41 : !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 20907 : else if (!bAlreadyVisitedMG &&
2459 8869 : 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 8653 : 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 8653 : XSModelGroup *psSubModelGroup = poParticle->getModelGroupTerm();
2475 8653 : 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 28624 : if (bSimpleEnoughOut && nCountSubEltsOut > m_nMaximumFieldsForFlattening)
2502 : {
2503 1574 : CPLString osIgnored;
2504 787 : 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 787 : bSimpleEnoughOut = false;
2513 : }
2514 : }
2515 :
2516 28624 : return true;
2517 : }
2518 :
2519 : /************************************************************************/
2520 : /* IsGMLNamespace() */
2521 : /************************************************************************/
2522 :
2523 704814 : bool GMLASSchemaAnalyzer::IsGMLNamespace(const CPLString &osURI)
2524 : {
2525 704814 : if (osURI.find(szGML_URI) == 0)
2526 8095 : return true;
2527 : // Below is mostly for unit tests were we use xmlns:gml="http://fake_gml"
2528 696719 : const auto oIter = m_oMapURIToPrefix.find(osURI);
2529 696719 : return oIter != m_oMapURIToPrefix.end() && oIter->second == szGML_PREFIX;
2530 : }
2531 :
2532 : /************************************************************************/
2533 : /* BuildMapCountOccurrencesOfSameName() */
2534 : /************************************************************************/
2535 :
2536 48339 : void GMLASSchemaAnalyzer::BuildMapCountOccurrencesOfSameName(
2537 : XSModelGroup *poModelGroup,
2538 : std::map<CPLString, int> &oMapCountOccurrencesOfSameName)
2539 : {
2540 48339 : XSParticleList *poParticles = poModelGroup->getParticles();
2541 271719 : for (size_t i = 0; i < poParticles->size(); ++i)
2542 : {
2543 223380 : XSParticle *poParticle = poParticles->elementAt(i);
2544 223380 : if (poParticle->getTermType() == XSParticle::TERM_ELEMENT)
2545 : {
2546 204930 : XSElementDeclaration *poElt = poParticle->getElementTerm();
2547 204930 : const CPLString osEltName(transcode(poElt->getName()));
2548 204930 : oMapCountOccurrencesOfSameName[osEltName]++;
2549 : }
2550 18450 : else if (poParticle->getTermType() == XSParticle::TERM_MODELGROUP)
2551 : {
2552 18257 : XSModelGroup *psSubModelGroup = poParticle->getModelGroupTerm();
2553 18257 : BuildMapCountOccurrencesOfSameName(psSubModelGroup,
2554 : oMapCountOccurrencesOfSameName);
2555 : }
2556 : }
2557 48339 : }
2558 :
2559 : /************************************************************************/
2560 : /* ComposeMinOccurs() */
2561 : /************************************************************************/
2562 :
2563 3975 : static int ComposeMinOccurs(int nVal1, int nVal2)
2564 : {
2565 3975 : return nVal1 * nVal2;
2566 : }
2567 :
2568 : /************************************************************************/
2569 : /* ComposeMaxOccurs() */
2570 : /************************************************************************/
2571 :
2572 3975 : static int ComposeMaxOccurs(int nVal1, int nVal2)
2573 : {
2574 3975 : if (nVal1 == MAXOCCURS_UNLIMITED || nVal2 == MAXOCCURS_UNLIMITED)
2575 3257 : return MAXOCCURS_UNLIMITED;
2576 718 : return nVal1 * nVal2;
2577 : }
2578 :
2579 : /************************************************************************/
2580 : /* ExploreModelGroup() */
2581 : /************************************************************************/
2582 :
2583 48342 : 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 48342 : if (oSetVisitedModelGroups.find(poModelGroup) !=
2590 96684 : 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 48342 : oSetVisitedModelGroups.insert(poModelGroup);
2597 :
2598 48342 : 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 48342 : if (poMainAttrList != nullptr)
2607 : {
2608 385 : const size_t nMainAttrListSize = poMainAttrList->size();
2609 1760 : for (size_t j = 0; j < nMainAttrListSize; ++j)
2610 : {
2611 1376 : GMLASField oField;
2612 1376 : XSAttributeUse *poAttr = poMainAttrList->elementAt(j);
2613 1376 : if (!SetFieldFromAttribute(oField, poAttr, oClass.GetXPath()))
2614 : {
2615 1 : return false;
2616 : }
2617 :
2618 1375 : 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 419 : if (!oField.GetFixedValue().empty() ||
2625 190 : !oField.GetDefaultValue().empty())
2626 : {
2627 41 : oField.SetIgnored();
2628 : }
2629 : else
2630 : {
2631 188 : continue;
2632 : }
2633 : }
2634 :
2635 1187 : oClass.AddField(oField);
2636 : }
2637 : }
2638 :
2639 48341 : 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 48341 : if (oClass.GetXPath() == "gml:metaDataProperty" &&
2645 0 : poModelGroup->getCompositor() == XSModelGroup::COMPOSITOR_SEQUENCE &&
2646 48341 : 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 48341 : (poModelGroup->getCompositor() == XSModelGroup::COMPOSITOR_CHOICE);
2668 48341 : int nGroup = 0;
2669 :
2670 269906 : for (size_t i = 0; i < poParticles->size(); ++i)
2671 : {
2672 223038 : XSParticle *poParticle = poParticles->elementAt(i);
2673 430884 : const bool bRepeatedParticle = poParticle->getMaxOccursUnbounded() ||
2674 207846 : poParticle->getMaxOccurs() > 1;
2675 223038 : const int nMinOccurs = static_cast<int>(poParticle->getMinOccurs());
2676 : const int nMaxOccurs =
2677 223038 : poParticle->getMaxOccursUnbounded()
2678 430884 : ? MAXOCCURS_UNLIMITED
2679 207846 : : static_cast<int>(poParticle->getMaxOccurs());
2680 :
2681 223038 : if (poParticle->getTermType() == XSParticle::TERM_ELEMENT)
2682 : {
2683 204658 : XSElementDeclaration *poElt = poParticle->getElementTerm();
2684 204658 : const CPLString osEltName(transcode(poElt->getName()));
2685 :
2686 204658 : const auto oIter = oMapCountOccurrencesOfSameName.find(osEltName);
2687 : const bool bEltNameWillNeedPrefix =
2688 409316 : oIter != oMapCountOccurrencesOfSameName.end() &&
2689 204658 : oIter->second > 1;
2690 204658 : const CPLString osEltNS(transcode(poElt->getNamespace()));
2691 : const CPLString osPrefixedEltName((bEltNameWillNeedPrefix
2692 409272 : ? GetPrefix(osEltNS) + "_"
2693 818544 : : CPLString()) +
2694 204658 : osEltName);
2695 204658 : const CPLString osOnlyElementXPath(MakeXPath(osEltNS, osEltName));
2696 409316 : const CPLString osElementXPath(oClass.GetXPath() + "/" +
2697 204658 : osOnlyElementXPath);
2698 : #ifdef DEBUG_VERBOSE
2699 : CPLDebug("GMLAS", "Iterating through %s", osElementXPath.c_str());
2700 : #endif
2701 :
2702 204658 : if (IsIgnoredXPath(osElementXPath))
2703 : {
2704 : #ifdef DEBUG_VERBOSE
2705 : CPLDebug("GMLAS", "%s is in ignored xpaths",
2706 : osElementXPath.c_str());
2707 : #endif
2708 100 : continue;
2709 : }
2710 :
2711 204558 : CPLString osTargetElement;
2712 204558 : if (poElt->getAnnotation() != nullptr)
2713 : {
2714 : CPLString osAnnot(
2715 2820 : transcode(poElt->getAnnotation()->getAnnotationString()));
2716 :
2717 : #ifdef DEBUG_SUPER_VERBOSE
2718 : CPLDebug("GMLAS", "Annot: %s", osAnnot.c_str());
2719 : #endif
2720 1410 : CPLXMLNode *psRoot = CPLParseXMLString(osAnnot);
2721 1410 : CPLStripXMLNamespace(psRoot, nullptr, TRUE);
2722 : osTargetElement = CPLGetXMLValue(
2723 1410 : psRoot, "=annotation.appinfo.targetElement", "");
2724 1410 : CPLDestroyXMLNode(psRoot);
2725 : #ifdef DEBUG_VERBOSE
2726 : if (!osTargetElement.empty())
2727 : CPLDebug("GMLAS", "targetElement: %s",
2728 : osTargetElement.c_str());
2729 : #endif
2730 : }
2731 :
2732 204558 : XSTypeDefinition *poTypeDef = poElt->getTypeDefinition();
2733 :
2734 204558 : std::vector<XSElementDeclaration *> apoImplEltList;
2735 204558 : GetConcreteImplementationTypes(poElt, apoImplEltList);
2736 :
2737 : std::vector<XSElementDeclaration *> apoChildrenElements =
2738 204558 : GetConstraintChildrenElements(osElementXPath);
2739 :
2740 : // Special case for a GML geometry property
2741 204558 : OGRwkbGeometryType eGeomType =
2742 : wkbNone; // to make Visual Studio happy
2743 204558 : CPL_IGNORE_RET_VAL(eGeomType); // to make cppcheck happy
2744 :
2745 204558 : 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 206444 : else if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
2756 1889 : (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 205013 : else if (IsGMLNamespace(osEltNS) &&
2782 1138 : (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 204689 : else if (poElt->getAbstract() && IsGMLNamespace(osEltNS) &&
2804 267 : osEltName != "_Feature" &&
2805 204680 : osEltName != "AbstractFeature" &&
2806 122 : osEltName != "AbstractTimeObject")
2807 : {
2808 206 : GMLASField oField;
2809 103 : oField.SetName(osPrefixedEltName);
2810 103 : oField.SetMinOccurs(nMinOccurs);
2811 103 : oField.SetMaxOccurs(nMaxOccurs);
2812 103 : if (osEltName == "AbstractGeometry")
2813 : {
2814 31 : oField.SetType(GMLAS_FT_GEOMETRY, szFAKEXS_GEOMETRY);
2815 31 : oField.SetGeomType(wkbUnknown);
2816 31 : oField.SetArray(nMaxOccurs > 1 ||
2817 : nMaxOccurs == MAXOCCURS_UNLIMITED);
2818 : }
2819 : else
2820 : {
2821 72 : oField.SetType(GMLAS_FT_ANYTYPE, szXS_ANY_TYPE);
2822 : }
2823 103 : oField.SetIncludeThisEltInBlob(true);
2824 103 : oField.SetDocumentation(GetAnnotationDoc(poElt));
2825 :
2826 1883 : for (size_t j = 0; j < apoImplEltList.size(); j++)
2827 : {
2828 1780 : XSElementDeclaration *poSubElt = apoImplEltList[j];
2829 1780 : oField.AddAlternateXPath(
2830 3560 : oClass.GetXPath() + "/" +
2831 3560 : MakeXPath(transcode(poSubElt->getNamespace()),
2832 3560 : transcode(poSubElt->getName())));
2833 : }
2834 :
2835 103 : 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 422668 : else if (!apoImplEltList.empty() ||
2841 203206 : (m_oSetEltsForTopClass.find(poElt) !=
2842 218923 : m_oSetEltsForTopClass.end() &&
2843 15717 : m_oSetSimpleEnoughElts.find(poElt) ==
2844 219462 : m_oSetSimpleEnoughElts.end()))
2845 : {
2846 16251 : 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 187494 : 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 187275 : else if (poTypeDef->getTypeCategory() ==
2866 : XSTypeDefinition::SIMPLE_TYPE)
2867 : {
2868 142308 : XSSimpleTypeDefinition *poST =
2869 : reinterpret_cast<XSSimpleTypeDefinition *>(poTypeDef);
2870 284616 : GMLASField oField;
2871 142308 : SetFieldTypeAndWidthFromDefinition(poST, oField);
2872 142308 : oField.SetMinOccurs((bIsChoice) ? 0 : nMinOccurs);
2873 142308 : oField.SetMaxOccurs(nMaxOccurs);
2874 142308 : oField.SetDocumentation(GetAnnotationDoc(poElt));
2875 :
2876 142308 : bool bNeedAuxTable = false;
2877 142308 : const bool bIsList = (poST->getVariety() ==
2878 142308 : XSSimpleTypeDefinition::VARIETY_LIST);
2879 142308 : 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 144767 : if (m_bUseArrays && bRepeatedParticle &&
2897 2459 : IsCompatibleOfArray(oField.GetType()))
2898 : {
2899 2182 : oField.SetArray(true);
2900 : }
2901 140126 : else if (bRepeatedParticle)
2902 : {
2903 289 : bNeedAuxTable = true;
2904 : }
2905 142308 : 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 142019 : oField.SetName(osPrefixedEltName);
2934 142019 : oField.SetXPath(osElementXPath);
2935 142019 : if (!bIsChoice && nMinOccurs > 0 && !poElt->getNillable())
2936 : {
2937 42064 : oField.SetNotNullable(true);
2938 : }
2939 142019 : 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 142117 : if (nMinOccurs == 0 && poElt->getNillable() &&
2946 98 : !m_bUseNullState)
2947 : {
2948 196 : GMLASField oFieldNil;
2949 98 : oFieldNil.SetName(osPrefixedEltName + "_" + szNIL);
2950 98 : oFieldNil.SetXPath(osElementXPath + "/" + szAT_XSI_NIL);
2951 98 : oFieldNil.SetType(GMLAS_FT_BOOLEAN, "boolean");
2952 98 : oFieldNil.SetMinOccurs(0);
2953 98 : oFieldNil.SetMaxOccurs(1);
2954 98 : oClass.AddField(oFieldNil);
2955 : }
2956 : }
2957 : }
2958 :
2959 : // Complex type (element with attributes, composed element, etc...)
2960 44967 : else if (poTypeDef->getTypeCategory() ==
2961 : XSTypeDefinition::COMPLEX_TYPE)
2962 : {
2963 44967 : XSComplexTypeDefinition *poEltCT =
2964 : reinterpret_cast<XSComplexTypeDefinition *>(poTypeDef);
2965 44967 : std::vector<GMLASField> aoFields;
2966 44967 : bool bNothingMoreToDo = false;
2967 44967 : std::vector<GMLASFeatureClass> aoNestedClasses;
2968 :
2969 : const int nMinOccursEltParticle =
2970 44967 : poEltCT->getParticle()
2971 72181 : ? static_cast<int>(
2972 27214 : poEltCT->getParticle()->getMinOccurs())
2973 44967 : : -1;
2974 : const int nMaxOccursEltParticle =
2975 44967 : poEltCT->getParticle()
2976 72181 : ? (poEltCT->getParticle()->getMaxOccursUnbounded()
2977 54230 : ? MAXOCCURS_UNLIMITED
2978 : : static_cast<int>(
2979 27016 : poEltCT->getParticle()->getMaxOccurs()))
2980 44967 : : -1;
2981 :
2982 44967 : const bool bEltRepeatedParticle =
2983 44967 : nMaxOccursEltParticle > 1 ||
2984 : nMaxOccursEltParticle == MAXOCCURS_UNLIMITED;
2985 44967 : const bool bMoveNestedClassToTop =
2986 44967 : !bRepeatedParticle && !bEltRepeatedParticle;
2987 :
2988 : // Process attributes
2989 44967 : XSAttributeUseList *poAttrList = poEltCT->getAttributeUses();
2990 : const size_t nAttrListSize =
2991 44967 : (poAttrList != nullptr) ? poAttrList->size() : 0;
2992 69494 : for (size_t j = 0; j < nAttrListSize; ++j)
2993 : {
2994 24527 : XSAttributeUse *poAttr = poAttrList->elementAt(j);
2995 24527 : GMLASField oField;
2996 : CPLString osNamePrefix(bMoveNestedClassToTop
2997 : ? osPrefixedEltName
2998 24527 : : CPLString());
2999 24527 : if (!SetFieldFromAttribute(oField, poAttr, osElementXPath,
3000 : osNamePrefix))
3001 : {
3002 0 : return false;
3003 : }
3004 24527 : if (nMinOccurs == 0 || bIsChoice)
3005 : {
3006 11435 : oField.SetMinOccurs(0);
3007 11435 : oField.SetNotNullable(false);
3008 : }
3009 :
3010 24527 : 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 4501 : if (!oField.GetFixedValue().empty() ||
3017 2046 : !oField.GetDefaultValue().empty())
3018 : {
3019 410 : oField.SetIgnored();
3020 : }
3021 : else
3022 : {
3023 2045 : continue;
3024 : }
3025 : }
3026 :
3027 22482 : aoFields.push_back(std::move(oField));
3028 : }
3029 :
3030 : // Deal with anyAttributes (or any element that also imply it)
3031 44967 : XSWildcard *poAttrWildcard = poEltCT->getAttributeWildcard();
3032 44967 : 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 44967 : XSSimpleTypeDefinition *poST = poEltCT->getSimpleType();
3053 44967 : if (poST != nullptr)
3054 : {
3055 : /* Case of an element, generally with attributes */
3056 :
3057 34794 : GMLASField oField;
3058 17397 : SetFieldTypeAndWidthFromDefinition(poST, oField);
3059 338 : if (bRepeatedParticle && nAttrListSize == 0 &&
3060 17781 : 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 17351 : else if (bRepeatedParticle)
3072 : {
3073 292 : oField.SetName("value");
3074 292 : oField.SetMinOccurs(1);
3075 292 : oField.SetMaxOccurs(1);
3076 292 : oField.SetNotNullable(true);
3077 : }
3078 : else
3079 : {
3080 17059 : if (nMinOccurs == 0)
3081 : {
3082 10491 : for (size_t j = 0; j < aoFields.size(); j++)
3083 : {
3084 5487 : aoFields[j].SetMinOccurs(0);
3085 5487 : aoFields[j].SetNotNullable(false);
3086 : }
3087 : }
3088 :
3089 17059 : oField.SetName(osPrefixedEltName);
3090 17059 : oField.SetMinOccurs((bIsChoice) ? 0 : nMinOccurs);
3091 17059 : 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 17267 : 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 17397 : oField.SetXPath(osElementXPath);
3111 17397 : oField.SetDocumentation(GetAnnotationDoc(poElt));
3112 :
3113 17397 : aoFields.push_back(oField);
3114 17397 : if (oField.IsArray())
3115 : {
3116 46 : oClass.AddField(oField);
3117 46 : bNothingMoreToDo = true;
3118 : }
3119 : }
3120 27570 : 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 53954 : else if (poEltCT->getParticle() != nullptr &&
3154 0 : oSetVisitedModelGroups.find(
3155 26799 : poEltCT->getParticle()->getModelGroupTerm()) !=
3156 53954 : 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 27155 : GMLASFeatureClass oNestedClass;
3172 27155 : oNestedClass.SetName(oClass.GetName() + "_" +
3173 : osPrefixedEltName);
3174 27155 : oNestedClass.SetXPath(osElementXPath);
3175 27155 : oNestedClass.SetDocumentation(GetAnnotationDoc(poElt));
3176 :
3177 : // NULL can happen, for example for gml:ReferenceType
3178 : // that is an empty sequence with just attributes
3179 27155 : 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 26799 : oSetVisitedModelGroups);
3187 :
3188 : std::map<CPLString, int>
3189 26799 : oMapCountOccurrencesOfSameNameSub;
3190 26799 : BuildMapCountOccurrencesOfSameName(
3191 : poEltCT->getParticle()->getModelGroupTerm(),
3192 : oMapCountOccurrencesOfSameNameSub);
3193 :
3194 26799 : 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 54507 : if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
3208 54507 : transcode(poTypeDef->getName()) == "ReferenceType" &&
3209 56 : !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 27155 : 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 23026 : oNestedClass.GetFields();
3347 413446 : for (size_t j = 0; j < osNestedClassFields.size(); j++)
3348 : {
3349 780840 : GMLASField oField(osNestedClassFields[j]);
3350 390420 : oField.SetName(osPrefixedEltName + "_" +
3351 390420 : oField.GetName());
3352 993100 : if (nMinOccurs == 0 ||
3353 602680 : (poEltCT->getParticle() != nullptr &&
3354 301340 : poEltCT->getParticle()->getMinOccurs() == 0))
3355 : {
3356 89296 : oField.SetMinOccurs(0);
3357 89296 : oField.SetNotNullable(false);
3358 : }
3359 390420 : aoFields.push_back(std::move(oField));
3360 : }
3361 :
3362 23026 : aoNestedClasses = oNestedClass.GetNestedClasses();
3363 : }
3364 : else
3365 : {
3366 : // Case of an element like
3367 : // <xs:element name="foo">
3368 : // <xs:complexType>
3369 : // <xs:sequence maxOccurs="unbounded">
3370 : // or
3371 : // <xs:element name="foo" maxOccurs="unbounded">
3372 : // <xs:complexType>
3373 : // <xs:sequence>
3374 : // or even
3375 : // <xs:element name="foo" maxOccurs="unbounded">
3376 : // <xs:complexType>
3377 : // <xs:sequence maxOccurs="unbounded">
3378 7763 : if (m_bUseArrays && nAttrListSize == 0 &&
3379 6627 : oNestedClass.GetNestedClasses().empty() &&
3380 3132 : oNestedClass.GetFields().size() == 1 &&
3381 147 : IsCompatibleOfArray(
3382 8397 : oNestedClass.GetFields()[0].GetType()) &&
3383 53 : oNestedClass.GetFields()[0].GetCategory() !=
3384 : GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK)
3385 : {
3386 : // In the case the sequence has a single element,
3387 : // compatible of array type, and no attribute and
3388 : // no nested classes, then add an array attribute
3389 : // at the top-level
3390 104 : GMLASField oField(oNestedClass.GetFields()[0]);
3391 52 : oField.SetName(osPrefixedEltName + "_" +
3392 52 : oField.GetName());
3393 102 : if (oField.GetMaxOccurs() == 1 &&
3394 102 : bEltRepeatedParticle &&
3395 50 : poEltCT->getParticle() != nullptr)
3396 : {
3397 50 : oField.SetMaxOccurs(nMaxOccursEltParticle);
3398 : }
3399 52 : oField.SetArray(true);
3400 52 : oClass.AddField(oField);
3401 : }
3402 : else
3403 : {
3404 4077 : if (!aoFields.empty() && bEltRepeatedParticle)
3405 : {
3406 : // We have attributes and the sequence is
3407 : // repeated
3408 : // <xs:element name="foo"
3409 : // maxOccurs="unbounded">
3410 : // <xs:complexType>
3411 : // <xs:sequence maxOccurs="unbounded">
3412 : // ...
3413 : // </xs:sequence>
3414 : // <xs:attribute .../>
3415 : // </xs:complexType>
3416 : // </xs:element>
3417 : // So we need to create an
3418 : // intermediate class to store them
3419 106 : GMLASFeatureClass oIntermediateNestedClass;
3420 53 : oIntermediateNestedClass.SetName(
3421 106 : oClass.GetName() + "_" + osPrefixedEltName);
3422 53 : oIntermediateNestedClass.SetXPath(
3423 : osElementXPath);
3424 :
3425 53 : oIntermediateNestedClass.PrependFields(
3426 : aoFields);
3427 :
3428 159 : oNestedClass.SetName(oClass.GetName() + "_" +
3429 106 : osPrefixedEltName +
3430 : "_sequence");
3431 106 : oNestedClass.SetXPath(oNestedClass.GetXPath() +
3432 106 : szEXTRA_SUFFIX +
3433 : "sequence");
3434 53 : oNestedClass.SetIsRepeatedSequence(true);
3435 :
3436 106 : GMLASField oField;
3437 53 : oField.SetXPath(osElementXPath);
3438 53 : oField.SetCategory(
3439 : GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
3440 53 : if (nMaxOccursEltParticle != 1)
3441 53 : oField.SetRepetitionOnSequence(true);
3442 53 : oField.SetMinOccurs(nMinOccursEltParticle);
3443 53 : oField.SetMaxOccurs(nMaxOccursEltParticle);
3444 53 : oField.SetRelatedClassXPath(
3445 : oNestedClass.GetXPath());
3446 53 : oIntermediateNestedClass.AddField(oField);
3447 :
3448 53 : oIntermediateNestedClass.AddNestedClass(
3449 : oNestedClass);
3450 :
3451 53 : oClass.AddNestedClass(oIntermediateNestedClass);
3452 : }
3453 : else
3454 : {
3455 4024 : oNestedClass.SetIsRepeatedSequence(
3456 : bEltRepeatedParticle);
3457 4024 : oNestedClass.PrependFields(aoFields);
3458 :
3459 4024 : oClass.AddNestedClass(oNestedClass);
3460 : }
3461 :
3462 8154 : GMLASField oField;
3463 4077 : oField.SetName(osPrefixedEltName);
3464 4077 : oField.SetXPath(osElementXPath);
3465 4077 : if (bRepeatedParticle)
3466 : {
3467 3976 : if (poEltCT->getParticle() != nullptr)
3468 : {
3469 3929 : oField.SetMinOccurs(ComposeMinOccurs(
3470 : nMinOccurs, nMinOccursEltParticle));
3471 3929 : oField.SetMaxOccurs(ComposeMaxOccurs(
3472 : nMaxOccurs, nMaxOccursEltParticle));
3473 : }
3474 : else
3475 : {
3476 47 : oField.SetMinOccurs(nMinOccurs);
3477 47 : oField.SetMaxOccurs(nMaxOccurs);
3478 : }
3479 : }
3480 101 : else if (poEltCT->getParticle() != nullptr)
3481 : {
3482 101 : if (nMaxOccursEltParticle != 1)
3483 101 : oField.SetRepetitionOnSequence(true);
3484 101 : oField.SetMinOccurs(nMinOccursEltParticle);
3485 101 : oField.SetMaxOccurs(nMaxOccursEltParticle);
3486 : }
3487 4077 : oField.SetCategory(
3488 : GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
3489 4077 : oField.SetRelatedClassXPath(oField.GetXPath());
3490 4077 : oClass.AddField(oField);
3491 : }
3492 :
3493 4129 : bNothingMoreToDo = true;
3494 : }
3495 : }
3496 :
3497 44967 : if (bNothingMoreToDo)
3498 : {
3499 : // Nothing to do
3500 : }
3501 40792 : else if (bRepeatedParticle)
3502 : {
3503 892 : GMLASFeatureClass oNestedClass;
3504 446 : oNestedClass.SetName(oClass.GetName() + "_" +
3505 : osPrefixedEltName);
3506 446 : oNestedClass.SetXPath(osElementXPath);
3507 446 : oNestedClass.AppendFields(aoFields);
3508 446 : oNestedClass.SetDocumentation(GetAnnotationDoc(poElt));
3509 446 : oClass.AddNestedClass(oNestedClass);
3510 :
3511 892 : GMLASField oField;
3512 446 : oField.SetName(osPrefixedEltName);
3513 446 : oField.SetXPath(osElementXPath);
3514 446 : oField.SetMinOccurs((bIsChoice) ? 0 : nMinOccurs);
3515 446 : oField.SetMaxOccurs(nMaxOccurs);
3516 446 : oField.SetCategory(
3517 : GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
3518 446 : oField.SetRelatedClassXPath(oField.GetXPath());
3519 446 : oClass.AddField(oField);
3520 : }
3521 : else
3522 : {
3523 40346 : oClass.AppendFields(aoFields);
3524 43363 : for (size_t j = 0; j < aoNestedClasses.size(); j++)
3525 : {
3526 3017 : oClass.AddNestedClass(aoNestedClasses[j]);
3527 : }
3528 : }
3529 : }
3530 : }
3531 18380 : else if (poParticle->getTermType() == XSParticle::TERM_MODELGROUP)
3532 : {
3533 18190 : XSModelGroup *psSubModelGroup = poParticle->getModelGroupTerm();
3534 18190 : if (bRepeatedParticle)
3535 : {
3536 1688 : GMLASFeatureClass oNestedClass;
3537 1688 : CPLString osGroupName;
3538 : XSModelGroupDefinition *psGroupDefinition =
3539 1688 : GetGroupDefinition(psSubModelGroup);
3540 1688 : if (psGroupDefinition != nullptr)
3541 : {
3542 144 : osGroupName = transcode(psGroupDefinition->getName());
3543 144 : oNestedClass.SetDocumentation(
3544 288 : GetAnnotationDoc(psGroupDefinition->getAnnotation()));
3545 : }
3546 : else
3547 : {
3548 : // Is it a <xs:choice maxOccurs=">1|unbounded"
3549 1544 : if (psSubModelGroup->getCompositor() ==
3550 : XSModelGroup::COMPOSITOR_CHOICE)
3551 : {
3552 : std::set<XSModelGroup *> oSetNewVisitedModelGroups(
3553 1544 : oSetVisitedModelGroups);
3554 1544 : GMLASFeatureClass oTmpClass;
3555 1544 : oTmpClass.SetName(oClass.GetName());
3556 1544 : oTmpClass.SetXPath(oClass.GetXPath());
3557 1544 : if (!ExploreModelGroup(psSubModelGroup, nullptr,
3558 : oTmpClass, nRecursionCounter + 1,
3559 : oSetNewVisitedModelGroups,
3560 : poModel,
3561 : oMapCountOccurrencesOfSameName))
3562 : {
3563 0 : return false;
3564 : }
3565 1544 : bool bHasArray = false;
3566 : std::vector<GMLASField> &oTmpFields =
3567 1544 : oTmpClass.GetFields();
3568 151246 : for (size_t j = 0; j < oTmpFields.size(); ++j)
3569 : {
3570 149773 : if (oTmpFields[j].IsArray())
3571 : {
3572 71 : bHasArray = true;
3573 71 : break;
3574 : }
3575 : }
3576 1544 : if (!bHasArray)
3577 : {
3578 150963 : for (size_t j = 0; j < oTmpFields.size(); ++j)
3579 : {
3580 149490 : oTmpFields[j].SetMayAppearOutOfOrder(true);
3581 149490 : oClass.AddField(oTmpFields[j]);
3582 : }
3583 1473 : return true;
3584 : }
3585 : }
3586 :
3587 71 : nGroup++;
3588 71 : osGroupName = CPLSPrintf("_group%d", nGroup);
3589 : }
3590 215 : oNestedClass.SetName(oClass.GetName() + "_" + osGroupName);
3591 215 : oNestedClass.SetIsGroup(true);
3592 215 : oNestedClass.SetIsRepeatedSequence(true);
3593 : // Caution: we will change it afterwards !
3594 215 : oNestedClass.SetXPath(oClass.GetXPath());
3595 : std::set<XSModelGroup *> oSetNewVisitedModelGroups(
3596 215 : oSetVisitedModelGroups);
3597 215 : if (!ExploreModelGroup(psSubModelGroup, nullptr, oNestedClass,
3598 : nRecursionCounter + 1,
3599 : oSetNewVisitedModelGroups, poModel,
3600 : oMapCountOccurrencesOfSameName))
3601 : {
3602 0 : return false;
3603 : }
3604 : // This is a nasty hack. We set a unique fake xpath *AFTER*
3605 : // processing the group, so that we can add a fake GROUP field
3606 : // pointing to the nested class
3607 215 : oNestedClass.SetXPath(oClass.GetXPath() + szEXTRA_SUFFIX +
3608 : osGroupName);
3609 :
3610 261 : if (m_bUseArrays && oNestedClass.GetFields().size() == 1 &&
3611 46 : IsCompatibleOfArray(oNestedClass.GetFields()[0].GetType()))
3612 : {
3613 92 : GMLASField oField(oNestedClass.GetFields()[0]);
3614 46 : oField.SetMinOccurs(
3615 : ComposeMinOccurs(oField.GetMinOccurs(), nMinOccurs));
3616 46 : oField.SetMaxOccurs(
3617 : ComposeMaxOccurs(oField.GetMaxOccurs(), nMaxOccurs));
3618 46 : oField.SetArray(true);
3619 46 : oClass.AddField(oField);
3620 : }
3621 : else
3622 : {
3623 169 : oClass.AddNestedClass(oNestedClass);
3624 :
3625 338 : GMLASField oField;
3626 169 : oField.SetCategory(GMLASField::GROUP);
3627 169 : oField.SetMinOccurs(nMinOccurs);
3628 169 : oField.SetMaxOccurs(nMaxOccurs);
3629 169 : oField.SetRelatedClassXPath(oNestedClass.GetXPath());
3630 169 : oClass.AddField(oField);
3631 : }
3632 : }
3633 : else
3634 : {
3635 : std::set<XSModelGroup *> oSetNewVisitedModelGroups(
3636 16502 : oSetVisitedModelGroups);
3637 16502 : if (!ExploreModelGroup(psSubModelGroup, nullptr, oClass,
3638 : nRecursionCounter + 1,
3639 : oSetNewVisitedModelGroups, poModel,
3640 : oMapCountOccurrencesOfSameName))
3641 : {
3642 0 : return false;
3643 : }
3644 : }
3645 : }
3646 190 : else if (poParticle->getTermType() == XSParticle::TERM_WILDCARD)
3647 : {
3648 : /* Special case for a layer that matches everything, as found */
3649 : /* in swe:extension */
3650 190 : XSWildcard *poWildcard = poParticle->getWildcardTerm();
3651 380 : GMLASField oField;
3652 190 : oField.SetXPath(oClass.GetXPath() + szMATCH_ALL);
3653 190 : oField.SetName("value");
3654 190 : oField.SetType(GMLAS_FT_ANYTYPE, szXS_ANY_TYPE);
3655 190 : oField.SetIncludeThisEltInBlob(true);
3656 190 : oField.SetMinOccurs(nMinOccurs);
3657 190 : oField.SetMaxOccurs(1);
3658 190 : oField.SetDocumentation(
3659 380 : GetAnnotationDoc(poWildcard->getAnnotation()));
3660 190 : oClass.AddField(oField);
3661 : }
3662 : }
3663 :
3664 46868 : return true;
3665 : }
|