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 : // coverity[unsafe_xml_parse_config]
862 283 : poParser->setFeature(XMLUni::fgXercesValidationErrorAsFatal, false);
863 :
864 : // Use the loaded grammar during parsing.
865 : //
866 283 : poParser->setFeature(XMLUni::fgXercesUseCachedGrammarInParse, true);
867 :
868 : // Don't load schemas from any other source (e.g., from XML document's
869 : // xsi:schemaLocation attributes).
870 : //
871 283 : poParser->setFeature(XMLUni::fgXercesLoadSchema, false);
872 :
873 283 : poParser->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution,
874 283 : true);
875 :
876 283 : Grammar *poGrammar = nullptr;
877 283 : if (!GMLASReader::LoadXSDInParser(
878 : poParser.get(), oCache, oXSDEntityResolver, osBaseDirname,
879 : osXSDFilename, &poGrammar, bSchemaFullChecking,
880 : bHandleMultipleImports))
881 : {
882 10 : return false;
883 : }
884 :
885 : // Some .xsd like
886 : // http://www.opengis.net/gwml-main/2.1 ->
887 : // 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
888 : // do not have a declared targetNamespace, so use the one of the
889 : // schemaLocation if the grammar returns an empty namespace.
890 546 : CPLString osGrammarURI(transcode(poGrammar->getTargetNamespace()));
891 273 : if (osGrammarURI.empty())
892 : {
893 28 : if (!osURI.empty())
894 1 : osGrammarURI = osURI;
895 : }
896 273 : if (!osGrammarURI.empty())
897 : {
898 : // Patch back the aoXSDs element in case we didn't know the
899 : // namespace URI initially
900 246 : if (osURI.empty())
901 27 : aoXSDs[i].first = osGrammarURI;
902 246 : aoNamespaces.push_back(std::move(osGrammarURI));
903 : }
904 : }
905 :
906 179 : m_osGMLVersionFound = oXSDEntityResolver.GetGMLVersionFound();
907 179 : m_oSetSchemaURLs = oXSDEntityResolver.GetSchemaURLS();
908 :
909 179 : m_oIgnoredXPathMatcher.SetDocumentMapURIToPrefix(m_oMapURIToPrefix);
910 179 : m_oChildrenElementsConstraintsXPathMatcher.SetDocumentMapURIToPrefix(
911 179 : m_oMapURIToPrefix);
912 179 : m_oForcedFlattenedXPathMatcher.SetDocumentMapURIToPrefix(m_oMapURIToPrefix);
913 179 : m_oDisabledFlattenedXPathMatcher.SetDocumentMapURIToPrefix(
914 179 : m_oMapURIToPrefix);
915 :
916 179 : XSModel *poModel = getGrammarPool(poGrammarPool.get());
917 179 : CPLAssert(poModel); // should not be null according to doc
918 :
919 : #if 0
920 : XSNamespaceItem* nsItem = poModel->getNamespaceItem(
921 : loadedGrammar->getTargetNamespace());
922 : if( nsItem == NULL )
923 : {
924 : CPLError(CE_Failure, CPLE_AppDefined,
925 : "getNamespaceItem(%s) failed",
926 : transcode(loadedGrammar->getTargetNamespace()).c_str());
927 : return false;
928 : }
929 : #endif
930 :
931 179 : bool bFoundGMLFeature = false;
932 :
933 : // Second pass, in all namespaces, to figure out inheritance relationships
934 : // and group models that have names
935 358 : std::map<CPLString, CPLString> oMapURIToPrefixWithEmpty(m_oMapURIToPrefix);
936 179 : oMapURIToPrefixWithEmpty[""] = "";
937 1291 : for (const auto &oIterNS : oMapURIToPrefixWithEmpty)
938 : {
939 1112 : const CPLString &osNSURI(oIterNS.first);
940 2801 : if (osNSURI == szXS_URI || osNSURI == szXSI_URI ||
941 2801 : osNSURI == szXMLNS_URI || osNSURI == szXLINK_URI)
942 : {
943 542 : continue;
944 : }
945 :
946 570 : XMLCh *xmlNamespace = nullptr;
947 : try
948 : {
949 570 : xmlNamespace = XMLString::transcode(osNSURI.c_str());
950 : }
951 0 : catch (const TranscodingException &e)
952 : {
953 0 : CPLError(CE_Failure, CPLE_AppDefined, "TranscodingException: %s",
954 0 : transcode(e.getMessage()).c_str());
955 0 : return false;
956 : }
957 :
958 : XSNamedMap<XSObject> *poMapModelGroupDefinition =
959 570 : poModel->getComponentsByNamespace(
960 : XSConstants::MODEL_GROUP_DEFINITION, xmlNamespace);
961 :
962 : // Remember group models that have names
963 1441 : for (XMLSize_t i = 0; poMapModelGroupDefinition != nullptr &&
964 644 : i < poMapModelGroupDefinition->getLength();
965 : i++)
966 : {
967 : XSModelGroupDefinition *modelGroupDefinition =
968 : reinterpret_cast<XSModelGroupDefinition *>(
969 227 : poMapModelGroupDefinition->item(i));
970 227 : m_oMapModelGroupToMGD[modelGroupDefinition->getModelGroup()] =
971 : modelGroupDefinition;
972 : }
973 :
974 570 : CPLDebug("GMLAS", "Discovering substitutions of %s (%s)",
975 : oIterNS.second.c_str(), osNSURI.c_str());
976 :
977 570 : XSNamedMap<XSObject> *poMapElements = poModel->getComponentsByNamespace(
978 : XSConstants::ELEMENT_DECLARATION, xmlNamespace);
979 :
980 5634 : for (XMLSize_t i = 0;
981 5634 : poMapElements != nullptr && i < poMapElements->getLength(); i++)
982 : {
983 : XSElementDeclaration *poEltDecl =
984 : reinterpret_cast<XSElementDeclaration *>(
985 5064 : poMapElements->item(i));
986 : XSElementDeclaration *poSubstGroup =
987 5064 : poEltDecl->getSubstitutionGroupAffiliation();
988 : const CPLString osEltXPath(
989 10128 : MakeXPath(transcode(poEltDecl->getNamespace()),
990 15192 : transcode(poEltDecl->getName())));
991 5064 : m_oMapXPathToEltDecl[osEltXPath] = poEltDecl;
992 5064 : if (poSubstGroup)
993 : {
994 1481 : m_oMapParentEltToChildElt[poSubstGroup].push_back(poEltDecl);
995 : #ifdef DEBUG_VERBOSE
996 : CPLString osParentType(
997 : MakeXPath(transcode(poSubstGroup->getNamespace()),
998 : transcode(poSubstGroup->getName())));
999 : CPLDebug("GMLAS", "%s is a substitution for %s",
1000 : osEltXPath.c_str(), osParentType.c_str());
1001 : #endif
1002 :
1003 : // Check if this element derives from
1004 : // gml:_Feature/AbstractFeature
1005 325 : if (!bFoundGMLFeature && m_bInstantiateGMLFeaturesOnly &&
1006 2027 : !IsGMLNamespace(osNSURI) &&
1007 221 : DerivesFromGMLFeature(poEltDecl))
1008 : {
1009 35 : CPLDebug("GMLAS",
1010 : "Restricting (in first pass) top level "
1011 : "elements to those deriving from "
1012 : "gml:_Feature/gml:AbstractFeature (due "
1013 : "to %s found)",
1014 : osEltXPath.c_str());
1015 35 : bFoundGMLFeature = true;
1016 : }
1017 : }
1018 : }
1019 :
1020 570 : XMLString::release(&xmlNamespace);
1021 : }
1022 :
1023 : // Check that we can find elements in the namespaces pointed in the
1024 : // xsi:schemaLocation of the document, then fallback to namespaces
1025 : // that might be indirectly imported by those first level namespaces
1026 179 : bool bFoundElementsInFirstChoiceNamespaces = false;
1027 509 : for (size_t iNS = 0;
1028 509 : !bFoundElementsInFirstChoiceNamespaces && iNS < aoNamespaces.size();
1029 : iNS++)
1030 : {
1031 330 : XMLCh *xmlNamespace = nullptr;
1032 : try
1033 : {
1034 330 : xmlNamespace = XMLString::transcode(aoNamespaces[iNS].c_str());
1035 : }
1036 0 : catch (const TranscodingException &e)
1037 : {
1038 0 : CPLError(CE_Failure, CPLE_AppDefined, "TranscodingException: %s",
1039 0 : transcode(e.getMessage()).c_str());
1040 0 : return false;
1041 : }
1042 :
1043 330 : XSNamedMap<XSObject> *poMapElements = poModel->getComponentsByNamespace(
1044 : XSConstants::ELEMENT_DECLARATION, xmlNamespace);
1045 330 : bFoundElementsInFirstChoiceNamespaces =
1046 330 : poMapElements != nullptr && poMapElements->getLength() > 0;
1047 330 : XMLString::release(&xmlNamespace);
1048 : }
1049 179 : if (!bFoundElementsInFirstChoiceNamespaces)
1050 : {
1051 1 : CPLDebug("GMLAS", "Did not find element in 'first choice' namespaces. "
1052 : "Falling back to the namespaces they import");
1053 1 : aoNamespaces.clear();
1054 6 : for (const auto &oIterNS : oMapURIToPrefixWithEmpty)
1055 : {
1056 5 : const CPLString &osNSURI(oIterNS.first);
1057 12 : if (osNSURI == szXS_URI || osNSURI == szXSI_URI ||
1058 7 : osNSURI == szXMLNS_URI || osNSURI == szXLINK_URI ||
1059 6 : osNSURI == szWFS_URI || osNSURI == szWFS20_URI ||
1060 11 : osNSURI == szGML_URI || osNSURI == szGML32_URI)
1061 : {
1062 : // Skip all boring namespaces
1063 3 : continue;
1064 : }
1065 2 : aoNamespaces.push_back(osNSURI);
1066 : }
1067 : }
1068 :
1069 : // Find which elements must be top levels (because referenced several
1070 : // times)
1071 358 : std::set<XSElementDeclaration *> oSetVisitedEltDecl;
1072 358 : std::set<XSModelGroup *> oSetVisitedModelGroups;
1073 358 : std::vector<XSElementDeclaration *> oVectorEltsForTopClass;
1074 :
1075 : // For some reason, different XSElementDeclaration* can point to the
1076 : // same element, but we only want to instantiate a single class.
1077 : // This is the case for base:SpatialDataSet in
1078 : // inspire/geologicalunit/geologicalunit.gml test dataset.
1079 358 : std::set<CPLString> aoSetXPathEltsForTopClass;
1080 :
1081 : // Third and fourth passes
1082 537 : for (int iPass = 0; iPass < 2; ++iPass)
1083 : {
1084 1208 : for (size_t iNS = 0; iNS < aoNamespaces.size(); iNS++)
1085 : {
1086 850 : XMLCh *xmlNamespace = nullptr;
1087 : try
1088 : {
1089 850 : xmlNamespace = XMLString::transcode(aoNamespaces[iNS].c_str());
1090 : }
1091 0 : catch (const TranscodingException &e)
1092 : {
1093 0 : CPLError(CE_Failure, CPLE_AppDefined,
1094 : "TranscodingException: %s",
1095 0 : transcode(e.getMessage()).c_str());
1096 0 : return false;
1097 : }
1098 :
1099 : XSNamedMap<XSObject> *poMapElements =
1100 850 : poModel->getComponentsByNamespace(
1101 : XSConstants::ELEMENT_DECLARATION, xmlNamespace);
1102 :
1103 6700 : for (XMLSize_t i = 0;
1104 6700 : poMapElements != nullptr && i < poMapElements->getLength();
1105 : i++)
1106 : {
1107 : XSElementDeclaration *poEltDecl =
1108 : reinterpret_cast<XSElementDeclaration *>(
1109 5850 : poMapElements->item(i));
1110 5850 : XSComplexTypeDefinition *poCT = IsEltCompatibleOfFC(poEltDecl);
1111 5850 : if (!poEltDecl->getAbstract() && poCT != nullptr)
1112 : {
1113 : CPLString osXPath(
1114 9584 : MakeXPath(transcode(poEltDecl->getNamespace()),
1115 14376 : transcode(poEltDecl->getName())));
1116 4792 : if (!IsIgnoredXPath(osXPath))
1117 : {
1118 5782 : if (bFoundGMLFeature && m_bInstantiateGMLFeaturesOnly &&
1119 994 : !DerivesFromGMLFeature(poEltDecl))
1120 : {
1121 : // Do nothing
1122 : }
1123 4044 : else if (iPass == 0)
1124 : {
1125 : #ifdef DEBUG_VERBOSE
1126 : CPLDebug(
1127 : "GMLAS",
1128 : "%s (%s) must be exposed as "
1129 : "top-level (is top level in imported schemas)",
1130 : osXPath.c_str(),
1131 : transcode(
1132 : poEltDecl->getTypeDefinition()->getName())
1133 : .c_str());
1134 : #endif
1135 2022 : oSetVisitedEltDecl.insert(poEltDecl);
1136 2022 : if (aoSetXPathEltsForTopClass.find(osXPath) ==
1137 4044 : aoSetXPathEltsForTopClass.end())
1138 : {
1139 2022 : m_oSetEltsForTopClass.insert(poEltDecl);
1140 2022 : oVectorEltsForTopClass.push_back(poEltDecl);
1141 : aoSetXPathEltsForTopClass.insert(
1142 2022 : std::move(osXPath));
1143 : }
1144 : }
1145 : else
1146 : {
1147 2022 : bool bSimpleEnoughOut = true;
1148 2022 : int nSubCountSubEltOut = 0;
1149 2022 : auto poParticle = poCT->getParticle();
1150 2022 : if (poParticle)
1151 : {
1152 2021 : CPL_IGNORE_RET_VAL(
1153 2021 : FindElementsWithMustBeToLevel(
1154 : osXPath,
1155 : poParticle->getModelGroupTerm(), 0,
1156 : oSetVisitedEltDecl,
1157 : oSetVisitedModelGroups,
1158 : oVectorEltsForTopClass,
1159 : aoSetXPathEltsForTopClass, poModel,
1160 : bSimpleEnoughOut, nSubCountSubEltOut));
1161 : }
1162 : }
1163 : }
1164 : }
1165 : }
1166 :
1167 850 : XMLString::release(&xmlNamespace);
1168 : }
1169 : }
1170 :
1171 : // Find ambiguous class names
1172 : {
1173 3501 : for (const auto &oIter : m_oSetEltsForTopClass)
1174 : {
1175 3322 : CPLString osName(transcode(oIter->getName()));
1176 3322 : m_oMapEltNamesToInstanceCount[osName]++;
1177 : }
1178 : }
1179 :
1180 : // Instantiate all needed typenames
1181 3500 : for (const auto &poEltDecl : oVectorEltsForTopClass)
1182 : {
1183 6644 : const CPLString osXPath(MakeXPath(transcode(poEltDecl->getNamespace()),
1184 6644 : transcode(poEltDecl->getName())));
1185 :
1186 3322 : bool bError = false;
1187 : bool bResolvedType =
1188 3322 : InstantiateClassFromEltDeclaration(poEltDecl, poModel, bError);
1189 3322 : if (bError)
1190 : {
1191 1 : return false;
1192 : }
1193 3321 : if (!bResolvedType)
1194 : {
1195 0 : CPLError(
1196 : CE_Failure, CPLE_AppDefined, "Couldn't resolve %s (%s)",
1197 : osXPath.c_str(),
1198 0 : transcode(poEltDecl->getTypeDefinition()->getName()).c_str());
1199 0 : return false;
1200 : }
1201 : }
1202 :
1203 178 : LaunderClassNames();
1204 :
1205 178 : return true;
1206 : }
1207 :
1208 : /************************************************************************/
1209 : /* GetAnnotationDoc() */
1210 : /************************************************************************/
1211 :
1212 417245 : static CPLString GetAnnotationDoc(const XSAnnotation *annotation)
1213 : {
1214 417245 : if (!annotation)
1215 229212 : return CPLString();
1216 376066 : CPLString osAnnot(transcode(annotation->getAnnotationString()));
1217 188033 : CPLXMLNode *psRoot = CPLParseXMLString(osAnnot);
1218 188033 : CPLStripXMLNamespace(psRoot, nullptr, TRUE);
1219 376066 : CPLString osDoc(CPLGetXMLValue(psRoot, "=annotation.documentation", ""));
1220 188033 : CPLDestroyXMLNode(psRoot);
1221 188033 : return osDoc.Trim();
1222 : }
1223 :
1224 : /************************************************************************/
1225 : /* GetAnnotationDoc() */
1226 : /************************************************************************/
1227 :
1228 192142 : static CPLString GetAnnotationDoc(const XSAnnotationList *annotationList)
1229 : {
1230 192142 : if (!annotationList)
1231 7086 : return CPLString();
1232 370112 : CPLString osRet;
1233 370112 : for (size_t i = 0; i < annotationList->size(); ++i)
1234 : {
1235 370112 : CPLString osDoc(GetAnnotationDoc(annotationList->elementAt(i)));
1236 185056 : if (!osDoc.empty())
1237 : {
1238 184946 : if (!osRet.empty())
1239 0 : osRet += "\n";
1240 184946 : osRet += osDoc;
1241 : }
1242 : }
1243 185056 : return osRet;
1244 : }
1245 :
1246 : /************************************************************************/
1247 : /* GetAnnotationDoc() */
1248 : /************************************************************************/
1249 :
1250 192142 : static CPLString GetAnnotationDoc(const XSElementDeclaration *poEltDecl)
1251 : {
1252 192142 : XSTypeDefinition *poTypeDef = poEltDecl->getTypeDefinition();
1253 192142 : CPLString osDoc = GetAnnotationDoc(poEltDecl->getAnnotation());
1254 192142 : XSAnnotationList *list = nullptr;
1255 207418 : while (poTypeDef != nullptr)
1256 : {
1257 207418 : if (poTypeDef->getTypeCategory() == XSTypeDefinition::COMPLEX_TYPE)
1258 : {
1259 56743 : XSComplexTypeDefinition *poCT =
1260 : reinterpret_cast<XSComplexTypeDefinition *>(poTypeDef);
1261 56743 : list = poCT->getAnnotations();
1262 : }
1263 150675 : else if (poTypeDef->getTypeCategory() == XSTypeDefinition::SIMPLE_TYPE)
1264 : {
1265 150675 : XSSimpleTypeDefinition *poST =
1266 : reinterpret_cast<XSSimpleTypeDefinition *>(poTypeDef);
1267 150675 : list = poST->getAnnotations();
1268 : }
1269 207418 : if (list != nullptr)
1270 185056 : break;
1271 22362 : XSTypeDefinition *poNewTypeDef = poTypeDef->getBaseType();
1272 22362 : if (poNewTypeDef == poTypeDef)
1273 7086 : break;
1274 15276 : poTypeDef = poNewTypeDef;
1275 : }
1276 384284 : CPLString osDoc2 = GetAnnotationDoc(list);
1277 192142 : if (!osDoc.empty() && !osDoc2.empty())
1278 : {
1279 883 : osDoc += "\n";
1280 883 : osDoc += osDoc2;
1281 : }
1282 191259 : else if (!osDoc2.empty())
1283 184063 : osDoc = std::move(osDoc2);
1284 384284 : return osDoc;
1285 : }
1286 :
1287 : /************************************************************************/
1288 : /* InstantiateClassFromEltDeclaration() */
1289 : /************************************************************************/
1290 :
1291 3322 : bool GMLASSchemaAnalyzer::InstantiateClassFromEltDeclaration(
1292 : XSElementDeclaration *poEltDecl, XSModel *poModel, bool &bError)
1293 : {
1294 3322 : bError = false;
1295 3322 : XSComplexTypeDefinition *poCT = IsEltCompatibleOfFC(poEltDecl);
1296 3322 : if (!poEltDecl->getAbstract() && poCT != nullptr)
1297 : {
1298 6644 : GMLASFeatureClass oClass;
1299 6644 : const CPLString osEltName(transcode(poEltDecl->getName()));
1300 : const CPLString osXPath(
1301 6644 : MakeXPath(transcode(poEltDecl->getNamespace()), osEltName));
1302 :
1303 3322 : if (IsIgnoredXPath(osXPath))
1304 : {
1305 : #ifdef DEBUG_VERBOSE
1306 : CPLDebug("GMLAS", "%s is in ignored xpaths", osXPath.c_str());
1307 : #endif
1308 0 : return false;
1309 : }
1310 :
1311 3322 : if (m_oMapEltNamesToInstanceCount[osEltName] > 1)
1312 : {
1313 90 : CPLString osLaunderedXPath(osXPath);
1314 45 : osLaunderedXPath.replaceAll(':', '_');
1315 45 : oClass.SetName(osLaunderedXPath);
1316 : }
1317 : else
1318 3277 : oClass.SetName(osEltName);
1319 :
1320 : #ifdef DEBUG_VERBOSE
1321 : CPLDebug("GMLAS", "Instantiating element %s", osXPath.c_str());
1322 : #endif
1323 3322 : oClass.SetXPath(osXPath);
1324 3322 : oClass.SetIsTopLevelElt(
1325 3322 : GetTopElementDeclarationFromXPath(osXPath, poModel) != nullptr);
1326 :
1327 6644 : std::set<XSModelGroup *> oSetVisitedModelGroups;
1328 :
1329 3322 : oClass.SetDocumentation(GetAnnotationDoc(poEltDecl));
1330 :
1331 : // might be NULL on swe:values for example
1332 3322 : if (poCT->getParticle() != nullptr)
1333 : {
1334 3283 : std::map<CPLString, int> oMapCountOccurrencesOfSameName;
1335 3283 : BuildMapCountOccurrencesOfSameName(
1336 : poCT->getParticle()->getModelGroupTerm(),
1337 : oMapCountOccurrencesOfSameName);
1338 :
1339 3283 : OGRwkbGeometryType eGeomType = wkbUnknown;
1340 3353 : if (IsGMLNamespace(transcode(poCT->getNamespace())) &&
1341 70 : (eGeomType = GetOGRGeometryType(poCT)) != wkbNone)
1342 : {
1343 2 : GMLASField oField;
1344 1 : oField.SetName("geometry");
1345 1 : oField.SetMinOccurs(1);
1346 1 : oField.SetMaxOccurs(1);
1347 1 : oField.SetType(GMLAS_FT_GEOMETRY, szFAKEXS_GEOMETRY);
1348 1 : oField.SetGeomType(eGeomType);
1349 1 : oField.SetXPath(osXPath + szMATCH_ALL);
1350 1 : oField.SetIncludeThisEltInBlob(true);
1351 :
1352 1 : oClass.AddField(oField);
1353 : }
1354 3282 : else if (!ExploreModelGroup(
1355 : poCT->getParticle()->getModelGroupTerm(),
1356 : poCT->getAttributeUses(), oClass, 0,
1357 : oSetVisitedModelGroups, poModel,
1358 : oMapCountOccurrencesOfSameName))
1359 : {
1360 1 : bError = true;
1361 1 : return false;
1362 : }
1363 : }
1364 : else
1365 : {
1366 : // TODO ?
1367 : }
1368 :
1369 3321 : if (!LaunderFieldNames(oClass))
1370 0 : return false;
1371 :
1372 3321 : m_aoClasses.push_back(std::move(oClass));
1373 3321 : return true;
1374 : }
1375 0 : return false;
1376 : }
1377 :
1378 : /************************************************************************/
1379 : /* SetFieldTypeAndWidthFromDefinition() */
1380 : /************************************************************************/
1381 :
1382 200972 : void GMLASSchemaAnalyzer::SetFieldTypeAndWidthFromDefinition(
1383 : XSSimpleTypeDefinition *poST, GMLASField &oField)
1384 : {
1385 200972 : int nMaxLength = 0;
1386 346268 : while (
1387 1094480 : poST->getBaseType() != poST &&
1388 547240 : poST->getBaseType()->getTypeCategory() ==
1389 1094480 : XSTypeDefinition::SIMPLE_TYPE &&
1390 543812 : !XMLString::equals(poST->getNamespace(), PSVIUni::fgNamespaceXmlSchema))
1391 : {
1392 : const XMLCh *maxLength =
1393 346268 : poST->getLexicalFacetValue(XSSimpleTypeDefinition::FACET_LENGTH);
1394 346268 : if (maxLength == nullptr)
1395 : {
1396 346159 : maxLength = poST->getLexicalFacetValue(
1397 : XSSimpleTypeDefinition::FACET_MAXLENGTH);
1398 : }
1399 346268 : if (maxLength != nullptr)
1400 168286 : nMaxLength = MAX(nMaxLength, atoi(transcode(maxLength)));
1401 346268 : poST = reinterpret_cast<XSSimpleTypeDefinition *>(poST->getBaseType());
1402 : }
1403 :
1404 200972 : if (XMLString::equals(poST->getNamespace(), PSVIUni::fgNamespaceXmlSchema))
1405 : {
1406 401944 : CPLString osType(transcode(poST->getName()));
1407 200972 : oField.SetType(GMLASField::GetTypeFromString(osType), osType);
1408 : }
1409 : else
1410 : {
1411 0 : CPLError(CE_Warning, CPLE_AppDefined, "Base type is not a xs: one ???");
1412 : }
1413 :
1414 200972 : oField.SetWidth(nMaxLength);
1415 200972 : }
1416 :
1417 : /************************************************************************/
1418 : /* IsSame() */
1419 : /* */
1420 : /* The objects returned by different PSVI API are not always the same */
1421 : /* so do content inspection to figure out if they are equivalent. */
1422 : /************************************************************************/
1423 :
1424 412 : bool GMLASSchemaAnalyzer::IsSame(const XSModelGroup *poModelGroup1,
1425 : const XSModelGroup *poModelGroup2)
1426 : {
1427 412 : if (poModelGroup1->getCompositor() != poModelGroup2->getCompositor())
1428 127 : return false;
1429 :
1430 285 : const XSParticleList *poParticleList1 = poModelGroup1->getParticles();
1431 285 : const XSParticleList *poParticleList2 = poModelGroup2->getParticles();
1432 285 : if (poParticleList1->size() != poParticleList2->size())
1433 112 : return false;
1434 :
1435 620 : for (size_t i = 0; i < poParticleList1->size(); ++i)
1436 : {
1437 472 : const XSParticle *poParticle1 = poParticleList1->elementAt(i);
1438 472 : const XSParticle *poParticle2 = poParticleList2->elementAt(i);
1439 941 : if (poParticle1->getTermType() != poParticle2->getTermType() ||
1440 938 : poParticle1->getMinOccurs() != poParticle2->getMinOccurs() ||
1441 1410 : poParticle1->getMaxOccurs() != poParticle2->getMaxOccurs() ||
1442 469 : poParticle1->getMaxOccursUnbounded() !=
1443 469 : poParticle2->getMaxOccursUnbounded())
1444 : {
1445 3 : return false;
1446 : }
1447 469 : switch (poParticle1->getTermType())
1448 : {
1449 0 : case XSParticle::TERM_EMPTY:
1450 0 : break;
1451 :
1452 465 : case XSParticle::TERM_ELEMENT:
1453 : {
1454 : const XSElementDeclaration *poElt1 =
1455 465 : const_cast<XSParticle *>(poParticle1)->getElementTerm();
1456 : const XSElementDeclaration *poElt2 =
1457 465 : const_cast<XSParticle *>(poParticle2)->getElementTerm();
1458 : // Pointer comparison works here
1459 465 : if (poElt1 != poElt2)
1460 22 : return false;
1461 443 : break;
1462 : }
1463 :
1464 4 : case XSParticle::TERM_MODELGROUP:
1465 : {
1466 : const XSModelGroup *psSubGroup1 =
1467 4 : const_cast<XSParticle *>(poParticle1)->getModelGroupTerm();
1468 : const XSModelGroup *psSubGroup2 =
1469 4 : const_cast<XSParticle *>(poParticle2)->getModelGroupTerm();
1470 4 : if (!IsSame(psSubGroup1, psSubGroup2))
1471 0 : return false;
1472 4 : break;
1473 : }
1474 :
1475 0 : case XSParticle::TERM_WILDCARD:
1476 : {
1477 : // TODO: check that pointer comparison works
1478 : const XSWildcard *psWildcard1 =
1479 0 : const_cast<XSParticle *>(poParticle1)->getWildcardTerm();
1480 : const XSWildcard *psWildcard2 =
1481 0 : const_cast<XSParticle *>(poParticle2)->getWildcardTerm();
1482 0 : if (psWildcard1 != psWildcard2)
1483 0 : return false;
1484 0 : break;
1485 : }
1486 :
1487 0 : default:
1488 : {
1489 0 : CPLAssert(FALSE);
1490 : return false;
1491 : }
1492 : }
1493 : }
1494 :
1495 148 : return true;
1496 : }
1497 :
1498 : /************************************************************************/
1499 : /* GetGroupName() */
1500 : /* */
1501 : /* The model group object returned when exploring a high level model */
1502 : /* group isn't the same object as the one returned by model group */
1503 : /* definitions and has no name. So we have to investigate the content */
1504 : /* of model groups to figure out if they are the same. */
1505 : /************************************************************************/
1506 :
1507 : XSModelGroupDefinition *
1508 1688 : GMLASSchemaAnalyzer::GetGroupDefinition(const XSModelGroup *poModelGroup)
1509 : {
1510 1952 : for (const auto &oIter : m_oMapModelGroupToMGD)
1511 : {
1512 408 : if (IsSame(poModelGroup, oIter.first))
1513 : {
1514 144 : return oIter.second;
1515 : }
1516 : }
1517 :
1518 1544 : return nullptr;
1519 : }
1520 :
1521 : /************************************************************************/
1522 : /* IsAnyType() */
1523 : /************************************************************************/
1524 :
1525 27570 : static bool IsAnyType(XSComplexTypeDefinition *poType)
1526 : {
1527 27570 : if (XMLString::equals(poType->getBaseType()->getNamespace(),
1528 74569 : PSVIUni::fgNamespaceXmlSchema) &&
1529 46999 : transcode(poType->getBaseType()->getName()) == szXS_ANY_TYPE)
1530 : {
1531 19429 : XSParticle *poParticle = poType->getParticle();
1532 19429 : if (poParticle != nullptr)
1533 : {
1534 19075 : XSModelGroup *poGroupTerm = poParticle->getModelGroupTerm();
1535 19075 : if (poGroupTerm != nullptr)
1536 : {
1537 19075 : XSParticleList *poParticles = poGroupTerm->getParticles();
1538 19075 : if (poParticles != nullptr)
1539 : {
1540 20945 : return poParticles->size() == 1 &&
1541 1870 : poParticles->elementAt(0)->getTermType() ==
1542 19075 : XSParticle::TERM_WILDCARD;
1543 : }
1544 : }
1545 : }
1546 354 : else if (poType->getDerivationMethod() ==
1547 : XSConstants::DERIVATION_EXTENSION)
1548 : {
1549 : // swe:values case
1550 0 : return true;
1551 : }
1552 : }
1553 8495 : return false;
1554 : }
1555 :
1556 : /************************************************************************/
1557 : /* SetFieldFromAttribute() */
1558 : /************************************************************************/
1559 :
1560 39357 : bool GMLASSchemaAnalyzer::SetFieldFromAttribute(GMLASField &oField,
1561 : XSAttributeUse *poAttr,
1562 : const CPLString &osXPathPrefix,
1563 : const CPLString &osNamePrefix)
1564 : {
1565 39357 : const XSAttributeDeclaration *poAttrDecl = poAttr->getAttrDeclaration();
1566 78714 : const CPLString osNS(transcode(poAttrDecl->getNamespace()));
1567 78714 : const CPLString osName(transcode(poAttrDecl->getName()));
1568 :
1569 39357 : if (osNamePrefix.empty())
1570 18640 : oField.SetName(osName);
1571 : else
1572 20717 : oField.SetName(osNamePrefix + "_" + osName);
1573 :
1574 39357 : XSSimpleTypeDefinition *poAttrType = poAttrDecl->getTypeDefinition();
1575 39357 : if (!poAttrType)
1576 : {
1577 1 : CPLError(CE_Failure, CPLE_AppDefined,
1578 : "Cannot get type definition for attribute %s",
1579 1 : oField.GetName().c_str());
1580 1 : return false;
1581 : }
1582 :
1583 39356 : SetFieldTypeAndWidthFromDefinition(poAttrType, oField);
1584 :
1585 39356 : oField.SetXPath(osXPathPrefix + "/@" + MakeXPath(osNS, osName));
1586 39356 : if (poAttr->getRequired())
1587 : {
1588 25921 : oField.SetNotNullable(true);
1589 : }
1590 39356 : oField.SetMinOccurs(oField.IsNotNullable() ? 1 : 0);
1591 39356 : oField.SetMaxOccurs(1);
1592 39356 : if (poAttr->getConstraintType() == XSConstants::VALUE_CONSTRAINT_FIXED)
1593 : {
1594 1105 : oField.SetFixedValue(transcode(poAttr->getConstraintValue()));
1595 : }
1596 38251 : else if (poAttr->getConstraintType() ==
1597 : XSConstants::VALUE_CONSTRAINT_DEFAULT)
1598 : {
1599 258 : oField.SetDefaultValue(transcode(poAttr->getConstraintValue()));
1600 : }
1601 :
1602 : const bool bIsList =
1603 39356 : (poAttrType->getVariety() == XSSimpleTypeDefinition::VARIETY_LIST);
1604 39356 : if (bIsList)
1605 : {
1606 1593 : SetFieldTypeAndWidthFromDefinition(poAttrType->getItemType(), oField);
1607 1593 : if (m_bUseArrays && IsCompatibleOfArray(oField.GetType()))
1608 : {
1609 1592 : oField.SetList(true);
1610 1592 : oField.SetArray(true);
1611 : }
1612 : else
1613 : {
1614 : // We should probably create an auxiliary table here, but this
1615 : // is too corner case for now...
1616 1 : oField.SetType(GMLAS_FT_STRING, szXS_STRING);
1617 : }
1618 : }
1619 :
1620 39356 : oField.SetDocumentation(GetAnnotationDoc(poAttrDecl->getAnnotation()));
1621 :
1622 39356 : return true;
1623 : }
1624 :
1625 : /************************************************************************/
1626 : /* GetConcreteImplementationTypes() */
1627 : /************************************************************************/
1628 :
1629 337879 : void GMLASSchemaAnalyzer::GetConcreteImplementationTypes(
1630 : XSElementDeclaration *poParentElt,
1631 : std::vector<XSElementDeclaration *> &apoImplEltList)
1632 : {
1633 337879 : const auto oIter = m_oMapParentEltToChildElt.find(poParentElt);
1634 337879 : if (oIter != m_oMapParentEltToChildElt.end())
1635 : {
1636 29706 : for (size_t j = 0; j < oIter->second.size(); j++)
1637 : {
1638 25227 : XSElementDeclaration *poSubElt = oIter->second[j];
1639 25227 : if (IsEltCompatibleOfFC(poSubElt))
1640 : {
1641 15329 : if (!poSubElt->getAbstract())
1642 : {
1643 12828 : apoImplEltList.push_back(poSubElt);
1644 : }
1645 : }
1646 25227 : GetConcreteImplementationTypes(poSubElt, apoImplEltList);
1647 : }
1648 : }
1649 337879 : }
1650 :
1651 : /************************************************************************/
1652 : /* GetConstraintChildrenElements() */
1653 : /************************************************************************/
1654 :
1655 : std::vector<XSElementDeclaration *>
1656 312648 : GMLASSchemaAnalyzer::GetConstraintChildrenElements(const CPLString &osFullXPath)
1657 : {
1658 :
1659 312648 : std::vector<XSElementDeclaration *> oVectorRes;
1660 625296 : CPLString osMatched;
1661 312648 : if (m_oChildrenElementsConstraintsXPathMatcher.MatchesRefXPath(osFullXPath,
1662 : osMatched))
1663 : {
1664 : const std::vector<CPLString> &oVector =
1665 6 : m_oMapChildrenElementsConstraints[osMatched];
1666 : const std::map<CPLString, CPLString> &oMapPrefixToURI =
1667 6 : m_oChildrenElementsConstraintsXPathMatcher.GetMapPrefixToURI();
1668 14 : for (size_t j = 0; j < oVector.size(); ++j)
1669 : {
1670 8 : const CPLString &osSubElt(oVector[j]);
1671 16 : CPLString osSubEltPrefix;
1672 16 : CPLString osSubEltURI;
1673 16 : CPLString osSubEltType(osSubElt);
1674 8 : size_t nPos = osSubElt.find(":");
1675 8 : if (nPos != std::string::npos)
1676 : {
1677 8 : osSubEltPrefix = osSubElt.substr(0, nPos);
1678 8 : osSubEltType = osSubElt.substr(nPos + 1);
1679 :
1680 8 : const auto oIter2 = oMapPrefixToURI.find(osSubEltPrefix);
1681 8 : if (oIter2 != oMapPrefixToURI.end())
1682 : {
1683 8 : osSubEltURI = oIter2->second;
1684 : }
1685 : else
1686 : {
1687 0 : CPLError(CE_Warning, CPLE_AppDefined,
1688 : "Cannot find prefix of type constraint %s",
1689 : osSubElt.c_str());
1690 : }
1691 : }
1692 :
1693 16 : const CPLString osSubEltXPath(MakeXPath(osSubEltURI, osSubEltType));
1694 8 : const auto oIter2 = m_oMapXPathToEltDecl.find(osSubEltXPath);
1695 8 : if (oIter2 != m_oMapXPathToEltDecl.end())
1696 : {
1697 8 : XSElementDeclaration *poSubElt = oIter2->second;
1698 8 : if (IsEltCompatibleOfFC(poSubElt))
1699 : {
1700 8 : oVectorRes.push_back(poSubElt);
1701 : }
1702 : }
1703 : else
1704 : {
1705 0 : CPLError(
1706 : CE_Warning, CPLE_AppDefined,
1707 : "Cannot find element declaration of type constraint %s",
1708 : osSubElt.c_str());
1709 : }
1710 : }
1711 : }
1712 625296 : return oVectorRes;
1713 : }
1714 :
1715 : /************************************************************************/
1716 : /* GetOGRGeometryType() */
1717 : /************************************************************************/
1718 :
1719 3519 : static OGRwkbGeometryType GetOGRGeometryType(XSTypeDefinition *poTypeDef)
1720 : {
1721 : const struct MyStruct
1722 : {
1723 : const char *pszName;
1724 : OGRwkbGeometryType eType;
1725 3519 : } asArray[] = {{"GeometryPropertyType", wkbUnknown},
1726 : {"PointPropertyType", wkbPoint},
1727 : {"BoundingShapeType", wkbPolygon},
1728 : {"PolygonPropertyType", wkbPolygon},
1729 : {"LineStringPropertyType", wkbLineString},
1730 : {"MultiPointPropertyType", wkbMultiPoint},
1731 : {"MultiPolygonPropertyType", wkbMultiPolygon},
1732 : {"MultiLineStringPropertyType", wkbMultiLineString},
1733 : {"MultiGeometryPropertyType", wkbGeometryCollection},
1734 : {"MultiCurvePropertyType", wkbMultiCurve},
1735 : {"MultiSurfacePropertyType", wkbMultiSurface},
1736 : {"MultiSolidPropertyType", wkbUnknown},
1737 : // GeometryArrayPropertyType ?
1738 : {"GeometricPrimitivePropertyType", wkbUnknown},
1739 : {"CurvePropertyType", wkbCurve},
1740 : {"SurfacePropertyType", wkbSurface},
1741 : // SurfaceArrayPropertyType ?
1742 : // AbstractRingPropertyType ?
1743 : // LinearRingPropertyType ?
1744 : {"CompositeCurvePropertyType", wkbCurve},
1745 : {"CompositeSurfacePropertyType", wkbSurface},
1746 : {"CompositeSolidPropertyType", wkbUnknown},
1747 : {"GeometricComplexPropertyType", wkbUnknown},
1748 : {"SolidPropertyType", wkbPolyhedralSurface}};
1749 :
1750 7038 : CPLString osName(transcode(poTypeDef->getName()));
1751 58041 : for (size_t i = 0; i < CPL_ARRAYSIZE(asArray); ++i)
1752 : {
1753 55827 : if (osName == asArray[i].pszName)
1754 1305 : return asArray[i].eType;
1755 : }
1756 2214 : return wkbNone;
1757 :
1758 : #if 0
1759 : <complexType name="CurveSegmentArrayPropertyType">
1760 : <complexType name="KnotPropertyType">
1761 : <complexType name="SurfacePatchArrayPropertyType">
1762 : <complexType name="RingPropertyType">
1763 : <complexType name="PolygonPatchArrayPropertyType">
1764 : <complexType name="TrianglePatchArrayPropertyType">
1765 : <complexType name="LineStringSegmentArrayPropertyType">
1766 : <complexType name="SolidArrayPropertyType">
1767 : #endif
1768 : }
1769 :
1770 : /************************************************************************/
1771 : /* GetOGRGeometryTypeFromGMLEltName() */
1772 : /************************************************************************/
1773 :
1774 : static OGRwkbGeometryType
1775 1971 : GetOGRGeometryTypeFromGMLEltName(const CPLString &osEltName)
1776 : {
1777 : const struct MyStruct
1778 : {
1779 : const char *pszName;
1780 : OGRwkbGeometryType eType;
1781 1971 : } asArray[] = {
1782 : {"Point", wkbPoint},
1783 : {"Polygon", wkbPolygon},
1784 : {"Envelope", wkbPolygon},
1785 : {"LineString", wkbLineString},
1786 : {"MultiPoint", wkbMultiPoint},
1787 : {"MultiPolygon", wkbMultiPolygon},
1788 : {"MultiLineString", wkbMultiLineString},
1789 : {"MultiGeometry", wkbGeometryCollection},
1790 : {"MultiCurve", wkbMultiCurve},
1791 : {"MultiSurface", wkbMultiSurface},
1792 : {"MultiSolid", wkbUnknown},
1793 : {"Curve", wkbCurve},
1794 : {"Surface", wkbSurface},
1795 : {"CompositeCurve", wkbCurve},
1796 : {"CompositeSurface", wkbSurface},
1797 : {"CompositeSolid", wkbUnknown},
1798 : {"GeometricComplex", wkbUnknown},
1799 : };
1800 :
1801 34622 : for (size_t i = 0; i < CPL_ARRAYSIZE(asArray); ++i)
1802 : {
1803 32705 : if (osEltName == asArray[i].pszName)
1804 54 : return asArray[i].eType;
1805 : }
1806 1917 : return wkbNone;
1807 : }
1808 :
1809 : /************************************************************************/
1810 : /* CreateNonNestedRelationship() */
1811 : /************************************************************************/
1812 :
1813 16254 : void GMLASSchemaAnalyzer::CreateNonNestedRelationship(
1814 : XSElementDeclaration *poElt,
1815 : std::vector<XSElementDeclaration *> &apoImplEltList,
1816 : GMLASFeatureClass &oClass, int nMaxOccurs, bool bEltNameWillNeedPrefix,
1817 : bool bForceJunctionTable, bool bCaseOfConstraintChildren)
1818 : {
1819 32508 : const CPLString osEltPrefix(GetPrefix(transcode(poElt->getNamespace())));
1820 32508 : const CPLString osEltName(transcode(poElt->getName()));
1821 : const CPLString osOnlyElementXPath(
1822 32508 : MakeXPath(transcode(poElt->getNamespace()), osEltName));
1823 32508 : const CPLString osElementXPath(oClass.GetXPath() + "/" +
1824 32508 : osOnlyElementXPath);
1825 :
1826 16254 : if (!poElt->getAbstract() && !bCaseOfConstraintChildren)
1827 : {
1828 15870 : apoImplEltList.insert(apoImplEltList.begin(), poElt);
1829 : }
1830 :
1831 32508 : std::set<CPLString> aoSetSubEltXPath;
1832 16254 : if (nMaxOccurs == 1 && !bForceJunctionTable)
1833 : {
1834 : // If the field isn't repeated, then we can link to each
1835 : // potential realization types with a field
1836 :
1837 21327 : for (size_t j = 0; j < apoImplEltList.size(); j++)
1838 : {
1839 14246 : XSElementDeclaration *poSubElt = apoImplEltList[j];
1840 14246 : const CPLString osSubEltName(transcode(poSubElt->getName()));
1841 : const CPLString osSubEltXPath(
1842 14246 : MakeXPath(transcode(poSubElt->getNamespace()), osSubEltName));
1843 :
1844 : // For AbstractFeature_SpatialDataSet_pkid in SpatialDataSet_member
1845 14246 : if (aoSetSubEltXPath.find(osSubEltXPath) != aoSetSubEltXPath.end())
1846 : {
1847 0 : continue;
1848 : }
1849 14246 : aoSetSubEltXPath.insert(osSubEltXPath);
1850 :
1851 28492 : const CPLString osRealFullXPath(oClass.GetXPath() + "/" +
1852 : ((bCaseOfConstraintChildren)
1853 28489 : ? osOnlyElementXPath + "/"
1854 56981 : : CPLString("")) +
1855 14246 : osSubEltXPath);
1856 :
1857 14246 : if (IsIgnoredXPath(osRealFullXPath))
1858 : {
1859 : #ifdef DEBUG_VERBOSE
1860 : CPLDebug("GMLAS", "%s is in ignored xpaths",
1861 : osRealFullXPath.c_str());
1862 : #endif
1863 91 : continue;
1864 : }
1865 :
1866 28310 : GMLASField oField;
1867 14155 : if (apoImplEltList.size() > 1 || bCaseOfConstraintChildren)
1868 : {
1869 7437 : if (m_oMapEltNamesToInstanceCount[osSubEltName] > 1)
1870 : {
1871 3098 : CPLString osLaunderedXPath(osSubEltXPath);
1872 3098 : osLaunderedXPath.replaceAll(':', '_');
1873 9294 : oField.SetName(((bEltNameWillNeedPrefix) ? osEltPrefix + "_"
1874 9294 : : CPLString()) +
1875 12392 : transcode(poElt->getName()) + "_" +
1876 6196 : osLaunderedXPath + "_pkid");
1877 : }
1878 : else
1879 : {
1880 13017 : oField.SetName(((bEltNameWillNeedPrefix) ? osEltPrefix + "_"
1881 13017 : : CPLString()) +
1882 17356 : transcode(poElt->getName()) + "_" +
1883 8678 : osSubEltName + "_pkid");
1884 : }
1885 : }
1886 : else
1887 : {
1888 6718 : oField.SetName(transcode(poElt->getName()) + "_pkid");
1889 : }
1890 14155 : oField.SetXPath(osRealFullXPath);
1891 14155 : oField.SetMinOccurs(0);
1892 14155 : oField.SetMaxOccurs(nMaxOccurs);
1893 14155 : oField.SetCategory(GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK);
1894 14155 : oField.SetRelatedClassXPath(osSubEltXPath);
1895 14155 : oField.SetType(GMLAS_FT_STRING, szXS_STRING);
1896 14155 : oClass.AddField(oField);
1897 7081 : }
1898 : }
1899 : else
1900 : {
1901 : // If the field is repeated, we need to use junction
1902 : // tables
1903 19030 : for (size_t j = 0; j < apoImplEltList.size(); j++)
1904 : {
1905 9857 : XSElementDeclaration *poSubElt = apoImplEltList[j];
1906 9857 : const CPLString osSubEltName(transcode(poSubElt->getName()));
1907 : const CPLString osSubEltXPath(
1908 9857 : MakeXPath(transcode(poSubElt->getNamespace()), osSubEltName));
1909 :
1910 : // For AbstractFeature_SpatialDataSet_pkid in SpatialDataSet_member
1911 9857 : if (aoSetSubEltXPath.find(osSubEltXPath) != aoSetSubEltXPath.end())
1912 : {
1913 0 : continue;
1914 : }
1915 9857 : aoSetSubEltXPath.insert(osSubEltXPath);
1916 :
1917 : // Instantiate a junction table
1918 19714 : GMLASFeatureClass oJunctionTable;
1919 :
1920 9857 : if (m_oMapEltNamesToInstanceCount[osSubEltName] > 1)
1921 : {
1922 80 : CPLString osLaunderedXPath(osSubEltXPath);
1923 80 : osLaunderedXPath.replaceAll(':', '_');
1924 240 : oJunctionTable.SetName(oClass.GetName() + "_" +
1925 320 : transcode(poElt->getName()) + "_" +
1926 : osLaunderedXPath);
1927 : }
1928 : else
1929 : {
1930 29331 : oJunctionTable.SetName(oClass.GetName() + "_" +
1931 39108 : transcode(poElt->getName()) + "_" +
1932 : osSubEltName);
1933 : }
1934 : // Create a fake XPath binding the parent xpath (to an abstract
1935 : // element) to the child element
1936 9857 : oJunctionTable.SetXPath(
1937 19714 : BuildJunctionTableXPath(osElementXPath, osSubEltXPath));
1938 9857 : oJunctionTable.SetParentXPath(oClass.GetXPath());
1939 9857 : oJunctionTable.SetChildXPath(osSubEltXPath);
1940 9857 : m_aoClasses.push_back(std::move(oJunctionTable));
1941 :
1942 : // Add an abstract field
1943 19714 : GMLASField oField;
1944 9857 : oField.SetName(
1945 19714 : ((bEltNameWillNeedPrefix) ? osEltPrefix + "_" : CPLString()) +
1946 19714 : osEltName + "_" + osSubEltName);
1947 29571 : oField.SetXPath(oClass.GetXPath() + "/" +
1948 : ((bCaseOfConstraintChildren)
1949 29570 : ? osOnlyElementXPath + "/"
1950 39427 : : CPLString("")) +
1951 : osSubEltXPath);
1952 9857 : oField.SetMinOccurs(0);
1953 9857 : oField.SetMaxOccurs(nMaxOccurs);
1954 9857 : oField.SetAbstractElementXPath(osElementXPath);
1955 9857 : oField.SetRelatedClassXPath(osSubEltXPath);
1956 9857 : oField.SetCategory(
1957 : GMLASField::PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE);
1958 9857 : oClass.AddField(oField);
1959 : }
1960 : }
1961 :
1962 : #if 0
1963 : GMLASField oField;
1964 : oField.SetName( transcode(poElt->getName()) );
1965 : oField.SetXPath( osElementXPath );
1966 : oField.SetMinOccurs( poParticle->getMinOccurs() );
1967 : oField.SetMaxOccurs( poParticle->getMaxOccursUnbounded() ?
1968 : MAXOCCURS_UNLIMITED : poParticle->getMaxOccurs() );
1969 :
1970 : for( size_t j = 0; j < apoImplEltList.size(); j++ )
1971 : {
1972 : XSElementDeclaration* poSubElt = apoImplEltList[j];
1973 : XSTypeDefinition* poSubEltType =
1974 : poSubElt->getTypeDefinition();
1975 : XSComplexTypeDefinition* poCT =
1976 : reinterpret_cast<XSComplexTypeDefinition*>(poSubEltType);
1977 :
1978 : GMLASFeatureClass oNestedClass;
1979 : oNestedClass.SetName( oClass.GetName() + "_" +
1980 : transcode(poSubElt->getName()) );
1981 : oNestedClass.SetXPath( oClass.GetXPath() + "/" +
1982 : MakeXPath(transcode(poSubElt->getNamespace()),
1983 : transcode(poSubElt->getName())) );
1984 :
1985 : std::set<XSModelGroup*>
1986 : oSetNewVisitedModelGroups(oSetVisitedModelGroups);
1987 : if( !ExploreModelGroup(
1988 : poCT->getParticle()->getModelGroupTerm(),
1989 : NULL,
1990 : oNestedClass,
1991 : nRecursionCounter + 1,
1992 : oSetNewVisitedModelGroups ) )
1993 : {
1994 : return false;
1995 : }
1996 :
1997 : oClass.AddNestedClass( oNestedClass );
1998 : }
1999 :
2000 : if( !apoImplEltList.empty() )
2001 : {
2002 : oField.SetAbstract(true);
2003 : }
2004 : else
2005 : {
2006 : oField.SetType( GMLAS_FT_ANYTYPE, "anyType" );
2007 : oField.SetXPath( oClass.GetXPath() + "/" + "*" );
2008 : oField.SetIncludeThisEltInBlob( true );
2009 : }
2010 : oClass.AddField( oField );
2011 : #endif
2012 16254 : }
2013 :
2014 : /************************************************************************/
2015 : /* IsIgnoredXPath() */
2016 : /************************************************************************/
2017 :
2018 376600 : bool GMLASSchemaAnalyzer::IsIgnoredXPath(const CPLString &osXPath)
2019 : {
2020 753200 : CPLString osIgnored;
2021 753200 : return m_oIgnoredXPathMatcher.MatchesRefXPath(osXPath, osIgnored);
2022 : }
2023 :
2024 : /************************************************************************/
2025 : /* FindElementsWithMustBeToLevel() */
2026 : /************************************************************************/
2027 :
2028 28624 : bool GMLASSchemaAnalyzer::FindElementsWithMustBeToLevel(
2029 : const CPLString &osParentXPath, XSModelGroup *poModelGroup,
2030 : int nRecursionCounter, std::set<XSElementDeclaration *> &oSetVisitedEltDecl,
2031 : std::set<XSModelGroup *> &oSetVisitedModelGroups,
2032 : std::vector<XSElementDeclaration *> &oVectorEltsForTopClass,
2033 : std::set<CPLString> &aoSetXPathEltsForTopClass, XSModel *poModel,
2034 : bool &bSimpleEnoughOut, int &nCountSubEltsOut)
2035 : {
2036 28624 : const bool bAlreadyVisitedMG = (oSetVisitedModelGroups.find(poModelGroup) !=
2037 28624 : oSetVisitedModelGroups.end());
2038 :
2039 28624 : oSetVisitedModelGroups.insert(poModelGroup);
2040 :
2041 28624 : if (nRecursionCounter == 100)
2042 : {
2043 : // Presumably an hostile schema
2044 0 : CPLError(CE_Failure, CPLE_AppDefined,
2045 : "Schema analysis failed due to too deeply nested model");
2046 0 : return false;
2047 : }
2048 :
2049 : {
2050 57248 : CPLString osIgnored;
2051 28624 : if (m_oDisabledFlattenedXPathMatcher.MatchesRefXPath(osParentXPath,
2052 : osIgnored))
2053 : {
2054 0 : bSimpleEnoughOut = false;
2055 : }
2056 : }
2057 :
2058 28624 : XSParticleList *poParticles = poModelGroup->getParticles();
2059 148848 : for (size_t i = 0; i < poParticles->size(); ++i)
2060 : {
2061 120224 : XSParticle *poParticle = poParticles->elementAt(i);
2062 :
2063 226544 : const bool bRepeatedParticle = poParticle->getMaxOccursUnbounded() ||
2064 106320 : poParticle->getMaxOccurs() > 1;
2065 :
2066 120224 : if (poParticle->getTermType() == XSParticle::TERM_ELEMENT)
2067 : {
2068 108186 : XSElementDeclaration *poElt = poParticle->getElementTerm();
2069 108186 : XSTypeDefinition *poTypeDef = poElt->getTypeDefinition();
2070 108186 : const CPLString osEltName(transcode(poElt->getName()));
2071 108186 : const CPLString osEltNS(transcode(poElt->getNamespace()));
2072 108186 : CPLString osXPath(MakeXPath(osEltNS, osEltName));
2073 216372 : const CPLString osFullXPath(osParentXPath + "/" + osXPath);
2074 :
2075 : #ifdef DEBUG_SUPER_VERBOSE
2076 : CPLDebug("GMLAS", "FindElementsWithMustBeToLevel: %s",
2077 : osFullXPath.c_str());
2078 : #endif
2079 :
2080 108186 : if (IsIgnoredXPath(osFullXPath))
2081 : {
2082 : #ifdef DEBUG_VERBOSE
2083 : CPLDebug("GMLAS", "%s is in ignored xpaths",
2084 : osFullXPath.c_str());
2085 : #endif
2086 96 : continue;
2087 : }
2088 :
2089 : // This could be refined to detect if the repeated element might not
2090 : // be simplifiable as an array
2091 108090 : if (bSimpleEnoughOut && bRepeatedParticle)
2092 : {
2093 : #ifdef DEBUG_VERBOSE
2094 : CPLDebug("GMLAS", "%s not inlinable because %s is repeated",
2095 : osParentXPath.c_str(), osXPath.c_str());
2096 : #endif
2097 4460 : bSimpleEnoughOut = false;
2098 : }
2099 :
2100 : // We don't want to inline
2101 : // sub-classes with hundereds of attributes
2102 108090 : nCountSubEltsOut++;
2103 :
2104 108090 : std::vector<XSElementDeclaration *> apoImplEltList;
2105 108090 : GetConcreteImplementationTypes(poElt, apoImplEltList);
2106 :
2107 : std::vector<XSElementDeclaration *> apoChildrenElements =
2108 108090 : GetConstraintChildrenElements(osFullXPath);
2109 :
2110 : // Special case for a GML geometry property
2111 109650 : if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
2112 1560 : GetOGRGeometryType(poTypeDef) != wkbNone)
2113 : {
2114 : // Do nothing
2115 : }
2116 108299 : else if (IsGMLNamespace(osEltNS) &&
2117 833 : GetOGRGeometryTypeFromGMLEltName(osEltName) != wkbNone)
2118 : {
2119 : // Do nothing
2120 : }
2121 : // Any GML abstract type
2122 107953 : else if (poElt->getAbstract() && IsGMLNamespace(osEltNS) &&
2123 92 : osEltName != "_Feature" &&
2124 107947 : osEltName != "AbstractFeature" &&
2125 39 : osEltName != "AbstractTimeObject")
2126 : {
2127 : // Do nothing
2128 : }
2129 : // Are there substitution groups for this element ?
2130 107403 : else if (!apoImplEltList.empty() || !apoChildrenElements.empty())
2131 : {
2132 348 : if (!apoChildrenElements.empty())
2133 : {
2134 3 : apoImplEltList = std::move(apoChildrenElements);
2135 : }
2136 345 : else if (!poElt->getAbstract())
2137 : {
2138 123 : apoImplEltList.insert(apoImplEltList.begin(), poElt);
2139 : }
2140 2384 : for (size_t j = 0; j < apoImplEltList.size(); j++)
2141 : {
2142 2036 : XSElementDeclaration *poSubElt = apoImplEltList[j];
2143 : const CPLString osSubEltXPath(
2144 4072 : MakeXPath(transcode(poSubElt->getNamespace()),
2145 4072 : transcode(poSubElt->getName())));
2146 :
2147 2036 : if (IsIgnoredXPath(osParentXPath + "/" + osSubEltXPath))
2148 : {
2149 : #ifdef DEBUG_VERBOSE
2150 : CPLDebug("GMLAS", "%s is in ignored xpaths",
2151 : (osParentXPath + "/" + osSubEltXPath).c_str());
2152 : #endif
2153 87 : continue;
2154 : }
2155 :
2156 : // Make sure we will instantiate the referenced element
2157 1949 : if (m_oSetEltsForTopClass.find(poSubElt) ==
2158 4065 : m_oSetEltsForTopClass.end() &&
2159 167 : aoSetXPathEltsForTopClass.find(osSubEltXPath) ==
2160 2116 : aoSetXPathEltsForTopClass.end())
2161 : {
2162 : #ifdef DEBUG_VERBOSE
2163 : CPLDebug(
2164 : "GMLAS",
2165 : "%s (%s) must be exposed as "
2166 : "top-level (%s of %s)",
2167 : osSubEltXPath.c_str(),
2168 : transcode(poSubElt->getTypeDefinition()->getName())
2169 : .c_str(),
2170 : apoChildrenElements.empty() ? "derived class"
2171 : : "child",
2172 : osParentXPath.c_str());
2173 : #endif
2174 :
2175 167 : oSetVisitedEltDecl.insert(poSubElt);
2176 167 : m_oSetEltsForTopClass.insert(poSubElt);
2177 167 : oVectorEltsForTopClass.push_back(poSubElt);
2178 167 : aoSetXPathEltsForTopClass.insert(osSubEltXPath);
2179 :
2180 : XSComplexTypeDefinition *poSubEltCT =
2181 167 : IsEltCompatibleOfFC(poSubElt);
2182 329 : if (!bAlreadyVisitedMG && poSubEltCT != nullptr &&
2183 162 : poSubEltCT->getParticle() != nullptr)
2184 : {
2185 162 : bool bSubSimpleEnoughOut = true;
2186 162 : int nSubCountSubElt = 0;
2187 162 : if (!FindElementsWithMustBeToLevel(
2188 : osSubEltXPath,
2189 : poSubEltCT->getParticle()
2190 : ->getModelGroupTerm(),
2191 : nRecursionCounter + 1, oSetVisitedEltDecl,
2192 : oSetVisitedModelGroups,
2193 : oVectorEltsForTopClass,
2194 : aoSetXPathEltsForTopClass, poModel,
2195 : bSubSimpleEnoughOut, nSubCountSubElt))
2196 : {
2197 0 : return false;
2198 : }
2199 : }
2200 : }
2201 : }
2202 : }
2203 :
2204 213903 : else if (!poElt->getAbstract() &&
2205 106848 : poTypeDef->getTypeCategory() ==
2206 : XSTypeDefinition::COMPLEX_TYPE)
2207 : {
2208 45405 : nCountSubEltsOut--;
2209 :
2210 45405 : XSComplexTypeDefinition *poEltCT = IsEltCompatibleOfFC(poElt);
2211 45405 : if (poEltCT)
2212 : {
2213 : // Might be a bit extreme, but for now we don't inline
2214 : // classes that have subclasses.
2215 33503 : if (bSimpleEnoughOut)
2216 : {
2217 : #ifdef DEBUG_VERBOSE
2218 : CPLDebug("GMLAS",
2219 : "%s not inlinable because %s field is complex",
2220 : osParentXPath.c_str(), osXPath.c_str());
2221 : #endif
2222 5702 : bSimpleEnoughOut = false;
2223 : }
2224 :
2225 33503 : if (oSetVisitedEltDecl.find(poElt) !=
2226 67006 : oSetVisitedEltDecl.end())
2227 : {
2228 15597 : if (m_oSetEltsForTopClass.find(poElt) ==
2229 20651 : m_oSetEltsForTopClass.end() &&
2230 5054 : m_oSetSimpleEnoughElts.find(poElt) ==
2231 20651 : m_oSetSimpleEnoughElts.end() &&
2232 2904 : aoSetXPathEltsForTopClass.find(osXPath) ==
2233 18501 : aoSetXPathEltsForTopClass.end())
2234 : {
2235 2258 : CPLString osIgnored;
2236 1129 : if (!m_oForcedFlattenedXPathMatcher.MatchesRefXPath(
2237 : osXPath, osIgnored))
2238 : {
2239 : #ifdef DEBUG_VERBOSE
2240 : CPLDebug("GMLAS",
2241 : "%s (%s) must be exposed as "
2242 : "top-level (multiple time referenced)",
2243 : osXPath.c_str(),
2244 : transcode(poTypeDef->getNamespace())
2245 : .c_str());
2246 : #endif
2247 1129 : m_oSetEltsForTopClass.insert(poElt);
2248 1129 : oVectorEltsForTopClass.push_back(poElt);
2249 : aoSetXPathEltsForTopClass.insert(
2250 1129 : std::move(osXPath));
2251 : }
2252 : }
2253 : }
2254 : else
2255 : {
2256 17906 : oSetVisitedEltDecl.insert(poElt);
2257 :
2258 35812 : if (!bAlreadyVisitedMG &&
2259 17906 : poEltCT->getParticle() != nullptr)
2260 : {
2261 17784 : int nSubCountSubElt = 0;
2262 :
2263 : // Process attributes
2264 : XSAttributeUseList *poAttrList =
2265 17784 : poEltCT->getAttributeUses();
2266 : const size_t nAttrListSize =
2267 17784 : (poAttrList != nullptr) ? poAttrList->size()
2268 17784 : : 0;
2269 20447 : for (size_t j = 0; j < nAttrListSize; ++j)
2270 : {
2271 : XSAttributeUse *poAttr =
2272 2663 : poAttrList->elementAt(j);
2273 2663 : GMLASField oField;
2274 2663 : if (!SetFieldFromAttribute(oField, poAttr,
2275 : osFullXPath))
2276 : {
2277 0 : return false;
2278 : }
2279 4696 : if (!IsIgnoredXPath(oField.GetXPath()) &&
2280 2033 : oField.GetFixedValue().empty())
2281 : {
2282 : #ifdef DEBUG_SUPER_VERBOSE
2283 : CPLDebug(
2284 : "GMLAS",
2285 : "FindElementsWithMustBeToLevel: %s",
2286 : oField.GetXPath().c_str());
2287 : #endif
2288 1909 : nSubCountSubElt++;
2289 : }
2290 : }
2291 :
2292 17784 : bool bSubSimpleEnoughOut = true;
2293 17784 : if (!FindElementsWithMustBeToLevel(
2294 : osFullXPath,
2295 : poEltCT->getParticle()->getModelGroupTerm(),
2296 : nRecursionCounter + 1, oSetVisitedEltDecl,
2297 : oSetVisitedModelGroups,
2298 : oVectorEltsForTopClass,
2299 : aoSetXPathEltsForTopClass, poModel,
2300 : bSubSimpleEnoughOut, nSubCountSubElt))
2301 : {
2302 0 : return false;
2303 : }
2304 17784 : if (bSubSimpleEnoughOut)
2305 : {
2306 : #ifdef DEBUG_VERBOSE
2307 : CPLDebug("GMLAS", "%s is inlinable: %d fields",
2308 : osXPath.c_str(), nSubCountSubElt);
2309 : #endif
2310 7760 : m_oSetSimpleEnoughElts.insert(poElt);
2311 :
2312 7760 : nCountSubEltsOut += nSubCountSubElt;
2313 : }
2314 10024 : else if (bSimpleEnoughOut)
2315 : {
2316 : #ifdef DEBUG_VERBOSE
2317 : CPLDebug("GMLAS",
2318 : "%s not inlinable because %s is not "
2319 : "inlinable",
2320 : osParentXPath.c_str(),
2321 : osXPath.c_str());
2322 : #endif
2323 0 : bSimpleEnoughOut = false;
2324 : }
2325 : }
2326 : }
2327 : }
2328 : else
2329 : {
2330 11902 : if (transcode(poElt->getName()) != szFEATURE_COLLECTION)
2331 : {
2332 11902 : poEltCT = reinterpret_cast<XSComplexTypeDefinition *>(
2333 : poTypeDef);
2334 : // Process attributes
2335 : XSAttributeUseList *poAttrList =
2336 11902 : poEltCT->getAttributeUses();
2337 : const size_t nAttrListSize =
2338 11902 : (poAttrList != nullptr) ? poAttrList->size() : 0;
2339 22693 : for (size_t j = 0;
2340 22693 : bSimpleEnoughOut && j < nAttrListSize; ++j)
2341 : {
2342 10791 : XSAttributeUse *poAttr = poAttrList->elementAt(j);
2343 10791 : GMLASField oField;
2344 10791 : if (!SetFieldFromAttribute(oField, poAttr,
2345 : osFullXPath))
2346 : {
2347 0 : return false;
2348 : }
2349 21558 : if (!IsIgnoredXPath(oField.GetXPath()) &&
2350 10767 : oField.GetFixedValue().empty())
2351 : {
2352 : #ifdef DEBUG_SUPER_VERBOSE
2353 : CPLDebug("GMLAS",
2354 : "FindElementsWithMustBeToLevel: %s",
2355 : oField.GetXPath().c_str());
2356 : #endif
2357 10760 : nCountSubEltsOut++;
2358 : }
2359 : }
2360 : }
2361 : }
2362 :
2363 45405 : CPLString osTargetElement;
2364 45405 : if (poElt->getAnnotation() != nullptr)
2365 : {
2366 : CPLString osAnnot(transcode(
2367 1948 : poElt->getAnnotation()->getAnnotationString()));
2368 :
2369 : #ifdef DEBUG_SUPER_VERBOSE
2370 : CPLDebug("GMLAS", "Annot: %s", osAnnot.c_str());
2371 : #endif
2372 974 : CPLXMLNode *psRoot = CPLParseXMLString(osAnnot);
2373 974 : CPLStripXMLNamespace(psRoot, nullptr, TRUE);
2374 : osTargetElement = CPLGetXMLValue(
2375 974 : psRoot, "=annotation.appinfo.targetElement", "");
2376 974 : CPLDestroyXMLNode(psRoot);
2377 : #ifdef DEBUG_VERBOSE
2378 : if (!osTargetElement.empty())
2379 : CPLDebug("GMLAS", "targetElement: %s",
2380 : osTargetElement.c_str());
2381 : #endif
2382 : }
2383 :
2384 : // If we have a element of type gml:ReferenceType that has
2385 : // a targetElement in its annotation.appinfo, then create
2386 : // a dedicated field to have cross-layer relationships.
2387 91530 : if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
2388 91530 : transcode(poTypeDef->getName()) == "ReferenceType" &&
2389 41 : !osTargetElement.empty())
2390 : {
2391 : XSElementDeclaration *poTargetElt =
2392 10 : GetTopElementDeclarationFromXPath(osTargetElement,
2393 10 : poModel);
2394 : // TODO: even for non abstract we should probably
2395 : // handle substitutions
2396 10 : if (poTargetElt != nullptr && !poTargetElt->getAbstract())
2397 : {
2398 : const CPLString osTargetEltXPath(
2399 8 : MakeXPath(transcode(poTargetElt->getNamespace()),
2400 8 : transcode(poTargetElt->getName())));
2401 :
2402 4 : if (IsIgnoredXPath(osTargetEltXPath))
2403 : {
2404 : #ifdef DEBUG_VERBOSE
2405 : CPLDebug("GMLAS", "%s is in ignored xpaths",
2406 : osTargetEltXPath.c_str());
2407 : #endif
2408 0 : continue;
2409 : }
2410 :
2411 : // Make sure we will instantiate the referenced
2412 : // element
2413 4 : if (m_oSetEltsForTopClass.find(poTargetElt) ==
2414 12 : m_oSetEltsForTopClass.end() &&
2415 4 : aoSetXPathEltsForTopClass.find(osTargetEltXPath) ==
2416 8 : aoSetXPathEltsForTopClass.end())
2417 : {
2418 : #ifdef DEBUG_VERBOSE
2419 : CPLDebug(
2420 : "GMLAS", "%d: Adding %s as (%s) needed type",
2421 : __LINE__, osTargetElement.c_str(),
2422 : transcode(
2423 : poTargetElt->getTypeDefinition()->getName())
2424 : .c_str());
2425 : #endif
2426 4 : oSetVisitedEltDecl.insert(poTargetElt);
2427 4 : m_oSetEltsForTopClass.insert(poTargetElt);
2428 4 : oVectorEltsForTopClass.push_back(poTargetElt);
2429 4 : aoSetXPathEltsForTopClass.insert(osTargetEltXPath);
2430 : }
2431 :
2432 : XSComplexTypeDefinition *poTargetEltCT =
2433 4 : IsEltCompatibleOfFC(poTargetElt);
2434 8 : if (!bAlreadyVisitedMG && poTargetEltCT &&
2435 4 : poTargetEltCT->getParticle() != nullptr)
2436 : {
2437 4 : bool bSubSimpleEnoughOut = true;
2438 4 : int nSubCountSubElt = 0;
2439 4 : if (!FindElementsWithMustBeToLevel(
2440 : osTargetEltXPath,
2441 : poTargetEltCT->getParticle()
2442 : ->getModelGroupTerm(),
2443 : nRecursionCounter + 1, oSetVisitedEltDecl,
2444 : oSetVisitedModelGroups,
2445 : oVectorEltsForTopClass,
2446 : aoSetXPathEltsForTopClass, poModel,
2447 : bSubSimpleEnoughOut, nSubCountSubElt))
2448 : {
2449 0 : return false;
2450 : }
2451 : }
2452 : }
2453 : }
2454 : }
2455 : }
2456 20907 : else if (!bAlreadyVisitedMG &&
2457 8869 : poParticle->getTermType() == XSParticle::TERM_MODELGROUP)
2458 : {
2459 : // This could be refined to detect if the repeated element might not
2460 : // be simplifiable as an array
2461 8653 : if (bSimpleEnoughOut && bRepeatedParticle)
2462 : {
2463 : #ifdef DEBUG_VERBOSE
2464 : CPLDebug(
2465 : "GMLAS",
2466 : "%s not inlinable because there is a repeated particle",
2467 : osParentXPath.c_str());
2468 : #endif
2469 460 : bSimpleEnoughOut = false;
2470 : }
2471 :
2472 8653 : XSModelGroup *psSubModelGroup = poParticle->getModelGroupTerm();
2473 8653 : if (!FindElementsWithMustBeToLevel(
2474 : osParentXPath, psSubModelGroup, nRecursionCounter + 1,
2475 : oSetVisitedEltDecl, oSetVisitedModelGroups,
2476 : oVectorEltsForTopClass, aoSetXPathEltsForTopClass, poModel,
2477 : bSimpleEnoughOut, nCountSubEltsOut))
2478 : {
2479 0 : return false;
2480 : }
2481 : }
2482 : else
2483 : {
2484 : // This could be refined to detect if the repeated element might not
2485 : // be simplifiable as an array
2486 3385 : if (bSimpleEnoughOut && bRepeatedParticle)
2487 : {
2488 : #ifdef DEBUG_VERBOSE
2489 : CPLDebug(
2490 : "GMLAS",
2491 : "%s not inlinable because there is a repeated particle",
2492 : osParentXPath.c_str());
2493 : #endif
2494 424 : bSimpleEnoughOut = false;
2495 : }
2496 : }
2497 : }
2498 :
2499 28624 : if (bSimpleEnoughOut && nCountSubEltsOut > m_nMaximumFieldsForFlattening)
2500 : {
2501 1574 : CPLString osIgnored;
2502 787 : if (!m_oForcedFlattenedXPathMatcher.MatchesRefXPath(osParentXPath,
2503 : osIgnored))
2504 : {
2505 : #ifdef DEBUG_VERBOSE
2506 : CPLDebug("GMLAS",
2507 : "%s not inlinable because it has more than %d fields",
2508 : osParentXPath.c_str(), m_nMaximumFieldsForFlattening);
2509 : #endif
2510 787 : bSimpleEnoughOut = false;
2511 : }
2512 : }
2513 :
2514 28624 : return true;
2515 : }
2516 :
2517 : /************************************************************************/
2518 : /* IsGMLNamespace() */
2519 : /************************************************************************/
2520 :
2521 704814 : bool GMLASSchemaAnalyzer::IsGMLNamespace(const CPLString &osURI)
2522 : {
2523 704814 : if (osURI.find(szGML_URI) == 0)
2524 8095 : return true;
2525 : // Below is mostly for unit tests were we use xmlns:gml="http://fake_gml"
2526 696719 : const auto oIter = m_oMapURIToPrefix.find(osURI);
2527 696719 : return oIter != m_oMapURIToPrefix.end() && oIter->second == szGML_PREFIX;
2528 : }
2529 :
2530 : /************************************************************************/
2531 : /* BuildMapCountOccurrencesOfSameName() */
2532 : /************************************************************************/
2533 :
2534 48339 : void GMLASSchemaAnalyzer::BuildMapCountOccurrencesOfSameName(
2535 : XSModelGroup *poModelGroup,
2536 : std::map<CPLString, int> &oMapCountOccurrencesOfSameName)
2537 : {
2538 48339 : XSParticleList *poParticles = poModelGroup->getParticles();
2539 271719 : for (size_t i = 0; i < poParticles->size(); ++i)
2540 : {
2541 223380 : XSParticle *poParticle = poParticles->elementAt(i);
2542 223380 : if (poParticle->getTermType() == XSParticle::TERM_ELEMENT)
2543 : {
2544 204930 : XSElementDeclaration *poElt = poParticle->getElementTerm();
2545 204930 : const CPLString osEltName(transcode(poElt->getName()));
2546 204930 : oMapCountOccurrencesOfSameName[osEltName]++;
2547 : }
2548 18450 : else if (poParticle->getTermType() == XSParticle::TERM_MODELGROUP)
2549 : {
2550 18257 : XSModelGroup *psSubModelGroup = poParticle->getModelGroupTerm();
2551 18257 : BuildMapCountOccurrencesOfSameName(psSubModelGroup,
2552 : oMapCountOccurrencesOfSameName);
2553 : }
2554 : }
2555 48339 : }
2556 :
2557 : /************************************************************************/
2558 : /* ComposeMinOccurs() */
2559 : /************************************************************************/
2560 :
2561 3975 : static int ComposeMinOccurs(int nVal1, int nVal2)
2562 : {
2563 3975 : return nVal1 * nVal2;
2564 : }
2565 :
2566 : /************************************************************************/
2567 : /* ComposeMaxOccurs() */
2568 : /************************************************************************/
2569 :
2570 3975 : static int ComposeMaxOccurs(int nVal1, int nVal2)
2571 : {
2572 3975 : if (nVal1 == MAXOCCURS_UNLIMITED || nVal2 == MAXOCCURS_UNLIMITED)
2573 3257 : return MAXOCCURS_UNLIMITED;
2574 718 : return nVal1 * nVal2;
2575 : }
2576 :
2577 : /************************************************************************/
2578 : /* ExploreModelGroup() */
2579 : /************************************************************************/
2580 :
2581 48342 : bool GMLASSchemaAnalyzer::ExploreModelGroup(
2582 : XSModelGroup *poModelGroup, XSAttributeUseList *poMainAttrList,
2583 : GMLASFeatureClass &oClass, int nRecursionCounter,
2584 : std::set<XSModelGroup *> &oSetVisitedModelGroups, XSModel *poModel,
2585 : const std::map<CPLString, int> &oMapCountOccurrencesOfSameName)
2586 : {
2587 48342 : if (oSetVisitedModelGroups.find(poModelGroup) !=
2588 96684 : oSetVisitedModelGroups.end())
2589 : {
2590 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s already visited",
2591 0 : oClass.GetXPath().c_str());
2592 0 : return false;
2593 : }
2594 48342 : oSetVisitedModelGroups.insert(poModelGroup);
2595 :
2596 48342 : if (nRecursionCounter == 100)
2597 : {
2598 : // Presumably an hostile schema
2599 0 : CPLError(CE_Failure, CPLE_AppDefined,
2600 : "Schema analysis failed due to too deeply nested model");
2601 0 : return false;
2602 : }
2603 :
2604 48342 : if (poMainAttrList != nullptr)
2605 : {
2606 385 : const size_t nMainAttrListSize = poMainAttrList->size();
2607 1760 : for (size_t j = 0; j < nMainAttrListSize; ++j)
2608 : {
2609 1376 : GMLASField oField;
2610 1376 : XSAttributeUse *poAttr = poMainAttrList->elementAt(j);
2611 1376 : if (!SetFieldFromAttribute(oField, poAttr, oClass.GetXPath()))
2612 : {
2613 1 : return false;
2614 : }
2615 :
2616 1375 : if (IsIgnoredXPath(oField.GetXPath()))
2617 : {
2618 : #ifdef DEBUG_VERBOSE
2619 : CPLDebug("GMLAS", "%s is in ignored xpaths",
2620 : oField.GetXPath().c_str());
2621 : #endif
2622 419 : if (!oField.GetFixedValue().empty() ||
2623 190 : !oField.GetDefaultValue().empty())
2624 : {
2625 41 : oField.SetIgnored();
2626 : }
2627 : else
2628 : {
2629 188 : continue;
2630 : }
2631 : }
2632 :
2633 1187 : oClass.AddField(oField);
2634 : }
2635 : }
2636 :
2637 48341 : XSParticleList *poParticles = poModelGroup->getParticles();
2638 :
2639 : // Special case for GML 3.1.1 where gml:metaDataProperty should be
2640 : // a sequence of gml:_Metadata but for some reason they have used
2641 : // a sequence of any.
2642 48341 : if (oClass.GetXPath() == "gml:metaDataProperty" &&
2643 0 : poModelGroup->getCompositor() == XSModelGroup::COMPOSITOR_SEQUENCE &&
2644 48341 : poParticles->size() == 1 &&
2645 0 : poParticles->elementAt(0)->getTermType() == XSParticle::TERM_WILDCARD)
2646 : {
2647 : XSElementDeclaration *poGMLMetadata =
2648 0 : GetTopElementDeclarationFromXPath("gml:_MetaData", poModel);
2649 0 : if (poGMLMetadata != nullptr)
2650 : {
2651 0 : std::vector<XSElementDeclaration *> apoImplEltList;
2652 0 : GetConcreteImplementationTypes(poGMLMetadata, apoImplEltList);
2653 0 : CreateNonNestedRelationship(poGMLMetadata, apoImplEltList, oClass,
2654 : 1,
2655 : false, // doesn't need prefix
2656 : true, // force junction table
2657 : false // regular case
2658 : );
2659 :
2660 0 : return true;
2661 : }
2662 : }
2663 :
2664 : const bool bIsChoice =
2665 48341 : (poModelGroup->getCompositor() == XSModelGroup::COMPOSITOR_CHOICE);
2666 48341 : int nGroup = 0;
2667 :
2668 269906 : for (size_t i = 0; i < poParticles->size(); ++i)
2669 : {
2670 223038 : XSParticle *poParticle = poParticles->elementAt(i);
2671 430884 : const bool bRepeatedParticle = poParticle->getMaxOccursUnbounded() ||
2672 207846 : poParticle->getMaxOccurs() > 1;
2673 223038 : const int nMinOccurs = static_cast<int>(poParticle->getMinOccurs());
2674 : const int nMaxOccurs =
2675 223038 : poParticle->getMaxOccursUnbounded()
2676 430884 : ? MAXOCCURS_UNLIMITED
2677 207846 : : static_cast<int>(poParticle->getMaxOccurs());
2678 :
2679 223038 : if (poParticle->getTermType() == XSParticle::TERM_ELEMENT)
2680 : {
2681 204658 : XSElementDeclaration *poElt = poParticle->getElementTerm();
2682 204658 : const CPLString osEltName(transcode(poElt->getName()));
2683 :
2684 204658 : const auto oIter = oMapCountOccurrencesOfSameName.find(osEltName);
2685 : const bool bEltNameWillNeedPrefix =
2686 409316 : oIter != oMapCountOccurrencesOfSameName.end() &&
2687 204658 : oIter->second > 1;
2688 204658 : const CPLString osEltNS(transcode(poElt->getNamespace()));
2689 : const CPLString osPrefixedEltName((bEltNameWillNeedPrefix
2690 409272 : ? GetPrefix(osEltNS) + "_"
2691 818544 : : CPLString()) +
2692 204658 : osEltName);
2693 204658 : const CPLString osOnlyElementXPath(MakeXPath(osEltNS, osEltName));
2694 409316 : const CPLString osElementXPath(oClass.GetXPath() + "/" +
2695 204658 : osOnlyElementXPath);
2696 : #ifdef DEBUG_VERBOSE
2697 : CPLDebug("GMLAS", "Iterating through %s", osElementXPath.c_str());
2698 : #endif
2699 :
2700 204658 : if (IsIgnoredXPath(osElementXPath))
2701 : {
2702 : #ifdef DEBUG_VERBOSE
2703 : CPLDebug("GMLAS", "%s is in ignored xpaths",
2704 : osElementXPath.c_str());
2705 : #endif
2706 100 : continue;
2707 : }
2708 :
2709 204558 : CPLString osTargetElement;
2710 204558 : if (poElt->getAnnotation() != nullptr)
2711 : {
2712 : CPLString osAnnot(
2713 2820 : transcode(poElt->getAnnotation()->getAnnotationString()));
2714 :
2715 : #ifdef DEBUG_SUPER_VERBOSE
2716 : CPLDebug("GMLAS", "Annot: %s", osAnnot.c_str());
2717 : #endif
2718 1410 : CPLXMLNode *psRoot = CPLParseXMLString(osAnnot);
2719 1410 : CPLStripXMLNamespace(psRoot, nullptr, TRUE);
2720 : osTargetElement = CPLGetXMLValue(
2721 1410 : psRoot, "=annotation.appinfo.targetElement", "");
2722 1410 : CPLDestroyXMLNode(psRoot);
2723 : #ifdef DEBUG_VERBOSE
2724 : if (!osTargetElement.empty())
2725 : CPLDebug("GMLAS", "targetElement: %s",
2726 : osTargetElement.c_str());
2727 : #endif
2728 : }
2729 :
2730 204558 : XSTypeDefinition *poTypeDef = poElt->getTypeDefinition();
2731 :
2732 204558 : std::vector<XSElementDeclaration *> apoImplEltList;
2733 204558 : GetConcreteImplementationTypes(poElt, apoImplEltList);
2734 :
2735 : std::vector<XSElementDeclaration *> apoChildrenElements =
2736 204558 : GetConstraintChildrenElements(osElementXPath);
2737 :
2738 : // Special case for a GML geometry property
2739 204558 : OGRwkbGeometryType eGeomType =
2740 : wkbNone; // to make Visual Studio happy
2741 204558 : CPL_IGNORE_RET_VAL(eGeomType); // to make cppcheck happy
2742 :
2743 204558 : if (!apoChildrenElements.empty())
2744 : {
2745 3 : CreateNonNestedRelationship(
2746 : poElt, apoChildrenElements, oClass, nMaxOccurs,
2747 : bEltNameWillNeedPrefix,
2748 : false, // do not force junction table
2749 : true // special case for children elements
2750 : );
2751 : }
2752 :
2753 206444 : else if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
2754 1889 : (eGeomType = GetOGRGeometryType(poTypeDef)) != wkbNone)
2755 : {
2756 1360 : GMLASField oField;
2757 680 : oField.SetName(osPrefixedEltName);
2758 680 : oField.SetMinOccurs(nMinOccurs);
2759 680 : oField.SetMaxOccurs(nMaxOccurs);
2760 680 : oField.SetType(GMLAS_FT_GEOMETRY, szFAKEXS_GEOMETRY);
2761 680 : if (nMaxOccurs > 1 || nMaxOccurs == MAXOCCURS_UNLIMITED)
2762 : {
2763 : // Repeated geometry property can happen in some schemas
2764 : // like
2765 : // inspire.ec.europa.eu/schemas/ge_gp/4.0/GeophysicsCore.xsd
2766 : // or
2767 : // http://ngwd-bdnes.cits.nrcan.gc.ca/service/gwml/schemas/2.1/gwml2-flow.xsd
2768 26 : oField.SetGeomType(wkbUnknown);
2769 26 : oField.SetArray(true);
2770 : }
2771 : else
2772 654 : oField.SetGeomType(eGeomType);
2773 680 : oField.SetXPath(osElementXPath);
2774 680 : oField.SetDocumentation(GetAnnotationDoc(poElt));
2775 :
2776 680 : oClass.AddField(oField);
2777 : }
2778 :
2779 205013 : else if (IsGMLNamespace(osEltNS) &&
2780 1138 : (eGeomType = GetOGRGeometryTypeFromGMLEltName(
2781 : osEltName)) != wkbNone)
2782 : {
2783 54 : GMLASField oField;
2784 27 : oField.SetName(osPrefixedEltName);
2785 27 : oField.SetMinOccurs(nMinOccurs);
2786 27 : oField.SetMaxOccurs(nMaxOccurs);
2787 :
2788 27 : oField.SetType(GMLAS_FT_GEOMETRY, szFAKEXS_GEOMETRY);
2789 27 : oField.SetGeomType(eGeomType);
2790 27 : oField.SetArray(nMaxOccurs > 1 ||
2791 : nMaxOccurs == MAXOCCURS_UNLIMITED);
2792 :
2793 27 : oField.SetXPath(osElementXPath);
2794 27 : oField.SetIncludeThisEltInBlob(true);
2795 27 : oField.SetDocumentation(GetAnnotationDoc(poElt));
2796 :
2797 27 : oClass.AddField(oField);
2798 : }
2799 :
2800 : // Any GML abstract type
2801 204689 : else if (poElt->getAbstract() && IsGMLNamespace(osEltNS) &&
2802 267 : osEltName != "_Feature" &&
2803 204680 : osEltName != "AbstractFeature" &&
2804 122 : osEltName != "AbstractTimeObject")
2805 : {
2806 206 : GMLASField oField;
2807 103 : oField.SetName(osPrefixedEltName);
2808 103 : oField.SetMinOccurs(nMinOccurs);
2809 103 : oField.SetMaxOccurs(nMaxOccurs);
2810 103 : if (osEltName == "AbstractGeometry")
2811 : {
2812 31 : oField.SetType(GMLAS_FT_GEOMETRY, szFAKEXS_GEOMETRY);
2813 31 : oField.SetGeomType(wkbUnknown);
2814 31 : oField.SetArray(nMaxOccurs > 1 ||
2815 : nMaxOccurs == MAXOCCURS_UNLIMITED);
2816 : }
2817 : else
2818 : {
2819 72 : oField.SetType(GMLAS_FT_ANYTYPE, szXS_ANY_TYPE);
2820 : }
2821 103 : oField.SetIncludeThisEltInBlob(true);
2822 103 : oField.SetDocumentation(GetAnnotationDoc(poElt));
2823 :
2824 1883 : for (size_t j = 0; j < apoImplEltList.size(); j++)
2825 : {
2826 1780 : XSElementDeclaration *poSubElt = apoImplEltList[j];
2827 1780 : oField.AddAlternateXPath(
2828 3560 : oClass.GetXPath() + "/" +
2829 3560 : MakeXPath(transcode(poSubElt->getNamespace()),
2830 3560 : transcode(poSubElt->getName())));
2831 : }
2832 :
2833 103 : oClass.AddField(oField);
2834 : }
2835 :
2836 : // Are there substitution groups for this element ?
2837 : // or is this element already identified as being a top-level one ?
2838 422668 : else if (!apoImplEltList.empty() ||
2839 203206 : (m_oSetEltsForTopClass.find(poElt) !=
2840 218923 : m_oSetEltsForTopClass.end() &&
2841 15717 : m_oSetSimpleEnoughElts.find(poElt) ==
2842 219462 : m_oSetSimpleEnoughElts.end()))
2843 : {
2844 16251 : CreateNonNestedRelationship(
2845 : poElt, apoImplEltList, oClass, nMaxOccurs,
2846 : bEltNameWillNeedPrefix,
2847 : false, // do not force junction table
2848 : false // regular case
2849 : );
2850 : }
2851 :
2852 : // Abstract element without realizations !
2853 187494 : else if (poElt->getAbstract())
2854 : {
2855 : // Do nothing with it since it cannot be instantiated
2856 : // in a valid way.
2857 219 : CPLDebug("GMLAS",
2858 : "Ignoring %s that is abstract without realizations",
2859 : osElementXPath.c_str());
2860 : }
2861 :
2862 : // Simple type like string, int, etc...
2863 187275 : else if (poTypeDef->getTypeCategory() ==
2864 : XSTypeDefinition::SIMPLE_TYPE)
2865 : {
2866 142308 : XSSimpleTypeDefinition *poST =
2867 : reinterpret_cast<XSSimpleTypeDefinition *>(poTypeDef);
2868 284616 : GMLASField oField;
2869 142308 : SetFieldTypeAndWidthFromDefinition(poST, oField);
2870 142308 : oField.SetMinOccurs((bIsChoice) ? 0 : nMinOccurs);
2871 142308 : oField.SetMaxOccurs(nMaxOccurs);
2872 142308 : oField.SetDocumentation(GetAnnotationDoc(poElt));
2873 :
2874 142308 : bool bNeedAuxTable = false;
2875 142308 : const bool bIsList = (poST->getVariety() ==
2876 142308 : XSSimpleTypeDefinition::VARIETY_LIST);
2877 142308 : if (bIsList)
2878 : {
2879 318 : SetFieldTypeAndWidthFromDefinition(poST->getItemType(),
2880 : oField);
2881 630 : if (bRepeatedParticle || !m_bUseArrays ||
2882 312 : !IsCompatibleOfArray(oField.GetType()))
2883 : {
2884 : // Really particular case. This is a workaround
2885 70 : oField.SetType(GMLAS_FT_STRING, szXS_STRING);
2886 : }
2887 : else
2888 : {
2889 248 : oField.SetList(true);
2890 248 : oField.SetArray(true);
2891 : }
2892 : }
2893 :
2894 144767 : if (m_bUseArrays && bRepeatedParticle &&
2895 2459 : IsCompatibleOfArray(oField.GetType()))
2896 : {
2897 2182 : oField.SetArray(true);
2898 : }
2899 140126 : else if (bRepeatedParticle)
2900 : {
2901 289 : bNeedAuxTable = true;
2902 : }
2903 142308 : if (bNeedAuxTable)
2904 : {
2905 578 : GMLASFeatureClass oNestedClass;
2906 289 : oNestedClass.SetName(oClass.GetName() + "_" +
2907 : osPrefixedEltName);
2908 289 : oNestedClass.SetXPath(osElementXPath);
2909 578 : GMLASField oUniqueField;
2910 289 : oUniqueField.SetName("value");
2911 289 : oUniqueField.SetMinOccurs(1);
2912 289 : oUniqueField.SetMaxOccurs(1);
2913 289 : oUniqueField.SetXPath(osElementXPath);
2914 289 : oUniqueField.SetType(oField.GetType(),
2915 289 : oField.GetTypeName());
2916 289 : oNestedClass.AddField(oUniqueField);
2917 289 : oNestedClass.SetDocumentation(GetAnnotationDoc(poElt));
2918 :
2919 289 : oClass.AddNestedClass(oNestedClass);
2920 :
2921 289 : oField.SetType(GMLAS_FT_STRING, "");
2922 289 : oField.SetName(osPrefixedEltName);
2923 289 : oField.SetXPath(osElementXPath);
2924 289 : oField.SetCategory(
2925 : GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
2926 289 : oField.SetRelatedClassXPath(oField.GetXPath());
2927 289 : oClass.AddField(oField);
2928 : }
2929 : else
2930 : {
2931 142019 : oField.SetName(osPrefixedEltName);
2932 142019 : oField.SetXPath(osElementXPath);
2933 142019 : if (!bIsChoice && nMinOccurs > 0 && !poElt->getNillable())
2934 : {
2935 42064 : oField.SetNotNullable(true);
2936 : }
2937 142019 : oClass.AddField(oField);
2938 :
2939 : // If the element has minOccurs=0 and is nillable, then we
2940 : // need an extra field to be able to distinguish between the
2941 : // case of the missing element or the element with
2942 : // xsi:nil="true"
2943 142117 : if (nMinOccurs == 0 && poElt->getNillable() &&
2944 98 : !m_bUseNullState)
2945 : {
2946 196 : GMLASField oFieldNil;
2947 98 : oFieldNil.SetName(osPrefixedEltName + "_" + szNIL);
2948 98 : oFieldNil.SetXPath(osElementXPath + "/" + szAT_XSI_NIL);
2949 98 : oFieldNil.SetType(GMLAS_FT_BOOLEAN, "boolean");
2950 98 : oFieldNil.SetMinOccurs(0);
2951 98 : oFieldNil.SetMaxOccurs(1);
2952 98 : oClass.AddField(oFieldNil);
2953 : }
2954 : }
2955 : }
2956 :
2957 : // Complex type (element with attributes, composed element, etc...)
2958 44967 : else if (poTypeDef->getTypeCategory() ==
2959 : XSTypeDefinition::COMPLEX_TYPE)
2960 : {
2961 44967 : XSComplexTypeDefinition *poEltCT =
2962 : reinterpret_cast<XSComplexTypeDefinition *>(poTypeDef);
2963 44967 : std::vector<GMLASField> aoFields;
2964 44967 : bool bNothingMoreToDo = false;
2965 44967 : std::vector<GMLASFeatureClass> aoNestedClasses;
2966 :
2967 : const int nMinOccursEltParticle =
2968 44967 : poEltCT->getParticle()
2969 72181 : ? static_cast<int>(
2970 27214 : poEltCT->getParticle()->getMinOccurs())
2971 44967 : : -1;
2972 : const int nMaxOccursEltParticle =
2973 44967 : poEltCT->getParticle()
2974 72181 : ? (poEltCT->getParticle()->getMaxOccursUnbounded()
2975 54230 : ? MAXOCCURS_UNLIMITED
2976 : : static_cast<int>(
2977 27016 : poEltCT->getParticle()->getMaxOccurs()))
2978 44967 : : -1;
2979 :
2980 44967 : const bool bEltRepeatedParticle =
2981 44967 : nMaxOccursEltParticle > 1 ||
2982 : nMaxOccursEltParticle == MAXOCCURS_UNLIMITED;
2983 44967 : const bool bMoveNestedClassToTop =
2984 44967 : !bRepeatedParticle && !bEltRepeatedParticle;
2985 :
2986 : // Process attributes
2987 44967 : XSAttributeUseList *poAttrList = poEltCT->getAttributeUses();
2988 : const size_t nAttrListSize =
2989 44967 : (poAttrList != nullptr) ? poAttrList->size() : 0;
2990 69494 : for (size_t j = 0; j < nAttrListSize; ++j)
2991 : {
2992 24527 : XSAttributeUse *poAttr = poAttrList->elementAt(j);
2993 24527 : GMLASField oField;
2994 : CPLString osNamePrefix(bMoveNestedClassToTop
2995 : ? osPrefixedEltName
2996 24527 : : CPLString());
2997 24527 : if (!SetFieldFromAttribute(oField, poAttr, osElementXPath,
2998 : osNamePrefix))
2999 : {
3000 0 : return false;
3001 : }
3002 24527 : if (nMinOccurs == 0 || bIsChoice)
3003 : {
3004 11435 : oField.SetMinOccurs(0);
3005 11435 : oField.SetNotNullable(false);
3006 : }
3007 :
3008 24527 : if (IsIgnoredXPath(oField.GetXPath()))
3009 : {
3010 : #ifdef DEBUG_VERBOSE
3011 : CPLDebug("GMLAS", "%s is in ignored xpaths",
3012 : oField.GetXPath().c_str());
3013 : #endif
3014 4501 : if (!oField.GetFixedValue().empty() ||
3015 2046 : !oField.GetDefaultValue().empty())
3016 : {
3017 410 : oField.SetIgnored();
3018 : }
3019 : else
3020 : {
3021 2045 : continue;
3022 : }
3023 : }
3024 :
3025 22482 : aoFields.push_back(std::move(oField));
3026 : }
3027 :
3028 : // Deal with anyAttributes (or any element that also imply it)
3029 44967 : XSWildcard *poAttrWildcard = poEltCT->getAttributeWildcard();
3030 44967 : if (poAttrWildcard != nullptr)
3031 : {
3032 714 : GMLASField oField;
3033 357 : oField.SetType(GMLASField::GetTypeFromString(szXS_STRING),
3034 : szFAKEXS_JSON_DICT);
3035 357 : if (!bMoveNestedClassToTop)
3036 : {
3037 95 : oField.SetName("anyAttributes");
3038 : }
3039 : else
3040 : {
3041 262 : oField.SetName(osPrefixedEltName + "_anyAttributes");
3042 : }
3043 357 : oField.SetXPath(osElementXPath + "/" + szAT_ANY_ATTR);
3044 357 : oField.SetDocumentation(
3045 714 : GetAnnotationDoc(poAttrWildcard->getAnnotation()));
3046 :
3047 357 : aoFields.push_back(std::move(oField));
3048 : }
3049 :
3050 44967 : XSSimpleTypeDefinition *poST = poEltCT->getSimpleType();
3051 44967 : if (poST != nullptr)
3052 : {
3053 : /* Case of an element, generally with attributes */
3054 :
3055 34794 : GMLASField oField;
3056 17397 : SetFieldTypeAndWidthFromDefinition(poST, oField);
3057 338 : if (bRepeatedParticle && nAttrListSize == 0 &&
3058 17781 : m_bUseArrays && IsCompatibleOfArray(oField.GetType()) &&
3059 46 : oField.GetCategory() !=
3060 : GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK)
3061 : {
3062 : /* We have a complex type, but no attributes, and */
3063 : /* compatible of arrays, so move it to top level! */
3064 46 : oField.SetName(osPrefixedEltName);
3065 46 : oField.SetArray(true);
3066 46 : oField.SetMinOccurs(nMinOccurs);
3067 46 : oField.SetMaxOccurs(nMaxOccurs);
3068 : }
3069 17351 : else if (bRepeatedParticle)
3070 : {
3071 292 : oField.SetName("value");
3072 292 : oField.SetMinOccurs(1);
3073 292 : oField.SetMaxOccurs(1);
3074 292 : oField.SetNotNullable(true);
3075 : }
3076 : else
3077 : {
3078 17059 : if (nMinOccurs == 0)
3079 : {
3080 10491 : for (size_t j = 0; j < aoFields.size(); j++)
3081 : {
3082 5487 : aoFields[j].SetMinOccurs(0);
3083 5487 : aoFields[j].SetNotNullable(false);
3084 : }
3085 : }
3086 :
3087 17059 : oField.SetName(osPrefixedEltName);
3088 17059 : oField.SetMinOccurs((bIsChoice) ? 0 : nMinOccurs);
3089 17059 : oField.SetMaxOccurs(nMaxOccurs);
3090 :
3091 : // If the element has minOccurs=0 and is nillable, then
3092 : // we need an extra field to be able to distinguish
3093 : // between the case of the missing element or the
3094 : // element with xsi:nil="true"
3095 17267 : if (nMinOccurs == 0 && poElt->getNillable() &&
3096 208 : !m_bUseNullState)
3097 : {
3098 416 : GMLASField oFieldNil;
3099 208 : oFieldNil.SetName(osPrefixedEltName + "_" + szNIL);
3100 208 : oFieldNil.SetXPath(osElementXPath + "/" +
3101 : szAT_XSI_NIL);
3102 208 : oFieldNil.SetType(GMLAS_FT_BOOLEAN, "boolean");
3103 208 : oFieldNil.SetMinOccurs(0);
3104 208 : oFieldNil.SetMaxOccurs(1);
3105 208 : aoFields.push_back(std::move(oFieldNil));
3106 : }
3107 : }
3108 17397 : oField.SetXPath(osElementXPath);
3109 17397 : oField.SetDocumentation(GetAnnotationDoc(poElt));
3110 :
3111 17397 : aoFields.push_back(oField);
3112 17397 : if (oField.IsArray())
3113 : {
3114 46 : oClass.AddField(oField);
3115 46 : bNothingMoreToDo = true;
3116 : }
3117 : }
3118 27570 : else if (IsAnyType(poEltCT))
3119 : {
3120 830 : GMLASField oField;
3121 415 : oField.SetType(GMLAS_FT_ANYTYPE, szXS_ANY_TYPE);
3122 415 : if (bRepeatedParticle)
3123 : {
3124 154 : oField.SetName("value");
3125 154 : oField.SetMinOccurs(1);
3126 154 : oField.SetMaxOccurs(1);
3127 154 : oField.SetNotNullable(true);
3128 : }
3129 : else
3130 : {
3131 261 : if (nMinOccurs == 0)
3132 : {
3133 216 : for (size_t j = 0; j < aoFields.size(); j++)
3134 : {
3135 48 : aoFields[j].SetMinOccurs(0);
3136 48 : aoFields[j].SetNotNullable(false);
3137 : }
3138 : }
3139 :
3140 261 : oField.SetName(osPrefixedEltName);
3141 261 : oField.SetMinOccurs(nMinOccurs);
3142 261 : oField.SetMaxOccurs(nMaxOccurs);
3143 : }
3144 415 : oField.SetXPath(osElementXPath);
3145 415 : oField.SetDocumentation(GetAnnotationDoc(poElt));
3146 :
3147 415 : aoFields.push_back(std::move(oField));
3148 : }
3149 :
3150 : // Is it an element that we already visited ? (cycle)
3151 53954 : else if (poEltCT->getParticle() != nullptr &&
3152 0 : oSetVisitedModelGroups.find(
3153 26799 : poEltCT->getParticle()->getModelGroupTerm()) !=
3154 53954 : oSetVisitedModelGroups.end())
3155 : {
3156 0 : CreateNonNestedRelationship(
3157 : poElt, apoImplEltList, oClass,
3158 : bMoveNestedClassToTop ? 1 : MAXOCCURS_UNLIMITED,
3159 : bEltNameWillNeedPrefix,
3160 : true, // force junction table
3161 : false // regular case
3162 : );
3163 :
3164 0 : bNothingMoreToDo = true;
3165 : }
3166 :
3167 : else
3168 : {
3169 27155 : GMLASFeatureClass oNestedClass;
3170 27155 : oNestedClass.SetName(oClass.GetName() + "_" +
3171 : osPrefixedEltName);
3172 27155 : oNestedClass.SetXPath(osElementXPath);
3173 27155 : oNestedClass.SetDocumentation(GetAnnotationDoc(poElt));
3174 :
3175 : // NULL can happen, for example for gml:ReferenceType
3176 : // that is an empty sequence with just attributes
3177 27155 : if (poEltCT->getParticle() != nullptr)
3178 : {
3179 : #ifdef DEBUG_VERBOSE
3180 : CPLDebug("GMLAS", "Exploring %s",
3181 : osElementXPath.c_str());
3182 : #endif
3183 : std::set<XSModelGroup *> oSetNewVisitedModelGroups(
3184 26799 : oSetVisitedModelGroups);
3185 :
3186 : std::map<CPLString, int>
3187 26799 : oMapCountOccurrencesOfSameNameSub;
3188 26799 : BuildMapCountOccurrencesOfSameName(
3189 : poEltCT->getParticle()->getModelGroupTerm(),
3190 : oMapCountOccurrencesOfSameNameSub);
3191 :
3192 26799 : if (!ExploreModelGroup(
3193 : poEltCT->getParticle()->getModelGroupTerm(),
3194 : nullptr, oNestedClass, nRecursionCounter + 1,
3195 : oSetNewVisitedModelGroups, poModel,
3196 : oMapCountOccurrencesOfSameNameSub))
3197 : {
3198 0 : return false;
3199 : }
3200 : }
3201 :
3202 : // If we have a element of type gml:ReferenceType that has
3203 : // a targetElement in its annotation.appinfo, then create
3204 : // a dedicated field to have cross-layer relationships.
3205 54507 : if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
3206 54507 : transcode(poTypeDef->getName()) == "ReferenceType" &&
3207 56 : !osTargetElement.empty())
3208 : {
3209 : XSElementDeclaration *poTargetElt =
3210 10 : GetTopElementDeclarationFromXPath(osTargetElement,
3211 : poModel);
3212 : // TODO: even for non abstract we should probably
3213 : // handle substitutions
3214 18 : if (poTargetElt != nullptr &&
3215 8 : !poTargetElt->getAbstract())
3216 : {
3217 4 : bool bHasRequiredId = false;
3218 : XSComplexTypeDefinition *poTargetEltCT =
3219 4 : IsEltCompatibleOfFC(poTargetElt);
3220 4 : if (poTargetEltCT)
3221 : {
3222 : XSAttributeUseList *poTargetEltAttrList =
3223 4 : poTargetEltCT->getAttributeUses();
3224 : const size_t nTEAttrListSize =
3225 : (poTargetEltAttrList != nullptr)
3226 4 : ? poTargetEltAttrList->size()
3227 4 : : 0;
3228 6 : for (size_t j = 0; j < nTEAttrListSize; ++j)
3229 : {
3230 : XSAttributeUse *poTEAttr =
3231 4 : poTargetEltAttrList->elementAt(j);
3232 : XSAttributeDeclaration *poTEAttrDecl =
3233 4 : poTEAttr->getAttrDeclaration();
3234 : XSSimpleTypeDefinition *poTEAttrType =
3235 4 : poTEAttrDecl->getTypeDefinition();
3236 8 : if (transcode(poTEAttrType->getName()) ==
3237 12 : szXS_ID &&
3238 4 : poTEAttr->getRequired())
3239 : {
3240 2 : bHasRequiredId = true;
3241 2 : break;
3242 : }
3243 : }
3244 : }
3245 4 : if (bHasRequiredId && !m_bAlwaysGenerateOGRId)
3246 : {
3247 : // If the element is nillable, then we
3248 : // need an extra field to be able to distinguish
3249 : // between the case of the missing element or
3250 : // the element with xsi:nil="true"
3251 2 : if (poElt->getNillable() && !m_bUseNullState)
3252 : {
3253 0 : GMLASField oFieldNil;
3254 0 : oFieldNil.SetName(osPrefixedEltName + "_" +
3255 : szNIL);
3256 0 : oFieldNil.SetXPath(osElementXPath + "/" +
3257 : szAT_XSI_NIL);
3258 0 : oFieldNil.SetType(GMLAS_FT_BOOLEAN,
3259 : "boolean");
3260 0 : oFieldNil.SetMinOccurs(0);
3261 0 : oFieldNil.SetMaxOccurs(1);
3262 0 : aoFields.push_back(std::move(oFieldNil));
3263 : }
3264 :
3265 4 : GMLASField oField;
3266 : // Fake xpath
3267 2 : oField.SetXPath(
3268 : GMLASField::
3269 4 : MakePKIDFieldXPathFromXLinkHrefXPath(
3270 4 : osElementXPath + "/" +
3271 : szAT_XLINK_HREF));
3272 2 : oField.SetName(osPrefixedEltName +
3273 : szPKID_SUFFIX);
3274 2 : oField.SetMinOccurs(0);
3275 2 : oField.SetMaxOccurs(1);
3276 2 : oField.SetType(GMLAS_FT_STRING, szXS_STRING);
3277 2 : oField.SetCategory(
3278 : GMLASField::
3279 : PATH_TO_CHILD_ELEMENT_WITH_LINK);
3280 2 : oField.SetRelatedClassXPath(osTargetElement);
3281 2 : aoFields.push_back(std::move(oField));
3282 : }
3283 : }
3284 10 : else if (poTargetElt != nullptr &&
3285 4 : poTargetElt->getAbstract())
3286 : {
3287 : // If the element is nillable, then we
3288 : // need an extra field to be able to distinguish
3289 : // between the case of the missing element or the
3290 : // element with xsi:nil="true"
3291 4 : if (poElt->getNillable() && !m_bUseNullState)
3292 : {
3293 4 : GMLASField oFieldNil;
3294 2 : oFieldNil.SetName(osPrefixedEltName + "_" +
3295 : szNIL);
3296 2 : oFieldNil.SetXPath(osElementXPath + "/" +
3297 : szAT_XSI_NIL);
3298 2 : oFieldNil.SetType(GMLAS_FT_BOOLEAN, "boolean");
3299 2 : oFieldNil.SetMinOccurs(0);
3300 2 : oFieldNil.SetMaxOccurs(1);
3301 2 : aoFields.push_back(std::move(oFieldNil));
3302 : }
3303 :
3304 : // e.g importing
3305 : // http://inspire.ec.europa.eu/schemas/ad/4.0
3306 : // references bu-base:AbstractConstruction, but
3307 : // sometimes there are no realization available for
3308 : // it, so no need to be verbose about that.
3309 : std::vector<XSElementDeclaration *>
3310 8 : apoImplTargetEltList;
3311 4 : GetConcreteImplementationTypes(
3312 : poTargetElt, apoImplTargetEltList);
3313 4 : if (!apoImplTargetEltList.empty())
3314 : {
3315 0 : CPLDebug("GMLAS",
3316 : "Not handled: targetElement %s of %s "
3317 : "is abstract but has substitutions",
3318 : osTargetElement.c_str(),
3319 : osElementXPath.c_str());
3320 : }
3321 : }
3322 : else
3323 : {
3324 : // This shouldn't happen with consistent schemas
3325 : // but as targetElement is in <annotation>, no
3326 : // general-purpose XSD validator can ensure this
3327 2 : CPLDebug("GMLAS",
3328 : "%s is a targetElement of %s, "
3329 : "but cannot be found",
3330 : osTargetElement.c_str(),
3331 : osElementXPath.c_str());
3332 : }
3333 : }
3334 :
3335 : // Can we move the nested class(es) one level up ?
3336 27155 : if (bMoveNestedClassToTop)
3337 : {
3338 : // Case of an element like
3339 : // <xs:element name="foo">
3340 : // <xs:complexType>
3341 : // <xs:sequence>
3342 :
3343 : const std::vector<GMLASField> &osNestedClassFields =
3344 23026 : oNestedClass.GetFields();
3345 413446 : for (size_t j = 0; j < osNestedClassFields.size(); j++)
3346 : {
3347 780840 : GMLASField oField(osNestedClassFields[j]);
3348 390420 : oField.SetName(osPrefixedEltName + "_" +
3349 390420 : oField.GetName());
3350 993100 : if (nMinOccurs == 0 ||
3351 602680 : (poEltCT->getParticle() != nullptr &&
3352 301340 : poEltCT->getParticle()->getMinOccurs() == 0))
3353 : {
3354 89296 : oField.SetMinOccurs(0);
3355 89296 : oField.SetNotNullable(false);
3356 : }
3357 390420 : aoFields.push_back(std::move(oField));
3358 : }
3359 :
3360 23026 : aoNestedClasses = oNestedClass.GetNestedClasses();
3361 : }
3362 : else
3363 : {
3364 : // Case of an element like
3365 : // <xs:element name="foo">
3366 : // <xs:complexType>
3367 : // <xs:sequence maxOccurs="unbounded">
3368 : // or
3369 : // <xs:element name="foo" maxOccurs="unbounded">
3370 : // <xs:complexType>
3371 : // <xs:sequence>
3372 : // or even
3373 : // <xs:element name="foo" maxOccurs="unbounded">
3374 : // <xs:complexType>
3375 : // <xs:sequence maxOccurs="unbounded">
3376 7763 : if (m_bUseArrays && nAttrListSize == 0 &&
3377 6627 : oNestedClass.GetNestedClasses().empty() &&
3378 3132 : oNestedClass.GetFields().size() == 1 &&
3379 147 : IsCompatibleOfArray(
3380 8397 : oNestedClass.GetFields()[0].GetType()) &&
3381 53 : oNestedClass.GetFields()[0].GetCategory() !=
3382 : GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK)
3383 : {
3384 : // In the case the sequence has a single element,
3385 : // compatible of array type, and no attribute and
3386 : // no nested classes, then add an array attribute
3387 : // at the top-level
3388 104 : GMLASField oField(oNestedClass.GetFields()[0]);
3389 52 : oField.SetName(osPrefixedEltName + "_" +
3390 52 : oField.GetName());
3391 102 : if (oField.GetMaxOccurs() == 1 &&
3392 102 : bEltRepeatedParticle &&
3393 50 : poEltCT->getParticle() != nullptr)
3394 : {
3395 50 : oField.SetMaxOccurs(nMaxOccursEltParticle);
3396 : }
3397 52 : oField.SetArray(true);
3398 52 : oClass.AddField(oField);
3399 : }
3400 : else
3401 : {
3402 4077 : if (!aoFields.empty() && bEltRepeatedParticle)
3403 : {
3404 : // We have attributes and the sequence is
3405 : // repeated
3406 : // <xs:element name="foo"
3407 : // maxOccurs="unbounded">
3408 : // <xs:complexType>
3409 : // <xs:sequence maxOccurs="unbounded">
3410 : // ...
3411 : // </xs:sequence>
3412 : // <xs:attribute .../>
3413 : // </xs:complexType>
3414 : // </xs:element>
3415 : // So we need to create an
3416 : // intermediate class to store them
3417 106 : GMLASFeatureClass oIntermediateNestedClass;
3418 53 : oIntermediateNestedClass.SetName(
3419 106 : oClass.GetName() + "_" + osPrefixedEltName);
3420 53 : oIntermediateNestedClass.SetXPath(
3421 : osElementXPath);
3422 :
3423 53 : oIntermediateNestedClass.PrependFields(
3424 : aoFields);
3425 :
3426 159 : oNestedClass.SetName(oClass.GetName() + "_" +
3427 106 : osPrefixedEltName +
3428 : "_sequence");
3429 106 : oNestedClass.SetXPath(oNestedClass.GetXPath() +
3430 106 : szEXTRA_SUFFIX +
3431 : "sequence");
3432 53 : oNestedClass.SetIsRepeatedSequence(true);
3433 :
3434 106 : GMLASField oField;
3435 53 : oField.SetXPath(osElementXPath);
3436 53 : oField.SetCategory(
3437 : GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
3438 53 : if (nMaxOccursEltParticle != 1)
3439 53 : oField.SetRepetitionOnSequence(true);
3440 53 : oField.SetMinOccurs(nMinOccursEltParticle);
3441 53 : oField.SetMaxOccurs(nMaxOccursEltParticle);
3442 53 : oField.SetRelatedClassXPath(
3443 : oNestedClass.GetXPath());
3444 53 : oIntermediateNestedClass.AddField(oField);
3445 :
3446 53 : oIntermediateNestedClass.AddNestedClass(
3447 : oNestedClass);
3448 :
3449 53 : oClass.AddNestedClass(oIntermediateNestedClass);
3450 : }
3451 : else
3452 : {
3453 4024 : oNestedClass.SetIsRepeatedSequence(
3454 : bEltRepeatedParticle);
3455 4024 : oNestedClass.PrependFields(aoFields);
3456 :
3457 4024 : oClass.AddNestedClass(oNestedClass);
3458 : }
3459 :
3460 8154 : GMLASField oField;
3461 4077 : oField.SetName(osPrefixedEltName);
3462 4077 : oField.SetXPath(osElementXPath);
3463 4077 : if (bRepeatedParticle)
3464 : {
3465 3976 : if (poEltCT->getParticle() != nullptr)
3466 : {
3467 3929 : oField.SetMinOccurs(ComposeMinOccurs(
3468 : nMinOccurs, nMinOccursEltParticle));
3469 3929 : oField.SetMaxOccurs(ComposeMaxOccurs(
3470 : nMaxOccurs, nMaxOccursEltParticle));
3471 : }
3472 : else
3473 : {
3474 47 : oField.SetMinOccurs(nMinOccurs);
3475 47 : oField.SetMaxOccurs(nMaxOccurs);
3476 : }
3477 : }
3478 101 : else if (poEltCT->getParticle() != nullptr)
3479 : {
3480 101 : if (nMaxOccursEltParticle != 1)
3481 101 : oField.SetRepetitionOnSequence(true);
3482 101 : oField.SetMinOccurs(nMinOccursEltParticle);
3483 101 : oField.SetMaxOccurs(nMaxOccursEltParticle);
3484 : }
3485 4077 : oField.SetCategory(
3486 : GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
3487 4077 : oField.SetRelatedClassXPath(oField.GetXPath());
3488 4077 : oClass.AddField(oField);
3489 : }
3490 :
3491 4129 : bNothingMoreToDo = true;
3492 : }
3493 : }
3494 :
3495 44967 : if (bNothingMoreToDo)
3496 : {
3497 : // Nothing to do
3498 : }
3499 40792 : else if (bRepeatedParticle)
3500 : {
3501 892 : GMLASFeatureClass oNestedClass;
3502 446 : oNestedClass.SetName(oClass.GetName() + "_" +
3503 : osPrefixedEltName);
3504 446 : oNestedClass.SetXPath(osElementXPath);
3505 446 : oNestedClass.AppendFields(aoFields);
3506 446 : oNestedClass.SetDocumentation(GetAnnotationDoc(poElt));
3507 446 : oClass.AddNestedClass(oNestedClass);
3508 :
3509 892 : GMLASField oField;
3510 446 : oField.SetName(osPrefixedEltName);
3511 446 : oField.SetXPath(osElementXPath);
3512 446 : oField.SetMinOccurs((bIsChoice) ? 0 : nMinOccurs);
3513 446 : oField.SetMaxOccurs(nMaxOccurs);
3514 446 : oField.SetCategory(
3515 : GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
3516 446 : oField.SetRelatedClassXPath(oField.GetXPath());
3517 446 : oClass.AddField(oField);
3518 : }
3519 : else
3520 : {
3521 40346 : oClass.AppendFields(aoFields);
3522 43363 : for (size_t j = 0; j < aoNestedClasses.size(); j++)
3523 : {
3524 3017 : oClass.AddNestedClass(aoNestedClasses[j]);
3525 : }
3526 : }
3527 : }
3528 : }
3529 18380 : else if (poParticle->getTermType() == XSParticle::TERM_MODELGROUP)
3530 : {
3531 18190 : XSModelGroup *psSubModelGroup = poParticle->getModelGroupTerm();
3532 18190 : if (bRepeatedParticle)
3533 : {
3534 1688 : GMLASFeatureClass oNestedClass;
3535 1688 : CPLString osGroupName;
3536 : XSModelGroupDefinition *psGroupDefinition =
3537 1688 : GetGroupDefinition(psSubModelGroup);
3538 1688 : if (psGroupDefinition != nullptr)
3539 : {
3540 144 : osGroupName = transcode(psGroupDefinition->getName());
3541 144 : oNestedClass.SetDocumentation(
3542 288 : GetAnnotationDoc(psGroupDefinition->getAnnotation()));
3543 : }
3544 : else
3545 : {
3546 : // Is it a <xs:choice maxOccurs=">1|unbounded"
3547 1544 : if (psSubModelGroup->getCompositor() ==
3548 : XSModelGroup::COMPOSITOR_CHOICE)
3549 : {
3550 : std::set<XSModelGroup *> oSetNewVisitedModelGroups(
3551 1544 : oSetVisitedModelGroups);
3552 1544 : GMLASFeatureClass oTmpClass;
3553 1544 : oTmpClass.SetName(oClass.GetName());
3554 1544 : oTmpClass.SetXPath(oClass.GetXPath());
3555 1544 : if (!ExploreModelGroup(psSubModelGroup, nullptr,
3556 : oTmpClass, nRecursionCounter + 1,
3557 : oSetNewVisitedModelGroups,
3558 : poModel,
3559 : oMapCountOccurrencesOfSameName))
3560 : {
3561 0 : return false;
3562 : }
3563 1544 : bool bHasArray = false;
3564 : std::vector<GMLASField> &oTmpFields =
3565 1544 : oTmpClass.GetFields();
3566 151246 : for (size_t j = 0; j < oTmpFields.size(); ++j)
3567 : {
3568 149773 : if (oTmpFields[j].IsArray())
3569 : {
3570 71 : bHasArray = true;
3571 71 : break;
3572 : }
3573 : }
3574 1544 : if (!bHasArray)
3575 : {
3576 150963 : for (size_t j = 0; j < oTmpFields.size(); ++j)
3577 : {
3578 149490 : oTmpFields[j].SetMayAppearOutOfOrder(true);
3579 149490 : oClass.AddField(oTmpFields[j]);
3580 : }
3581 1473 : return true;
3582 : }
3583 : }
3584 :
3585 71 : nGroup++;
3586 71 : osGroupName = CPLSPrintf("_group%d", nGroup);
3587 : }
3588 215 : oNestedClass.SetName(oClass.GetName() + "_" + osGroupName);
3589 215 : oNestedClass.SetIsGroup(true);
3590 215 : oNestedClass.SetIsRepeatedSequence(true);
3591 : // Caution: we will change it afterwards !
3592 215 : oNestedClass.SetXPath(oClass.GetXPath());
3593 : std::set<XSModelGroup *> oSetNewVisitedModelGroups(
3594 215 : oSetVisitedModelGroups);
3595 215 : if (!ExploreModelGroup(psSubModelGroup, nullptr, oNestedClass,
3596 : nRecursionCounter + 1,
3597 : oSetNewVisitedModelGroups, poModel,
3598 : oMapCountOccurrencesOfSameName))
3599 : {
3600 0 : return false;
3601 : }
3602 : // This is a nasty hack. We set a unique fake xpath *AFTER*
3603 : // processing the group, so that we can add a fake GROUP field
3604 : // pointing to the nested class
3605 215 : oNestedClass.SetXPath(oClass.GetXPath() + szEXTRA_SUFFIX +
3606 : osGroupName);
3607 :
3608 261 : if (m_bUseArrays && oNestedClass.GetFields().size() == 1 &&
3609 46 : IsCompatibleOfArray(oNestedClass.GetFields()[0].GetType()))
3610 : {
3611 92 : GMLASField oField(oNestedClass.GetFields()[0]);
3612 46 : oField.SetMinOccurs(
3613 : ComposeMinOccurs(oField.GetMinOccurs(), nMinOccurs));
3614 46 : oField.SetMaxOccurs(
3615 : ComposeMaxOccurs(oField.GetMaxOccurs(), nMaxOccurs));
3616 46 : oField.SetArray(true);
3617 46 : oClass.AddField(oField);
3618 : }
3619 : else
3620 : {
3621 169 : oClass.AddNestedClass(oNestedClass);
3622 :
3623 338 : GMLASField oField;
3624 169 : oField.SetCategory(GMLASField::GROUP);
3625 169 : oField.SetMinOccurs(nMinOccurs);
3626 169 : oField.SetMaxOccurs(nMaxOccurs);
3627 169 : oField.SetRelatedClassXPath(oNestedClass.GetXPath());
3628 169 : oClass.AddField(oField);
3629 : }
3630 : }
3631 : else
3632 : {
3633 : std::set<XSModelGroup *> oSetNewVisitedModelGroups(
3634 16502 : oSetVisitedModelGroups);
3635 16502 : if (!ExploreModelGroup(psSubModelGroup, nullptr, oClass,
3636 : nRecursionCounter + 1,
3637 : oSetNewVisitedModelGroups, poModel,
3638 : oMapCountOccurrencesOfSameName))
3639 : {
3640 0 : return false;
3641 : }
3642 : }
3643 : }
3644 190 : else if (poParticle->getTermType() == XSParticle::TERM_WILDCARD)
3645 : {
3646 : /* Special case for a layer that matches everything, as found */
3647 : /* in swe:extension */
3648 190 : XSWildcard *poWildcard = poParticle->getWildcardTerm();
3649 380 : GMLASField oField;
3650 190 : oField.SetXPath(oClass.GetXPath() + szMATCH_ALL);
3651 190 : oField.SetName("value");
3652 190 : oField.SetType(GMLAS_FT_ANYTYPE, szXS_ANY_TYPE);
3653 190 : oField.SetIncludeThisEltInBlob(true);
3654 190 : oField.SetMinOccurs(nMinOccurs);
3655 190 : oField.SetMaxOccurs(1);
3656 190 : oField.SetDocumentation(
3657 380 : GetAnnotationDoc(poWildcard->getAnnotation()));
3658 190 : oClass.AddField(oField);
3659 : }
3660 : }
3661 :
3662 46868 : return true;
3663 : }
|