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 174 : static XSModel *getGrammarPool(XMLGrammarPool *pool)
20 : {
21 : bool changed;
22 348 : 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 4536 : static bool IsCompatibleOfArray(GMLASFieldType eType)
37 : {
38 978 : return eType == GMLAS_FT_STRING || eType == GMLAS_FT_BOOLEAN ||
39 886 : eType == GMLAS_FT_SHORT || eType == GMLAS_FT_INT32 ||
40 601 : eType == GMLAS_FT_INT64 || eType == GMLAS_FT_FLOAT ||
41 5514 : eType == GMLAS_FT_DOUBLE || eType == GMLAS_FT_DECIMAL ||
42 4536 : 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 927 : GMLASPrefixMappingHander(
57 : std::map<CPLString, CPLString> &oMapURIToPrefix,
58 : const std::map<CPLString, CPLString> &oMapDocNSURIToPrefix,
59 : CPLString &osGMLVersionFound)
60 927 : : m_oMapURIToPrefix(oMapURIToPrefix),
61 : m_oMapDocNSURIToPrefix(oMapDocNSURIToPrefix),
62 927 : m_osGMLVersionFound(osGMLVersionFound)
63 : {
64 927 : }
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 814561 : void GMLASPrefixMappingHander::startElement(const XMLCh *const uri,
80 : const XMLCh *const localname,
81 : const XMLCh *const /*qname*/,
82 : const Attributes &attrs)
83 : {
84 814561 : if (!m_osGMLVersionFound.empty())
85 31324 : return;
86 :
87 1566470 : const CPLString osURI(transcode(uri));
88 1566470 : const CPLString osLocalname(transcode(localname));
89 783237 : if (osURI == szXS_URI && osLocalname == "schema")
90 : {
91 515 : bool bIsGML = false;
92 1030 : std::string osVersion;
93 2259 : for (unsigned int i = 0; i < attrs.getLength(); i++)
94 : {
95 3488 : const std::string osAttrLocalName(transcode(attrs.getLocalName(i)));
96 1744 : if (osAttrLocalName == "targetNamespace")
97 : {
98 482 : bIsGML = transcode(attrs.getValue(i)) == szGML_URI;
99 : }
100 1262 : else if (osAttrLocalName == "version")
101 : {
102 280 : osVersion = transcode(attrs.getValue(i));
103 : }
104 : }
105 515 : if (bIsGML && !osVersion.empty())
106 : {
107 0 : m_osGMLVersionFound = std::move(osVersion);
108 : }
109 : }
110 : }
111 :
112 : /************************************************************************/
113 : /* startPrefixMapping() */
114 : /************************************************************************/
115 :
116 2495 : void GMLASPrefixMappingHander::startPrefixMapping(const XMLCh *const prefix,
117 : const XMLCh *const uri)
118 : {
119 4990 : const CPLString osURI(transcode(uri));
120 4990 : CPLString osPrefix(transcode(prefix));
121 2495 : if (osPrefix.empty())
122 : {
123 264 : const auto oIter = m_oMapDocNSURIToPrefix.find(osURI);
124 264 : if (oIter != m_oMapDocNSURIToPrefix.end())
125 : {
126 11 : osPrefix = oIter->second;
127 : }
128 : }
129 2495 : if (!osPrefix.empty())
130 : {
131 2242 : const auto oIter = m_oMapURIToPrefix.find(osURI);
132 2242 : if (oIter == m_oMapURIToPrefix.end())
133 : {
134 557 : m_oMapURIToPrefix[osURI] = osPrefix;
135 557 : CPLDebug("GMLAS", "Registering prefix=%s for uri=%s",
136 : osPrefix.c_str(), osURI.c_str());
137 : }
138 1685 : else if (oIter->second != osPrefix)
139 : {
140 174 : CPLDebug("GMLAS",
141 : "Existing prefix=%s for uri=%s (new prefix %s not used)",
142 87 : oIter->second.c_str(), osURI.c_str(), osPrefix.c_str());
143 : }
144 : }
145 2495 : }
146 :
147 : /************************************************************************/
148 : /* CollectNamespacePrefixes() */
149 : /************************************************************************/
150 :
151 927 : 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 1854 : 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 927 : SAX2XMLReader *poReader = XMLReaderFactory::createXMLReader();
162 :
163 : GMLASPrefixMappingHander contentHandler(
164 1854 : oMapURIToPrefix, oMapDocNSURIToPrefix, osGMLVersionFound);
165 927 : poReader->setContentHandler(&contentHandler);
166 :
167 1854 : GMLASErrorHandler oErrorHandler;
168 927 : poReader->setErrorHandler(&oErrorHandler);
169 :
170 927 : poReader->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution, true);
171 :
172 1854 : std::string osErrorMsg;
173 : try
174 : {
175 927 : 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 927 : if (!osErrorMsg.empty())
197 : {
198 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrorMsg.c_str());
199 : }
200 927 : delete poReader;
201 927 : }
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 184 : GMLASAnalyzerEntityResolver(
214 : const CPLString &osBasePath,
215 : std::map<CPLString, CPLString> &oMapURIToPrefix,
216 : const std::map<CPLString, CPLString> &oMapDocNSURIToPrefix,
217 : GMLASXSDCache &oCache)
218 184 : : GMLASBaseEntityResolver(osBasePath, oCache),
219 : m_oMapURIToPrefix(oMapURIToPrefix),
220 184 : m_oMapDocNSURIToPrefix(oMapDocNSURIToPrefix)
221 : {
222 184 : }
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 927 : void GMLASAnalyzerEntityResolver::DoExtraSchemaProcessing(
234 : const CPLString &osFilename, const std::shared_ptr<VSIVirtualHandle> &fp)
235 : {
236 927 : CollectNamespacePrefixes(osFilename, fp, m_oMapURIToPrefix,
237 927 : m_oMapDocNSURIToPrefix, m_osGMLVersionFound);
238 927 : fp->Seek(0, SEEK_SET);
239 927 : }
240 :
241 : /************************************************************************/
242 : /* GMLASSchemaAnalyzer() */
243 : /************************************************************************/
244 :
245 188 : GMLASSchemaAnalyzer::GMLASSchemaAnalyzer(
246 : GMLASXPathMatcher &oIgnoredXPathMatcher,
247 : GMLASXPathMatcher &oChildrenElementsConstraintsXPathMatcher,
248 : const std::map<CPLString, std::vector<CPLString>>
249 : &oMapChildrenElementsConstraints,
250 : GMLASXPathMatcher &oForcedFlattenedXPathMatcher,
251 188 : 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 188 : m_bAlwaysGenerateOGRId(ALWAYS_GENERATE_OGR_ID_DEFAULT)
264 : {
265 : // A few hardcoded namespace uri->prefix mappings
266 188 : m_oMapURIToPrefix[szXMLNS_URI] = szXMLNS_PREFIX;
267 188 : m_oMapURIToPrefix[szXSI_URI] = szXSI_PREFIX;
268 188 : }
269 :
270 : /************************************************************************/
271 : /* GetPrefix() */
272 : /************************************************************************/
273 :
274 405125 : CPLString GMLASSchemaAnalyzer::GetPrefix(const CPLString &osNamespaceURI)
275 : {
276 405125 : if (osNamespaceURI.empty())
277 30129 : return "";
278 374996 : const auto oIter = m_oMapURIToPrefix.find(osNamespaceURI);
279 374996 : if (oIter != m_oMapURIToPrefix.end())
280 374993 : 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 389556 : CPLString GMLASSchemaAnalyzer::MakeXPath(const CPLString &osNamespaceURI,
315 : const CPLString &osName)
316 : {
317 779112 : const CPLString osPrefix(GetPrefix(osNamespaceURI));
318 389556 : if (osPrefix.empty())
319 30125 : return osName;
320 718862 : 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 6478 : bool GMLASSchemaAnalyzer::LaunderFieldNames(GMLASFeatureClass &oClass)
353 : {
354 6478 : 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 12956 : std::map<CPLString, std::list<int>> oMapNameToFieldIndex;
362 194667 : for (int i = 0; i < static_cast<int>(aoFields.size()); i++)
363 : {
364 188189 : if (aoFields[i].GetCategory() == GMLASField::REGULAR)
365 : {
366 168528 : oMapNameToFieldIndex[aoFields[i].GetName()].push_back(i);
367 : }
368 : }
369 :
370 12956 : std::set<CPLString> oSetDuplicates;
371 174913 : for (const auto &oIter : oMapNameToFieldIndex)
372 : {
373 : // Has it duplicates ?
374 168435 : const size_t nOccurrences = oIter.second.size();
375 168435 : if (nOccurrences > 1)
376 : {
377 48 : oSetDuplicates.insert(oIter.first);
378 : }
379 : }
380 :
381 6571 : 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 12956 : std::set<CPLString> oSetNames;
496 194667 : for (const auto &oField : aoFields)
497 : {
498 188189 : if (oField.GetCategory() == GMLASField::REGULAR)
499 : {
500 168528 : const auto &osName = oField.GetName();
501 168528 : CPLAssert(oSetNames.find(osName) == oSetNames.end());
502 168528 : oSetNames.insert(osName);
503 : }
504 : }
505 : }
506 : #endif
507 :
508 : // Now check if we must truncate names
509 6478 : if (m_nIdentifierMaxLength >= MIN_VALUE_OF_MAX_IDENTIFIER_LENGTH)
510 : {
511 11265 : for (size_t i = 0; i < aoFields.size(); i++)
512 : {
513 10071 : int nNameSize = static_cast<int>(aoFields[i].GetName().size());
514 : /* Somewhat arbitrary limitation to avoid performance issues in */
515 : /* OGRGMLASTruncateIdentifier() */
516 10071 : 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 10071 : if (nNameSize > m_nIdentifierMaxLength)
524 : {
525 6766 : aoFields[i].SetName(OGRGMLASTruncateIdentifier(
526 3383 : aoFields[i].GetName(), m_nIdentifierMaxLength));
527 : }
528 : }
529 : }
530 :
531 6478 : if (m_bPGIdentifierLaundering)
532 : {
533 189058 : for (size_t i = 0; i < aoFields.size(); i++)
534 : {
535 : char *pszLaundered =
536 182936 : OGRPGCommonLaunderName(aoFields[i].GetName(), "GMLAS", false);
537 182936 : aoFields[i].SetName(pszLaundered);
538 182936 : CPLFree(pszLaundered);
539 : }
540 : }
541 :
542 : // Detect duplicated field names
543 12956 : std::map<CPLString, std::vector<int>> oSetNames;
544 194667 : for (int i = 0; i < static_cast<int>(aoFields.size()); i++)
545 : {
546 188189 : if (aoFields[i].GetCategory() == GMLASField::REGULAR)
547 : {
548 337056 : CPLString osName(aoFields[i].GetName());
549 168528 : if (m_bCaseInsensitiveIdentifier)
550 168528 : osName.toupper();
551 168528 : oSetNames[osName].push_back(i);
552 : }
553 : }
554 :
555 : // Iterate over the unique names
556 174997 : for (const auto &oIter : oSetNames)
557 : {
558 : // Has it duplicates ?
559 168519 : const size_t nOccurrences = oIter.second.size();
560 168519 : 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 6478 : std::vector<GMLASFeatureClass> &aoNestedClasses = oClass.GetNestedClasses();
574 9818 : for (size_t i = 0; i < aoNestedClasses.size(); i++)
575 : {
576 3340 : if (!LaunderFieldNames(aoNestedClasses[i]))
577 0 : return false;
578 : }
579 6478 : return true;
580 : }
581 :
582 : /************************************************************************/
583 : /* CollectClassesReferences() */
584 : /************************************************************************/
585 :
586 15417 : void GMLASSchemaAnalyzer::CollectClassesReferences(
587 : GMLASFeatureClass &oClass, std::vector<GMLASFeatureClass *> &aoClasses)
588 : {
589 15417 : aoClasses.push_back(&oClass);
590 15417 : std::vector<GMLASFeatureClass> &aoNestedClasses = oClass.GetNestedClasses();
591 18757 : for (size_t i = 0; i < aoNestedClasses.size(); i++)
592 : {
593 3340 : CollectClassesReferences(aoNestedClasses[i], aoClasses);
594 : }
595 15417 : }
596 :
597 : /************************************************************************/
598 : /* LaunderClassNames() */
599 : /************************************************************************/
600 :
601 173 : void GMLASSchemaAnalyzer::LaunderClassNames()
602 : {
603 346 : std::vector<GMLASFeatureClass *> aoClasses;
604 12250 : for (size_t i = 0; i < m_aoClasses.size(); i++)
605 : {
606 12077 : CollectClassesReferences(m_aoClasses[i], aoClasses);
607 : }
608 :
609 173 : if (m_nIdentifierMaxLength >= MIN_VALUE_OF_MAX_IDENTIFIER_LENGTH)
610 : {
611 1514 : for (size_t i = 0; i < aoClasses.size(); i++)
612 : {
613 1425 : int nNameSize = static_cast<int>(aoClasses[i]->GetName().size());
614 1425 : if (nNameSize > m_nIdentifierMaxLength)
615 : {
616 788 : aoClasses[i]->SetName(OGRGMLASTruncateIdentifier(
617 394 : aoClasses[i]->GetName(), m_nIdentifierMaxLength));
618 : }
619 : }
620 : }
621 :
622 173 : if (m_bPGIdentifierLaundering)
623 : {
624 15194 : for (size_t i = 0; i < aoClasses.size(); i++)
625 : {
626 : char *pszLaundered =
627 15024 : OGRPGCommonLaunderName(aoClasses[i]->GetName(), "GMLAS", false);
628 15024 : aoClasses[i]->SetName(pszLaundered);
629 15024 : 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 346 : std::map<CPLString, std::vector<int>> oSetNames;
638 15590 : for (int i = 0; i < static_cast<int>(aoClasses.size()); i++)
639 : {
640 30834 : CPLString osName(aoClasses[i]->GetName());
641 15417 : if (m_bCaseInsensitiveIdentifier)
642 15417 : osName.toupper();
643 15417 : oSetNames[osName].push_back(i);
644 : }
645 :
646 : // Iterate over the unique names
647 15546 : for (const auto &oIter : oSetNames)
648 : {
649 : // Has it duplicates ?
650 15373 : const size_t nOccurrences = oIter.second.size();
651 15373 : if (nOccurrences > 1)
652 : {
653 88 : for (size_t i = 0; i < nOccurrences; i++)
654 : {
655 66 : GMLASFeatureClass *poClass = aoClasses[oIter.second[i]];
656 66 : poClass->SetName(OGRGMLASAddSerialNumber(
657 66 : poClass->GetName(), static_cast<int>(i + 1), nOccurrences,
658 : m_nIdentifierMaxLength));
659 : }
660 : }
661 : }
662 173 : }
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 447 : explicit GMLASUniquePtr(T *p) : m_p(p)
678 : {
679 447 : }
680 :
681 447 : ~GMLASUniquePtr()
682 : {
683 447 : delete m_p;
684 447 : }
685 :
686 2104 : T *operator->() const
687 : {
688 2104 : CPLAssert(m_p);
689 2104 : return m_p;
690 : }
691 :
692 700 : T *get() const
693 : {
694 700 : 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 3159 : GMLASSchemaAnalyzer::GetTopElementDeclarationFromXPath(const CPLString &osXPath,
711 : XSModel *poModel)
712 : {
713 3159 : const char *pszTypename = osXPath.c_str();
714 3159 : const char *pszColon = strrchr(pszTypename, ':');
715 3159 : XSElementDeclaration *poEltDecl = nullptr;
716 3159 : if (pszColon != nullptr)
717 : {
718 6248 : CPLString osNSPrefix = pszTypename;
719 3124 : osNSPrefix.resize(pszColon - pszTypename);
720 6248 : CPLString osName = pszColon + 1;
721 6248 : CPLString osNSURI;
722 :
723 10064 : for (const auto &oIterNS : m_oMapURIToPrefix)
724 : {
725 10064 : const CPLString &osIterNSURI(oIterNS.first);
726 10064 : const CPLString &osIterNSPrefix(oIterNS.second);
727 10064 : if (osNSPrefix == osIterNSPrefix)
728 : {
729 3124 : osNSURI = osIterNSURI;
730 3124 : break;
731 : }
732 : }
733 3124 : XMLCh *xmlNS = nullptr;
734 3124 : XMLCh *xmlName = nullptr;
735 : try
736 : {
737 3124 : xmlNS = XMLString::transcode(osNSURI);
738 3124 : xmlName = XMLString::transcode(osName);
739 3124 : 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 3124 : XMLString::release(&xmlNS);
747 3124 : 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 3159 : return poEltDecl;
764 : }
765 :
766 : /************************************************************************/
767 : /* IsEltCompatibleOfFC() */
768 : /************************************************************************/
769 :
770 : static XSComplexTypeDefinition *
771 62378 : IsEltCompatibleOfFC(XSElementDeclaration *poEltDecl)
772 : {
773 62378 : XSTypeDefinition *poTypeDef = poEltDecl->getTypeDefinition();
774 123739 : if (poTypeDef->getTypeCategory() == XSTypeDefinition::COMPLEX_TYPE &&
775 123739 : transcode(poEltDecl->getName()) != szFEATURE_COLLECTION)
776 : {
777 61252 : XSComplexTypeDefinition *poCT =
778 : reinterpret_cast<XSComplexTypeDefinition *>(poTypeDef);
779 : XSComplexTypeDefinition::CONTENT_TYPE eContentType(
780 61252 : poCT->getContentType());
781 61252 : if (eContentType == XSComplexTypeDefinition::CONTENTTYPE_ELEMENT ||
782 : eContentType == XSComplexTypeDefinition::CONTENTTYPE_MIXED)
783 : {
784 42595 : return poCT;
785 : }
786 : }
787 19783 : return nullptr;
788 : }
789 :
790 : /************************************************************************/
791 : /* DerivesFromGMLFeature() */
792 : /************************************************************************/
793 :
794 304 : bool GMLASSchemaAnalyzer::DerivesFromGMLFeature(XSElementDeclaration *poEltDecl)
795 : {
796 304 : XSElementDeclaration *poIter = poEltDecl;
797 : while (true)
798 : {
799 : XSElementDeclaration *poSubstGroup =
800 505 : poIter->getSubstitutionGroupAffiliation();
801 505 : if (poSubstGroup == nullptr)
802 193 : break;
803 312 : const CPLString osSubstNS(transcode(poSubstGroup->getNamespace()));
804 312 : const CPLString osSubstName(transcode(poSubstGroup->getName()));
805 312 : if (IsGMLNamespace(osSubstNS) && osSubstName == "_FeatureCollection")
806 : {
807 3 : return false;
808 : }
809 488 : if (IsGMLNamespace(osSubstNS) &&
810 179 : (osSubstName == "AbstractFeature" || osSubstName == "_Feature"))
811 : {
812 108 : return true;
813 : }
814 201 : poIter = poSubstGroup;
815 201 : }
816 193 : return false;
817 : }
818 :
819 : /************************************************************************/
820 : /* Analyze() */
821 : /************************************************************************/
822 :
823 184 : 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 368 : (new XMLGrammarPoolImpl(XMLPlatformUtils::fgMemoryManager)));
831 :
832 368 : std::vector<CPLString> aoNamespaces;
833 : GMLASAnalyzerEntityResolver oXSDEntityResolver(
834 368 : 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 184 : aoNamespaces.push_back("");
842 437 : for (size_t i = 0; i < aoXSDs.size(); i++)
843 : {
844 263 : const CPLString osURI(aoXSDs[i].first);
845 263 : const CPLString osXSDFilename(aoXSDs[i].second);
846 :
847 : GMLASUniquePtr<SAX2XMLReader> poParser(
848 : XMLReaderFactory::createXMLReader(XMLPlatformUtils::fgMemoryManager,
849 263 : poGrammarPool.get()));
850 :
851 : // Commonly useful configuration.
852 : //
853 263 : poParser->setFeature(XMLUni::fgSAX2CoreNameSpaces, true);
854 263 : poParser->setFeature(XMLUni::fgSAX2CoreNameSpacePrefixes, true);
855 263 : poParser->setFeature(XMLUni::fgSAX2CoreValidation, true);
856 :
857 : // Enable validation.
858 : //
859 263 : poParser->setFeature(XMLUni::fgXercesSchema, true);
860 :
861 : // coverity[unsafe_xml_parse_config]
862 263 : poParser->setFeature(XMLUni::fgXercesValidationErrorAsFatal, false);
863 :
864 : // Use the loaded grammar during parsing.
865 : //
866 263 : 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 263 : poParser->setFeature(XMLUni::fgXercesLoadSchema, false);
872 :
873 263 : poParser->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution,
874 263 : true);
875 :
876 263 : Grammar *poGrammar = nullptr;
877 263 : 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 506 : CPLString osGrammarURI(transcode(poGrammar->getTargetNamespace()));
891 253 : if (osGrammarURI.empty())
892 : {
893 28 : if (!osURI.empty())
894 1 : osGrammarURI = osURI;
895 : }
896 253 : if (!osGrammarURI.empty())
897 : {
898 : // Patch back the aoXSDs element in case we didn't know the
899 : // namespace URI initially
900 226 : if (osURI.empty())
901 27 : aoXSDs[i].first = osGrammarURI;
902 226 : aoNamespaces.push_back(osGrammarURI);
903 : }
904 : }
905 :
906 174 : m_osGMLVersionFound = oXSDEntityResolver.GetGMLVersionFound();
907 174 : m_oSetSchemaURLs = oXSDEntityResolver.GetSchemaURLS();
908 :
909 174 : m_oIgnoredXPathMatcher.SetDocumentMapURIToPrefix(m_oMapURIToPrefix);
910 174 : m_oChildrenElementsConstraintsXPathMatcher.SetDocumentMapURIToPrefix(
911 174 : m_oMapURIToPrefix);
912 174 : m_oForcedFlattenedXPathMatcher.SetDocumentMapURIToPrefix(m_oMapURIToPrefix);
913 174 : m_oDisabledFlattenedXPathMatcher.SetDocumentMapURIToPrefix(
914 174 : m_oMapURIToPrefix);
915 :
916 174 : XSModel *poModel = getGrammarPool(poGrammarPool.get());
917 174 : 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 174 : bool bFoundGMLFeature = false;
932 :
933 : // Second pass, in all namespaces, to figure out inheritance relationships
934 : // and group models that have names
935 348 : std::map<CPLString, CPLString> oMapURIToPrefixWithEmpty(m_oMapURIToPrefix);
936 174 : oMapURIToPrefixWithEmpty[""] = "";
937 1243 : for (const auto &oIterNS : oMapURIToPrefixWithEmpty)
938 : {
939 1069 : const CPLString &osNSURI(oIterNS.first);
940 2687 : if (osNSURI == szXS_URI || osNSURI == szXSI_URI ||
941 2687 : osNSURI == szXMLNS_URI || osNSURI == szXLINK_URI)
942 : {
943 526 : continue;
944 : }
945 :
946 543 : XMLCh *xmlNamespace = nullptr;
947 : try
948 : {
949 543 : 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 543 : poModel->getComponentsByNamespace(
960 : XSConstants::MODEL_GROUP_DEFINITION, xmlNamespace);
961 :
962 : // Remember group models that have names
963 1369 : for (XMLSize_t i = 0; poMapModelGroupDefinition != nullptr &&
964 611 : i < poMapModelGroupDefinition->getLength();
965 : i++)
966 : {
967 : XSModelGroupDefinition *modelGroupDefinition =
968 : reinterpret_cast<XSModelGroupDefinition *>(
969 215 : poMapModelGroupDefinition->item(i));
970 215 : m_oMapModelGroupToMGD[modelGroupDefinition->getModelGroup()] =
971 : modelGroupDefinition;
972 : }
973 :
974 543 : CPLDebug("GMLAS", "Discovering substitutions of %s (%s)",
975 : oIterNS.second.c_str(), osNSURI.c_str());
976 :
977 543 : XSNamedMap<XSObject> *poMapElements = poModel->getComponentsByNamespace(
978 : XSConstants::ELEMENT_DECLARATION, xmlNamespace);
979 :
980 4882 : for (XMLSize_t i = 0;
981 4882 : poMapElements != nullptr && i < poMapElements->getLength(); i++)
982 : {
983 : XSElementDeclaration *poEltDecl =
984 : reinterpret_cast<XSElementDeclaration *>(
985 4339 : poMapElements->item(i));
986 : XSElementDeclaration *poSubstGroup =
987 4339 : poEltDecl->getSubstitutionGroupAffiliation();
988 : const CPLString osEltXPath(
989 8678 : MakeXPath(transcode(poEltDecl->getNamespace()),
990 13017 : transcode(poEltDecl->getName())));
991 4339 : m_oMapXPathToEltDecl[osEltXPath] = poEltDecl;
992 4339 : if (poSubstGroup)
993 : {
994 1168 : 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 322 : if (!bFoundGMLFeature && m_bInstantiateGMLFeaturesOnly &&
1006 1708 : !IsGMLNamespace(osNSURI) &&
1007 218 : DerivesFromGMLFeature(poEltDecl))
1008 : {
1009 34 : 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 34 : bFoundGMLFeature = true;
1016 : }
1017 : }
1018 : }
1019 :
1020 543 : 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 174 : bool bFoundElementsInFirstChoiceNamespaces = false;
1027 494 : for (size_t iNS = 0;
1028 494 : !bFoundElementsInFirstChoiceNamespaces && iNS < aoNamespaces.size();
1029 : iNS++)
1030 : {
1031 320 : XMLCh *xmlNamespace = nullptr;
1032 : try
1033 : {
1034 320 : 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 320 : XSNamedMap<XSObject> *poMapElements = poModel->getComponentsByNamespace(
1044 : XSConstants::ELEMENT_DECLARATION, xmlNamespace);
1045 320 : bFoundElementsInFirstChoiceNamespaces =
1046 320 : poMapElements != nullptr && poMapElements->getLength() > 0;
1047 320 : XMLString::release(&xmlNamespace);
1048 : }
1049 174 : 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 348 : std::set<XSElementDeclaration *> oSetVisitedEltDecl;
1072 348 : std::set<XSModelGroup *> oSetVisitedModelGroups;
1073 348 : 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 348 : std::set<CPLString> aoSetXPathEltsForTopClass;
1080 :
1081 : // Third and fourth passes
1082 522 : for (int iPass = 0; iPass < 2; ++iPass)
1083 : {
1084 1148 : for (size_t iNS = 0; iNS < aoNamespaces.size(); iNS++)
1085 : {
1086 800 : XMLCh *xmlNamespace = nullptr;
1087 : try
1088 : {
1089 800 : 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 800 : poModel->getComponentsByNamespace(
1101 : XSConstants::ELEMENT_DECLARATION, xmlNamespace);
1102 :
1103 5208 : for (XMLSize_t i = 0;
1104 5208 : poMapElements != nullptr && i < poMapElements->getLength();
1105 : i++)
1106 : {
1107 : XSElementDeclaration *poEltDecl =
1108 : reinterpret_cast<XSElementDeclaration *>(
1109 4408 : poMapElements->item(i));
1110 4408 : XSComplexTypeDefinition *poCT = IsEltCompatibleOfFC(poEltDecl);
1111 4408 : if (!poEltDecl->getAbstract() && poCT != nullptr)
1112 : {
1113 : const CPLString osXPath(
1114 7744 : MakeXPath(transcode(poEltDecl->getNamespace()),
1115 11616 : transcode(poEltDecl->getName())));
1116 3872 : if (!IsIgnoredXPath(osXPath))
1117 : {
1118 3958 : if (bFoundGMLFeature && m_bInstantiateGMLFeaturesOnly &&
1119 86 : !DerivesFromGMLFeature(poEltDecl))
1120 : {
1121 : // Do nothing
1122 : }
1123 3860 : 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 1930 : oSetVisitedEltDecl.insert(poEltDecl);
1136 1930 : if (aoSetXPathEltsForTopClass.find(osXPath) ==
1137 3860 : aoSetXPathEltsForTopClass.end())
1138 : {
1139 1930 : m_oSetEltsForTopClass.insert(poEltDecl);
1140 1930 : oVectorEltsForTopClass.push_back(poEltDecl);
1141 1930 : aoSetXPathEltsForTopClass.insert(osXPath);
1142 : }
1143 : }
1144 : else
1145 : {
1146 1930 : bool bSimpleEnoughOut = true;
1147 1930 : int nSubCountSubEltOut = 0;
1148 1930 : auto poParticle = poCT->getParticle();
1149 1930 : if (poParticle)
1150 : {
1151 1929 : CPL_IGNORE_RET_VAL(
1152 1929 : FindElementsWithMustBeToLevel(
1153 : osXPath,
1154 : poParticle->getModelGroupTerm(), 0,
1155 : oSetVisitedEltDecl,
1156 : oSetVisitedModelGroups,
1157 : oVectorEltsForTopClass,
1158 : aoSetXPathEltsForTopClass, poModel,
1159 : bSimpleEnoughOut, nSubCountSubEltOut));
1160 : }
1161 : }
1162 : }
1163 : }
1164 : }
1165 :
1166 800 : XMLString::release(&xmlNamespace);
1167 : }
1168 : }
1169 :
1170 : // Find ambiguous class names
1171 : {
1172 3313 : for (const auto &oIter : m_oSetEltsForTopClass)
1173 : {
1174 3139 : CPLString osName(transcode(oIter->getName()));
1175 3139 : m_oMapEltNamesToInstanceCount[osName]++;
1176 : }
1177 : }
1178 :
1179 : // Instantiate all needed typenames
1180 3312 : for (const auto &poEltDecl : oVectorEltsForTopClass)
1181 : {
1182 6278 : const CPLString osXPath(MakeXPath(transcode(poEltDecl->getNamespace()),
1183 6278 : transcode(poEltDecl->getName())));
1184 :
1185 3139 : bool bError = false;
1186 : bool bResolvedType =
1187 3139 : InstantiateClassFromEltDeclaration(poEltDecl, poModel, bError);
1188 3139 : if (bError)
1189 : {
1190 1 : return false;
1191 : }
1192 3138 : if (!bResolvedType)
1193 : {
1194 0 : CPLError(
1195 : CE_Failure, CPLE_AppDefined, "Couldn't resolve %s (%s)",
1196 : osXPath.c_str(),
1197 0 : transcode(poEltDecl->getTypeDefinition()->getName()).c_str());
1198 0 : return false;
1199 : }
1200 : }
1201 :
1202 173 : LaunderClassNames();
1203 :
1204 173 : return true;
1205 : }
1206 :
1207 : /************************************************************************/
1208 : /* GetAnnotationDoc() */
1209 : /************************************************************************/
1210 :
1211 408365 : static CPLString GetAnnotationDoc(const XSAnnotation *annotation)
1212 : {
1213 408365 : if (!annotation)
1214 223606 : return CPLString();
1215 369518 : CPLString osAnnot(transcode(annotation->getAnnotationString()));
1216 184759 : CPLXMLNode *psRoot = CPLParseXMLString(osAnnot);
1217 184759 : CPLStripXMLNamespace(psRoot, nullptr, TRUE);
1218 369518 : CPLString osDoc(CPLGetXMLValue(psRoot, "=annotation.documentation", ""));
1219 184759 : CPLDestroyXMLNode(psRoot);
1220 184759 : return osDoc.Trim();
1221 : }
1222 :
1223 : /************************************************************************/
1224 : /* GetAnnotationDoc() */
1225 : /************************************************************************/
1226 :
1227 190129 : static CPLString GetAnnotationDoc(const XSAnnotationList *annotationList)
1228 : {
1229 190129 : if (!annotationList)
1230 6746 : return CPLString();
1231 366766 : CPLString osRet;
1232 366766 : for (size_t i = 0; i < annotationList->size(); ++i)
1233 : {
1234 366766 : CPLString osDoc(GetAnnotationDoc(annotationList->elementAt(i)));
1235 183383 : if (!osDoc.empty())
1236 : {
1237 183280 : if (!osRet.empty())
1238 0 : osRet += "\n";
1239 183280 : osRet += osDoc;
1240 : }
1241 : }
1242 183383 : return osRet;
1243 : }
1244 :
1245 : /************************************************************************/
1246 : /* GetAnnotationDoc() */
1247 : /************************************************************************/
1248 :
1249 190129 : static CPLString GetAnnotationDoc(const XSElementDeclaration *poEltDecl)
1250 : {
1251 190129 : XSTypeDefinition *poTypeDef = poEltDecl->getTypeDefinition();
1252 190129 : CPLString osDoc = GetAnnotationDoc(poEltDecl->getAnnotation());
1253 190129 : XSAnnotationList *list = nullptr;
1254 204788 : while (poTypeDef != nullptr)
1255 : {
1256 204788 : if (poTypeDef->getTypeCategory() == XSTypeDefinition::COMPLEX_TYPE)
1257 : {
1258 54765 : XSComplexTypeDefinition *poCT =
1259 : reinterpret_cast<XSComplexTypeDefinition *>(poTypeDef);
1260 54765 : list = poCT->getAnnotations();
1261 : }
1262 150023 : else if (poTypeDef->getTypeCategory() == XSTypeDefinition::SIMPLE_TYPE)
1263 : {
1264 150023 : XSSimpleTypeDefinition *poST =
1265 : reinterpret_cast<XSSimpleTypeDefinition *>(poTypeDef);
1266 150023 : list = poST->getAnnotations();
1267 : }
1268 204788 : if (list != nullptr)
1269 183383 : break;
1270 21405 : XSTypeDefinition *poNewTypeDef = poTypeDef->getBaseType();
1271 21405 : if (poNewTypeDef == poTypeDef)
1272 6746 : break;
1273 14659 : poTypeDef = poNewTypeDef;
1274 : }
1275 380258 : const CPLString osDoc2 = GetAnnotationDoc(list);
1276 190129 : if (!osDoc.empty() && !osDoc2.empty())
1277 : {
1278 302 : osDoc += "\n";
1279 302 : osDoc += osDoc2;
1280 : }
1281 189827 : else if (!osDoc2.empty())
1282 182978 : osDoc = osDoc2;
1283 380258 : return osDoc;
1284 : }
1285 :
1286 : /************************************************************************/
1287 : /* InstantiateClassFromEltDeclaration() */
1288 : /************************************************************************/
1289 :
1290 3139 : bool GMLASSchemaAnalyzer::InstantiateClassFromEltDeclaration(
1291 : XSElementDeclaration *poEltDecl, XSModel *poModel, bool &bError)
1292 : {
1293 3139 : bError = false;
1294 3139 : XSComplexTypeDefinition *poCT = IsEltCompatibleOfFC(poEltDecl);
1295 3139 : if (!poEltDecl->getAbstract() && poCT != nullptr)
1296 : {
1297 6278 : GMLASFeatureClass oClass;
1298 6278 : const CPLString osEltName(transcode(poEltDecl->getName()));
1299 : const CPLString osXPath(
1300 6278 : MakeXPath(transcode(poEltDecl->getNamespace()), osEltName));
1301 :
1302 3139 : if (IsIgnoredXPath(osXPath))
1303 : {
1304 : #ifdef DEBUG_VERBOSE
1305 : CPLDebug("GMLAS", "%s is in ignored xpaths", osXPath.c_str());
1306 : #endif
1307 0 : return false;
1308 : }
1309 :
1310 3139 : if (m_oMapEltNamesToInstanceCount[osEltName] > 1)
1311 : {
1312 8 : CPLString osLaunderedXPath(osXPath);
1313 4 : osLaunderedXPath.replaceAll(':', '_');
1314 4 : oClass.SetName(osLaunderedXPath);
1315 : }
1316 : else
1317 3135 : oClass.SetName(osEltName);
1318 :
1319 : #ifdef DEBUG_VERBOSE
1320 : CPLDebug("GMLAS", "Instantiating element %s", osXPath.c_str());
1321 : #endif
1322 3139 : oClass.SetXPath(osXPath);
1323 3139 : oClass.SetIsTopLevelElt(
1324 3139 : GetTopElementDeclarationFromXPath(osXPath, poModel) != nullptr);
1325 :
1326 6278 : std::set<XSModelGroup *> oSetVisitedModelGroups;
1327 :
1328 3139 : oClass.SetDocumentation(GetAnnotationDoc(poEltDecl));
1329 :
1330 : // might be NULL on swe:values for example
1331 3139 : if (poCT->getParticle() != nullptr)
1332 : {
1333 3119 : std::map<CPLString, int> oMapCountOccurrencesOfSameName;
1334 3119 : BuildMapCountOccurrencesOfSameName(
1335 : poCT->getParticle()->getModelGroupTerm(),
1336 : oMapCountOccurrencesOfSameName);
1337 :
1338 3119 : OGRwkbGeometryType eGeomType = wkbUnknown;
1339 3158 : if (IsGMLNamespace(transcode(poCT->getNamespace())) &&
1340 39 : (eGeomType = GetOGRGeometryType(poCT)) != wkbNone)
1341 : {
1342 2 : GMLASField oField;
1343 1 : oField.SetName("geometry");
1344 1 : oField.SetMinOccurs(1);
1345 1 : oField.SetMaxOccurs(1);
1346 1 : oField.SetType(GMLAS_FT_GEOMETRY, szFAKEXS_GEOMETRY);
1347 1 : oField.SetGeomType(eGeomType);
1348 1 : oField.SetXPath(osXPath + szMATCH_ALL);
1349 1 : oField.SetIncludeThisEltInBlob(true);
1350 :
1351 1 : oClass.AddField(oField);
1352 : }
1353 3118 : else if (!ExploreModelGroup(
1354 : poCT->getParticle()->getModelGroupTerm(),
1355 : poCT->getAttributeUses(), oClass, 0,
1356 : oSetVisitedModelGroups, poModel,
1357 : oMapCountOccurrencesOfSameName))
1358 : {
1359 1 : bError = true;
1360 1 : return false;
1361 : }
1362 : }
1363 : else
1364 : {
1365 : // TODO ?
1366 : }
1367 :
1368 3138 : if (!LaunderFieldNames(oClass))
1369 0 : return false;
1370 :
1371 3138 : m_aoClasses.push_back(oClass);
1372 3138 : return true;
1373 : }
1374 0 : return false;
1375 : }
1376 :
1377 : /************************************************************************/
1378 : /* SetFieldTypeAndWidthFromDefinition() */
1379 : /************************************************************************/
1380 :
1381 195025 : void GMLASSchemaAnalyzer::SetFieldTypeAndWidthFromDefinition(
1382 : XSSimpleTypeDefinition *poST, GMLASField &oField)
1383 : {
1384 195025 : int nMaxLength = 0;
1385 342227 : while (
1386 1074500 : poST->getBaseType() != poST &&
1387 537252 : poST->getBaseType()->getTypeCategory() ==
1388 1074500 : XSTypeDefinition::SIMPLE_TYPE &&
1389 534135 : !XMLString::equals(poST->getNamespace(), PSVIUni::fgNamespaceXmlSchema))
1390 : {
1391 : const XMLCh *maxLength =
1392 342227 : poST->getLexicalFacetValue(XSSimpleTypeDefinition::FACET_LENGTH);
1393 342227 : if (maxLength == nullptr)
1394 : {
1395 342133 : maxLength = poST->getLexicalFacetValue(
1396 : XSSimpleTypeDefinition::FACET_MAXLENGTH);
1397 : }
1398 342227 : if (maxLength != nullptr)
1399 168269 : nMaxLength = MAX(nMaxLength, atoi(transcode(maxLength)));
1400 342227 : poST = reinterpret_cast<XSSimpleTypeDefinition *>(poST->getBaseType());
1401 : }
1402 :
1403 195025 : if (XMLString::equals(poST->getNamespace(), PSVIUni::fgNamespaceXmlSchema))
1404 : {
1405 390050 : CPLString osType(transcode(poST->getName()));
1406 195025 : oField.SetType(GMLASField::GetTypeFromString(osType), osType);
1407 : }
1408 : else
1409 : {
1410 0 : CPLError(CE_Warning, CPLE_AppDefined, "Base type is not a xs: one ???");
1411 : }
1412 :
1413 195025 : oField.SetWidth(nMaxLength);
1414 195025 : }
1415 :
1416 : /************************************************************************/
1417 : /* IsSame() */
1418 : /* */
1419 : /* The objects returned by different PSVI API are not always the same */
1420 : /* so do content inspection to figure out if they are equivalent. */
1421 : /************************************************************************/
1422 :
1423 362 : bool GMLASSchemaAnalyzer::IsSame(const XSModelGroup *poModelGroup1,
1424 : const XSModelGroup *poModelGroup2)
1425 : {
1426 362 : if (poModelGroup1->getCompositor() != poModelGroup2->getCompositor())
1427 113 : return false;
1428 :
1429 249 : const XSParticleList *poParticleList1 = poModelGroup1->getParticles();
1430 249 : const XSParticleList *poParticleList2 = poModelGroup2->getParticles();
1431 249 : if (poParticleList1->size() != poParticleList2->size())
1432 87 : return false;
1433 :
1434 585 : for (size_t i = 0; i < poParticleList1->size(); ++i)
1435 : {
1436 444 : const XSParticle *poParticle1 = poParticleList1->elementAt(i);
1437 444 : const XSParticle *poParticle2 = poParticleList2->elementAt(i);
1438 888 : if (poParticle1->getTermType() != poParticle2->getTermType() ||
1439 888 : poParticle1->getMinOccurs() != poParticle2->getMinOccurs() ||
1440 1332 : poParticle1->getMaxOccurs() != poParticle2->getMaxOccurs() ||
1441 444 : poParticle1->getMaxOccursUnbounded() !=
1442 444 : poParticle2->getMaxOccursUnbounded())
1443 : {
1444 0 : return false;
1445 : }
1446 444 : switch (poParticle1->getTermType())
1447 : {
1448 0 : case XSParticle::TERM_EMPTY:
1449 0 : break;
1450 :
1451 444 : case XSParticle::TERM_ELEMENT:
1452 : {
1453 : const XSElementDeclaration *poElt1 =
1454 444 : const_cast<XSParticle *>(poParticle1)->getElementTerm();
1455 : const XSElementDeclaration *poElt2 =
1456 444 : const_cast<XSParticle *>(poParticle2)->getElementTerm();
1457 : // Pointer comparison works here
1458 444 : if (poElt1 != poElt2)
1459 21 : return false;
1460 423 : break;
1461 : }
1462 :
1463 0 : case XSParticle::TERM_MODELGROUP:
1464 : {
1465 : const XSModelGroup *psSubGroup1 =
1466 0 : const_cast<XSParticle *>(poParticle1)->getModelGroupTerm();
1467 : const XSModelGroup *psSubGroup2 =
1468 0 : const_cast<XSParticle *>(poParticle2)->getModelGroupTerm();
1469 0 : if (!IsSame(psSubGroup1, psSubGroup2))
1470 0 : return false;
1471 0 : break;
1472 : }
1473 :
1474 0 : case XSParticle::TERM_WILDCARD:
1475 : {
1476 : // TODO: check that pointer comparison works
1477 : const XSWildcard *psWildcard1 =
1478 0 : const_cast<XSParticle *>(poParticle1)->getWildcardTerm();
1479 : const XSWildcard *psWildcard2 =
1480 0 : const_cast<XSParticle *>(poParticle2)->getWildcardTerm();
1481 0 : if (psWildcard1 != psWildcard2)
1482 0 : return false;
1483 0 : break;
1484 : }
1485 :
1486 0 : default:
1487 : {
1488 0 : CPLAssert(FALSE);
1489 : return false;
1490 : }
1491 : }
1492 : }
1493 :
1494 141 : return true;
1495 : }
1496 :
1497 : /************************************************************************/
1498 : /* GetGroupName() */
1499 : /* */
1500 : /* The model group object returned when exploring a high level model */
1501 : /* group isn't the same object as the one returned by model group */
1502 : /* definitions and has no name. So we have to investigate the content */
1503 : /* of model groups to figure out if they are the same. */
1504 : /************************************************************************/
1505 :
1506 : XSModelGroupDefinition *
1507 1683 : GMLASSchemaAnalyzer::GetGroupDefinition(const XSModelGroup *poModelGroup)
1508 : {
1509 1904 : for (const auto &oIter : m_oMapModelGroupToMGD)
1510 : {
1511 362 : if (IsSame(poModelGroup, oIter.first))
1512 : {
1513 141 : return oIter.second;
1514 : }
1515 : }
1516 :
1517 1542 : return nullptr;
1518 : }
1519 :
1520 : /************************************************************************/
1521 : /* IsAnyType() */
1522 : /************************************************************************/
1523 :
1524 27191 : static bool IsAnyType(XSComplexTypeDefinition *poType)
1525 : {
1526 27191 : if (XMLString::equals(poType->getBaseType()->getNamespace(),
1527 73444 : PSVIUni::fgNamespaceXmlSchema) &&
1528 46253 : transcode(poType->getBaseType()->getName()) == szXS_ANY_TYPE)
1529 : {
1530 19062 : XSParticle *poParticle = poType->getParticle();
1531 19062 : if (poParticle != nullptr)
1532 : {
1533 18750 : XSModelGroup *poGroupTerm = poParticle->getModelGroupTerm();
1534 18750 : if (poGroupTerm != nullptr)
1535 : {
1536 18750 : XSParticleList *poParticles = poGroupTerm->getParticles();
1537 18750 : if (poParticles != nullptr)
1538 : {
1539 20330 : return poParticles->size() == 1 &&
1540 1580 : poParticles->elementAt(0)->getTermType() ==
1541 18750 : XSParticle::TERM_WILDCARD;
1542 : }
1543 : }
1544 : }
1545 312 : else if (poType->getDerivationMethod() ==
1546 : XSConstants::DERIVATION_EXTENSION)
1547 : {
1548 : // swe:values case
1549 0 : return true;
1550 : }
1551 : }
1552 8441 : return false;
1553 : }
1554 :
1555 : /************************************************************************/
1556 : /* SetFieldFromAttribute() */
1557 : /************************************************************************/
1558 :
1559 34256 : bool GMLASSchemaAnalyzer::SetFieldFromAttribute(GMLASField &oField,
1560 : XSAttributeUse *poAttr,
1561 : const CPLString &osXPathPrefix,
1562 : const CPLString &osNamePrefix)
1563 : {
1564 34256 : const XSAttributeDeclaration *poAttrDecl = poAttr->getAttrDeclaration();
1565 68512 : const CPLString osNS(transcode(poAttrDecl->getNamespace()));
1566 68512 : const CPLString osName(transcode(poAttrDecl->getName()));
1567 :
1568 34256 : if (osNamePrefix.empty())
1569 15029 : oField.SetName(osName);
1570 : else
1571 19227 : oField.SetName(osNamePrefix + "_" + osName);
1572 :
1573 34256 : XSSimpleTypeDefinition *poAttrType = poAttrDecl->getTypeDefinition();
1574 34256 : if (!poAttrType)
1575 : {
1576 1 : CPLError(CE_Failure, CPLE_AppDefined,
1577 : "Cannot get type definition for attribute %s",
1578 1 : oField.GetName().c_str());
1579 1 : return false;
1580 : }
1581 :
1582 34255 : SetFieldTypeAndWidthFromDefinition(poAttrType, oField);
1583 :
1584 34255 : oField.SetXPath(osXPathPrefix + "/@" + MakeXPath(osNS, osName));
1585 34255 : if (poAttr->getRequired())
1586 : {
1587 25862 : oField.SetNotNullable(true);
1588 : }
1589 34255 : oField.SetMinOccurs(oField.IsNotNullable() ? 1 : 0);
1590 34255 : oField.SetMaxOccurs(1);
1591 34255 : if (poAttr->getConstraintType() == XSConstants::VALUE_CONSTRAINT_FIXED)
1592 : {
1593 569 : oField.SetFixedValue(transcode(poAttr->getConstraintValue()));
1594 : }
1595 33686 : else if (poAttr->getConstraintType() ==
1596 : XSConstants::VALUE_CONSTRAINT_DEFAULT)
1597 : {
1598 246 : oField.SetDefaultValue(transcode(poAttr->getConstraintValue()));
1599 : }
1600 :
1601 : const bool bIsList =
1602 34255 : (poAttrType->getVariety() == XSSimpleTypeDefinition::VARIETY_LIST);
1603 34255 : if (bIsList)
1604 : {
1605 1561 : SetFieldTypeAndWidthFromDefinition(poAttrType->getItemType(), oField);
1606 1561 : if (m_bUseArrays && IsCompatibleOfArray(oField.GetType()))
1607 : {
1608 1560 : oField.SetList(true);
1609 1560 : oField.SetArray(true);
1610 : }
1611 : else
1612 : {
1613 : // We should probably create an auxiliary table here, but this
1614 : // is too corner case for now...
1615 1 : oField.SetType(GMLAS_FT_STRING, szXS_STRING);
1616 : }
1617 : }
1618 :
1619 34255 : oField.SetDocumentation(GetAnnotationDoc(poAttrDecl->getAnnotation()));
1620 :
1621 34255 : return true;
1622 : }
1623 :
1624 : /************************************************************************/
1625 : /* GetConcreteImplementationTypes() */
1626 : /************************************************************************/
1627 :
1628 318457 : void GMLASSchemaAnalyzer::GetConcreteImplementationTypes(
1629 : XSElementDeclaration *poParentElt,
1630 : std::vector<XSElementDeclaration *> &apoImplEltList)
1631 : {
1632 318457 : const auto oIter = m_oMapParentEltToChildElt.find(poParentElt);
1633 318457 : if (oIter != m_oMapParentEltToChildElt.end())
1634 : {
1635 11745 : for (size_t j = 0; j < oIter->second.size(); j++)
1636 : {
1637 10374 : XSElementDeclaration *poSubElt = oIter->second[j];
1638 10374 : if (IsEltCompatibleOfFC(poSubElt))
1639 : {
1640 2449 : if (!poSubElt->getAbstract())
1641 : {
1642 2001 : apoImplEltList.push_back(poSubElt);
1643 : }
1644 : }
1645 10374 : GetConcreteImplementationTypes(poSubElt, apoImplEltList);
1646 : }
1647 : }
1648 318457 : }
1649 :
1650 : /************************************************************************/
1651 : /* GetConstraintChildrenElements() */
1652 : /************************************************************************/
1653 :
1654 : std::vector<XSElementDeclaration *>
1655 308079 : GMLASSchemaAnalyzer::GetConstraintChildrenElements(const CPLString &osFullXPath)
1656 : {
1657 :
1658 308079 : std::vector<XSElementDeclaration *> oVectorRes;
1659 616158 : CPLString osMatched;
1660 308079 : if (m_oChildrenElementsConstraintsXPathMatcher.MatchesRefXPath(osFullXPath,
1661 : osMatched))
1662 : {
1663 : const std::vector<CPLString> &oVector =
1664 6 : m_oMapChildrenElementsConstraints[osMatched];
1665 : const std::map<CPLString, CPLString> &oMapPrefixToURI =
1666 6 : m_oChildrenElementsConstraintsXPathMatcher.GetMapPrefixToURI();
1667 14 : for (size_t j = 0; j < oVector.size(); ++j)
1668 : {
1669 8 : const CPLString &osSubElt(oVector[j]);
1670 16 : CPLString osSubEltPrefix;
1671 16 : CPLString osSubEltURI;
1672 16 : CPLString osSubEltType(osSubElt);
1673 8 : size_t nPos = osSubElt.find(":");
1674 8 : if (nPos != std::string::npos)
1675 : {
1676 8 : osSubEltPrefix = osSubElt.substr(0, nPos);
1677 8 : osSubEltType = osSubElt.substr(nPos + 1);
1678 :
1679 8 : const auto oIter2 = oMapPrefixToURI.find(osSubEltPrefix);
1680 8 : if (oIter2 != oMapPrefixToURI.end())
1681 : {
1682 8 : osSubEltURI = oIter2->second;
1683 : }
1684 : else
1685 : {
1686 0 : CPLError(CE_Warning, CPLE_AppDefined,
1687 : "Cannot find prefix of type constraint %s",
1688 : osSubElt.c_str());
1689 : }
1690 : }
1691 :
1692 16 : const CPLString osSubEltXPath(MakeXPath(osSubEltURI, osSubEltType));
1693 8 : const auto oIter2 = m_oMapXPathToEltDecl.find(osSubEltXPath);
1694 8 : if (oIter2 != m_oMapXPathToEltDecl.end())
1695 : {
1696 8 : XSElementDeclaration *poSubElt = oIter2->second;
1697 8 : if (IsEltCompatibleOfFC(poSubElt))
1698 : {
1699 8 : oVectorRes.push_back(poSubElt);
1700 : }
1701 : }
1702 : else
1703 : {
1704 0 : CPLError(
1705 : CE_Warning, CPLE_AppDefined,
1706 : "Cannot find element declaration of type constraint %s",
1707 : osSubElt.c_str());
1708 : }
1709 : }
1710 : }
1711 616158 : return oVectorRes;
1712 : }
1713 :
1714 : /************************************************************************/
1715 : /* GetOGRGeometryType() */
1716 : /************************************************************************/
1717 :
1718 1495 : static OGRwkbGeometryType GetOGRGeometryType(XSTypeDefinition *poTypeDef)
1719 : {
1720 : const struct MyStruct
1721 : {
1722 : const char *pszName;
1723 : OGRwkbGeometryType eType;
1724 1495 : } asArray[] = {{"GeometryPropertyType", wkbUnknown},
1725 : {"PointPropertyType", wkbPoint},
1726 : {"BoundingShapeType", wkbPolygon},
1727 : {"PolygonPropertyType", wkbPolygon},
1728 : {"LineStringPropertyType", wkbLineString},
1729 : {"MultiPointPropertyType", wkbMultiPoint},
1730 : {"MultiPolygonPropertyType", wkbMultiPolygon},
1731 : {"MultiLineStringPropertyType", wkbMultiLineString},
1732 : {"MultiGeometryPropertyType", wkbGeometryCollection},
1733 : {"MultiCurvePropertyType", wkbMultiCurve},
1734 : {"MultiSurfacePropertyType", wkbMultiSurface},
1735 : {"MultiSolidPropertyType", wkbUnknown},
1736 : // GeometryArrayPropertyType ?
1737 : {"GeometricPrimitivePropertyType", wkbUnknown},
1738 : {"CurvePropertyType", wkbCurve},
1739 : {"SurfacePropertyType", wkbSurface},
1740 : // SurfaceArrayPropertyType ?
1741 : // AbstractRingPropertyType ?
1742 : // LinearRingPropertyType ?
1743 : {"CompositeCurvePropertyType", wkbCurve},
1744 : {"CompositeSurfacePropertyType", wkbSurface},
1745 : {"CompositeSolidPropertyType", wkbUnknown},
1746 : {"GeometricComplexPropertyType", wkbUnknown},
1747 : {"SolidPropertyType", wkbPolyhedralSurface}};
1748 :
1749 2990 : CPLString osName(transcode(poTypeDef->getName()));
1750 22522 : for (size_t i = 0; i < CPL_ARRAYSIZE(asArray); ++i)
1751 : {
1752 21653 : if (osName == asArray[i].pszName)
1753 626 : return asArray[i].eType;
1754 : }
1755 869 : return wkbNone;
1756 :
1757 : #if 0
1758 : <complexType name="CurveSegmentArrayPropertyType">
1759 : <complexType name="KnotPropertyType">
1760 : <complexType name="SurfacePatchArrayPropertyType">
1761 : <complexType name="RingPropertyType">
1762 : <complexType name="PolygonPatchArrayPropertyType">
1763 : <complexType name="TrianglePatchArrayPropertyType">
1764 : <complexType name="LineStringSegmentArrayPropertyType">
1765 : <complexType name="SolidArrayPropertyType">
1766 : #endif
1767 : }
1768 :
1769 : /************************************************************************/
1770 : /* GetOGRGeometryTypeFromGMLEltName() */
1771 : /************************************************************************/
1772 :
1773 : static OGRwkbGeometryType
1774 878 : GetOGRGeometryTypeFromGMLEltName(const CPLString &osEltName)
1775 : {
1776 : const struct MyStruct
1777 : {
1778 : const char *pszName;
1779 : OGRwkbGeometryType eType;
1780 878 : } asArray[] = {
1781 : {"Point", wkbPoint},
1782 : {"Polygon", wkbPolygon},
1783 : {"Envelope", wkbPolygon},
1784 : {"LineString", wkbLineString},
1785 : {"MultiPoint", wkbMultiPoint},
1786 : {"MultiPolygon", wkbMultiPolygon},
1787 : {"MultiLineString", wkbMultiLineString},
1788 : {"MultiGeometry", wkbGeometryCollection},
1789 : {"MultiCurve", wkbMultiCurve},
1790 : {"MultiSurface", wkbMultiSurface},
1791 : {"MultiSolid", wkbUnknown},
1792 : {"Curve", wkbCurve},
1793 : {"Surface", wkbSurface},
1794 : {"CompositeCurve", wkbCurve},
1795 : {"CompositeSurface", wkbSurface},
1796 : {"CompositeSolid", wkbUnknown},
1797 : {"GeometricComplex", wkbUnknown},
1798 : };
1799 :
1800 15022 : for (size_t i = 0; i < CPL_ARRAYSIZE(asArray); ++i)
1801 : {
1802 14190 : if (osEltName == asArray[i].pszName)
1803 46 : return asArray[i].eType;
1804 : }
1805 832 : return wkbNone;
1806 : }
1807 :
1808 : /************************************************************************/
1809 : /* CreateNonNestedRelationship() */
1810 : /************************************************************************/
1811 :
1812 15565 : void GMLASSchemaAnalyzer::CreateNonNestedRelationship(
1813 : XSElementDeclaration *poElt,
1814 : std::vector<XSElementDeclaration *> &apoImplEltList,
1815 : GMLASFeatureClass &oClass, int nMaxOccurs, bool bEltNameWillNeedPrefix,
1816 : bool bForceJunctionTable, bool bCaseOfConstraintChildren)
1817 : {
1818 31130 : const CPLString osEltPrefix(GetPrefix(transcode(poElt->getNamespace())));
1819 31130 : const CPLString osEltName(transcode(poElt->getName()));
1820 : const CPLString osOnlyElementXPath(
1821 31130 : MakeXPath(transcode(poElt->getNamespace()), osEltName));
1822 31130 : const CPLString osElementXPath(oClass.GetXPath() + "/" +
1823 31130 : osOnlyElementXPath);
1824 :
1825 15565 : if (!poElt->getAbstract() && !bCaseOfConstraintChildren)
1826 : {
1827 15397 : apoImplEltList.insert(apoImplEltList.begin(), poElt);
1828 : }
1829 :
1830 31130 : std::set<CPLString> aoSetSubEltXPath;
1831 15565 : if (nMaxOccurs == 1 && !bForceJunctionTable)
1832 : {
1833 : // If the field isn't repeated, then we can link to each
1834 : // potential realization types with a field
1835 :
1836 13757 : for (size_t j = 0; j < apoImplEltList.size(); j++)
1837 : {
1838 7082 : XSElementDeclaration *poSubElt = apoImplEltList[j];
1839 7082 : const CPLString osSubEltName(transcode(poSubElt->getName()));
1840 : const CPLString osSubEltXPath(
1841 7082 : MakeXPath(transcode(poSubElt->getNamespace()), osSubEltName));
1842 :
1843 : // For AbstractFeature_SpatialDataSet_pkid in SpatialDataSet_member
1844 7082 : if (aoSetSubEltXPath.find(osSubEltXPath) != aoSetSubEltXPath.end())
1845 : {
1846 0 : continue;
1847 : }
1848 7082 : aoSetSubEltXPath.insert(osSubEltXPath);
1849 :
1850 14164 : const CPLString osRealFullXPath(oClass.GetXPath() + "/" +
1851 : ((bCaseOfConstraintChildren)
1852 14161 : ? osOnlyElementXPath + "/"
1853 28325 : : CPLString("")) +
1854 7082 : osSubEltXPath);
1855 :
1856 7082 : if (IsIgnoredXPath(osRealFullXPath))
1857 : {
1858 : #ifdef DEBUG_VERBOSE
1859 : CPLDebug("GMLAS", "%s is in ignored xpaths",
1860 : osRealFullXPath.c_str());
1861 : #endif
1862 5 : continue;
1863 : }
1864 :
1865 14154 : GMLASField oField;
1866 7077 : if (apoImplEltList.size() > 1 || bCaseOfConstraintChildren)
1867 : {
1868 528 : if (m_oMapEltNamesToInstanceCount[osSubEltName] > 1)
1869 : {
1870 2 : CPLString osLaunderedXPath(osSubEltXPath);
1871 2 : osLaunderedXPath.replaceAll(':', '_');
1872 6 : oField.SetName(((bEltNameWillNeedPrefix) ? osEltPrefix + "_"
1873 6 : : CPLString()) +
1874 8 : transcode(poElt->getName()) + "_" +
1875 4 : osLaunderedXPath + "_pkid");
1876 : }
1877 : else
1878 : {
1879 1578 : oField.SetName(((bEltNameWillNeedPrefix) ? osEltPrefix + "_"
1880 1578 : : CPLString()) +
1881 2104 : transcode(poElt->getName()) + "_" +
1882 1052 : osSubEltName + "_pkid");
1883 : }
1884 : }
1885 : else
1886 : {
1887 6549 : oField.SetName(transcode(poElt->getName()) + "_pkid");
1888 : }
1889 7077 : oField.SetXPath(osRealFullXPath);
1890 7077 : oField.SetMinOccurs(0);
1891 7077 : oField.SetMaxOccurs(nMaxOccurs);
1892 7077 : oField.SetCategory(GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK);
1893 7077 : oField.SetRelatedClassXPath(osSubEltXPath);
1894 7077 : oField.SetType(GMLAS_FT_STRING, szXS_STRING);
1895 7077 : oClass.AddField(oField);
1896 6675 : }
1897 : }
1898 : else
1899 : {
1900 : // If the field is repeated, we need to use junction
1901 : // tables
1902 17829 : for (size_t j = 0; j < apoImplEltList.size(); j++)
1903 : {
1904 8939 : XSElementDeclaration *poSubElt = apoImplEltList[j];
1905 8939 : const CPLString osSubEltName(transcode(poSubElt->getName()));
1906 : const CPLString osSubEltXPath(
1907 8939 : MakeXPath(transcode(poSubElt->getNamespace()), osSubEltName));
1908 :
1909 : // For AbstractFeature_SpatialDataSet_pkid in SpatialDataSet_member
1910 8939 : if (aoSetSubEltXPath.find(osSubEltXPath) != aoSetSubEltXPath.end())
1911 : {
1912 0 : continue;
1913 : }
1914 8939 : aoSetSubEltXPath.insert(osSubEltXPath);
1915 :
1916 : // Instantiate a junction table
1917 17878 : GMLASFeatureClass oJunctionTable;
1918 :
1919 8939 : if (m_oMapEltNamesToInstanceCount[osSubEltName] > 1)
1920 : {
1921 41 : CPLString osLaunderedXPath(osSubEltXPath);
1922 41 : osLaunderedXPath.replaceAll(':', '_');
1923 123 : oJunctionTable.SetName(oClass.GetName() + "_" +
1924 164 : transcode(poElt->getName()) + "_" +
1925 : osLaunderedXPath);
1926 : }
1927 : else
1928 : {
1929 26694 : oJunctionTable.SetName(oClass.GetName() + "_" +
1930 35592 : transcode(poElt->getName()) + "_" +
1931 : osSubEltName);
1932 : }
1933 : // Create a fake XPath binding the parent xpath (to an abstract
1934 : // element) to the child element
1935 8939 : oJunctionTable.SetXPath(
1936 17878 : BuildJunctionTableXPath(osElementXPath, osSubEltXPath));
1937 8939 : oJunctionTable.SetParentXPath(oClass.GetXPath());
1938 8939 : oJunctionTable.SetChildXPath(osSubEltXPath);
1939 8939 : m_aoClasses.push_back(oJunctionTable);
1940 :
1941 : // Add an abstract field
1942 17878 : GMLASField oField;
1943 8939 : oField.SetName(
1944 17878 : ((bEltNameWillNeedPrefix) ? osEltPrefix + "_" : CPLString()) +
1945 17878 : osEltName + "_" + osSubEltName);
1946 26817 : oField.SetXPath(oClass.GetXPath() + "/" +
1947 : ((bCaseOfConstraintChildren)
1948 26816 : ? osOnlyElementXPath + "/"
1949 35755 : : CPLString("")) +
1950 : osSubEltXPath);
1951 8939 : oField.SetMinOccurs(0);
1952 8939 : oField.SetMaxOccurs(nMaxOccurs);
1953 8939 : oField.SetAbstractElementXPath(osElementXPath);
1954 8939 : oField.SetRelatedClassXPath(osSubEltXPath);
1955 8939 : oField.SetCategory(
1956 : GMLASField::PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE);
1957 8939 : oClass.AddField(oField);
1958 : }
1959 : }
1960 :
1961 : #if 0
1962 : GMLASField oField;
1963 : oField.SetName( transcode(poElt->getName()) );
1964 : oField.SetXPath( osElementXPath );
1965 : oField.SetMinOccurs( poParticle->getMinOccurs() );
1966 : oField.SetMaxOccurs( poParticle->getMaxOccursUnbounded() ?
1967 : MAXOCCURS_UNLIMITED : poParticle->getMaxOccurs() );
1968 :
1969 : for( size_t j = 0; j < apoImplEltList.size(); j++ )
1970 : {
1971 : XSElementDeclaration* poSubElt = apoImplEltList[j];
1972 : XSTypeDefinition* poSubEltType =
1973 : poSubElt->getTypeDefinition();
1974 : XSComplexTypeDefinition* poCT =
1975 : reinterpret_cast<XSComplexTypeDefinition*>(poSubEltType);
1976 :
1977 : GMLASFeatureClass oNestedClass;
1978 : oNestedClass.SetName( oClass.GetName() + "_" +
1979 : transcode(poSubElt->getName()) );
1980 : oNestedClass.SetXPath( oClass.GetXPath() + "/" +
1981 : MakeXPath(transcode(poSubElt->getNamespace()),
1982 : transcode(poSubElt->getName())) );
1983 :
1984 : std::set<XSModelGroup*>
1985 : oSetNewVisitedModelGroups(oSetVisitedModelGroups);
1986 : if( !ExploreModelGroup(
1987 : poCT->getParticle()->getModelGroupTerm(),
1988 : NULL,
1989 : oNestedClass,
1990 : nRecursionCounter + 1,
1991 : oSetNewVisitedModelGroups ) )
1992 : {
1993 : return false;
1994 : }
1995 :
1996 : oClass.AddNestedClass( oNestedClass );
1997 : }
1998 :
1999 : if( !apoImplEltList.empty() )
2000 : {
2001 : oField.SetAbstract(true);
2002 : }
2003 : else
2004 : {
2005 : oField.SetType( GMLAS_FT_ANYTYPE, "anyType" );
2006 : oField.SetXPath( oClass.GetXPath() + "/" + "*" );
2007 : oField.SetIncludeThisEltInBlob( true );
2008 : }
2009 : oClass.AddField( oField );
2010 : #endif
2011 15565 : }
2012 :
2013 : /************************************************************************/
2014 : /* IsIgnoredXPath() */
2015 : /************************************************************************/
2016 :
2017 356805 : bool GMLASSchemaAnalyzer::IsIgnoredXPath(const CPLString &osXPath)
2018 : {
2019 713610 : CPLString osIgnored;
2020 713610 : return m_oIgnoredXPathMatcher.MatchesRefXPath(osXPath, osIgnored);
2021 : }
2022 :
2023 : /************************************************************************/
2024 : /* FindElementsWithMustBeToLevel() */
2025 : /************************************************************************/
2026 :
2027 27547 : bool GMLASSchemaAnalyzer::FindElementsWithMustBeToLevel(
2028 : const CPLString &osParentXPath, XSModelGroup *poModelGroup,
2029 : int nRecursionCounter, std::set<XSElementDeclaration *> &oSetVisitedEltDecl,
2030 : std::set<XSModelGroup *> &oSetVisitedModelGroups,
2031 : std::vector<XSElementDeclaration *> &oVectorEltsForTopClass,
2032 : std::set<CPLString> &aoSetXPathEltsForTopClass, XSModel *poModel,
2033 : bool &bSimpleEnoughOut, int &nCountSubEltsOut)
2034 : {
2035 27547 : const bool bAlreadyVisitedMG = (oSetVisitedModelGroups.find(poModelGroup) !=
2036 27547 : oSetVisitedModelGroups.end());
2037 :
2038 27547 : oSetVisitedModelGroups.insert(poModelGroup);
2039 :
2040 27547 : if (nRecursionCounter == 100)
2041 : {
2042 : // Presumably an hostile schema
2043 0 : CPLError(CE_Failure, CPLE_AppDefined,
2044 : "Schema analysis failed due to too deeply nested model");
2045 0 : return false;
2046 : }
2047 :
2048 : {
2049 55094 : CPLString osIgnored;
2050 27547 : if (m_oDisabledFlattenedXPathMatcher.MatchesRefXPath(osParentXPath,
2051 : osIgnored))
2052 : {
2053 0 : bSimpleEnoughOut = false;
2054 : }
2055 : }
2056 :
2057 27547 : XSParticleList *poParticles = poModelGroup->getParticles();
2058 144678 : for (size_t i = 0; i < poParticles->size(); ++i)
2059 : {
2060 117131 : XSParticle *poParticle = poParticles->elementAt(i);
2061 :
2062 221247 : const bool bRepeatedParticle = poParticle->getMaxOccursUnbounded() ||
2063 104116 : poParticle->getMaxOccurs() > 1;
2064 :
2065 117131 : if (poParticle->getTermType() == XSParticle::TERM_ELEMENT)
2066 : {
2067 105929 : XSElementDeclaration *poElt = poParticle->getElementTerm();
2068 105929 : XSTypeDefinition *poTypeDef = poElt->getTypeDefinition();
2069 105929 : const CPLString osEltName(transcode(poElt->getName()));
2070 105929 : const CPLString osEltNS(transcode(poElt->getNamespace()));
2071 105929 : const CPLString osXPath(MakeXPath(osEltNS, osEltName));
2072 211858 : const CPLString osFullXPath(osParentXPath + "/" + osXPath);
2073 :
2074 : #ifdef DEBUG_SUPER_VERBOSE
2075 : CPLDebug("GMLAS", "FindElementsWithMustBeToLevel: %s",
2076 : osFullXPath.c_str());
2077 : #endif
2078 :
2079 105929 : if (IsIgnoredXPath(osFullXPath))
2080 : {
2081 : #ifdef DEBUG_VERBOSE
2082 : CPLDebug("GMLAS", "%s is in ignored xpaths",
2083 : osFullXPath.c_str());
2084 : #endif
2085 7 : continue;
2086 : }
2087 :
2088 : // This could be refined to detect if the repeated element might not
2089 : // be simplifiable as an array
2090 105922 : if (bSimpleEnoughOut && bRepeatedParticle)
2091 : {
2092 : #ifdef DEBUG_VERBOSE
2093 : CPLDebug("GMLAS", "%s not inlinable because %s is repeated",
2094 : osParentXPath.c_str(), osXPath.c_str());
2095 : #endif
2096 4320 : bSimpleEnoughOut = false;
2097 : }
2098 :
2099 : // We don't want to inline
2100 : // sub-classes with hundereds of attributes
2101 105922 : nCountSubEltsOut++;
2102 :
2103 105922 : std::vector<XSElementDeclaration *> apoImplEltList;
2104 105922 : GetConcreteImplementationTypes(poElt, apoImplEltList);
2105 :
2106 : std::vector<XSElementDeclaration *> apoChildrenElements =
2107 105922 : GetConstraintChildrenElements(osFullXPath);
2108 :
2109 : // Special case for a GML geometry property
2110 106531 : if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
2111 609 : GetOGRGeometryType(poTypeDef) != wkbNone)
2112 : {
2113 : // Do nothing
2114 : }
2115 105915 : else if (IsGMLNamespace(osEltNS) &&
2116 302 : GetOGRGeometryTypeFromGMLEltName(osEltName) != wkbNone)
2117 : {
2118 : // Do nothing
2119 : }
2120 : // Any GML abstract type
2121 105825 : else if (poElt->getAbstract() && IsGMLNamespace(osEltNS) &&
2122 70 : osEltName != "_Feature" &&
2123 105825 : osEltName != "AbstractFeature" &&
2124 31 : osEltName != "AbstractTimeObject")
2125 : {
2126 : // Do nothing
2127 : }
2128 : // Are there substitution groups for this element ?
2129 105562 : else if (!apoImplEltList.empty() || !apoChildrenElements.empty())
2130 : {
2131 151 : if (!apoChildrenElements.empty())
2132 : {
2133 3 : apoImplEltList = std::move(apoChildrenElements);
2134 : }
2135 148 : else if (!poElt->getAbstract())
2136 : {
2137 30 : apoImplEltList.insert(apoImplEltList.begin(), poElt);
2138 : }
2139 510 : for (size_t j = 0; j < apoImplEltList.size(); j++)
2140 : {
2141 359 : XSElementDeclaration *poSubElt = apoImplEltList[j];
2142 : const CPLString osSubEltXPath(
2143 718 : MakeXPath(transcode(poSubElt->getNamespace()),
2144 718 : transcode(poSubElt->getName())));
2145 :
2146 359 : if (IsIgnoredXPath(osParentXPath + "/" + osSubEltXPath))
2147 : {
2148 : #ifdef DEBUG_VERBOSE
2149 : CPLDebug("GMLAS", "%s is in ignored xpaths",
2150 : (osParentXPath + "/" + osSubEltXPath).c_str());
2151 : #endif
2152 4 : continue;
2153 : }
2154 :
2155 : // Make sure we will instantiate the referenced element
2156 355 : if (m_oSetEltsForTopClass.find(poSubElt) ==
2157 852 : m_oSetEltsForTopClass.end() &&
2158 142 : aoSetXPathEltsForTopClass.find(osSubEltXPath) ==
2159 497 : aoSetXPathEltsForTopClass.end())
2160 : {
2161 : #ifdef DEBUG_VERBOSE
2162 : CPLDebug(
2163 : "GMLAS",
2164 : "%s (%s) must be exposed as "
2165 : "top-level (%s of %s)",
2166 : osSubEltXPath.c_str(),
2167 : transcode(poSubElt->getTypeDefinition()->getName())
2168 : .c_str(),
2169 : apoChildrenElements.empty() ? "derived class"
2170 : : "child",
2171 : osParentXPath.c_str());
2172 : #endif
2173 :
2174 142 : oSetVisitedEltDecl.insert(poSubElt);
2175 142 : m_oSetEltsForTopClass.insert(poSubElt);
2176 142 : oVectorEltsForTopClass.push_back(poSubElt);
2177 142 : aoSetXPathEltsForTopClass.insert(osSubEltXPath);
2178 :
2179 : XSComplexTypeDefinition *poSubEltCT =
2180 142 : IsEltCompatibleOfFC(poSubElt);
2181 279 : if (!bAlreadyVisitedMG && poSubEltCT != nullptr &&
2182 137 : poSubEltCT->getParticle() != nullptr)
2183 : {
2184 137 : bool bSubSimpleEnoughOut = true;
2185 137 : int nSubCountSubElt = 0;
2186 137 : if (!FindElementsWithMustBeToLevel(
2187 : osSubEltXPath,
2188 : poSubEltCT->getParticle()
2189 : ->getModelGroupTerm(),
2190 : nRecursionCounter + 1, oSetVisitedEltDecl,
2191 : oSetVisitedModelGroups,
2192 : oVectorEltsForTopClass,
2193 : aoSetXPathEltsForTopClass, poModel,
2194 : bSubSimpleEnoughOut, nSubCountSubElt))
2195 : {
2196 0 : return false;
2197 : }
2198 : }
2199 : }
2200 : }
2201 : }
2202 :
2203 210768 : else if (!poElt->getAbstract() &&
2204 105357 : poTypeDef->getTypeCategory() ==
2205 : XSTypeDefinition::COMPLEX_TYPE)
2206 : {
2207 44299 : nCountSubEltsOut--;
2208 :
2209 44299 : XSComplexTypeDefinition *poEltCT = IsEltCompatibleOfFC(poElt);
2210 44299 : if (poEltCT)
2211 : {
2212 : // Might be a bit extreme, but for now we don't inline
2213 : // classes that have subclasses.
2214 32761 : if (bSimpleEnoughOut)
2215 : {
2216 : #ifdef DEBUG_VERBOSE
2217 : CPLDebug("GMLAS",
2218 : "%s not inlinable because %s field is complex",
2219 : osParentXPath.c_str(), osXPath.c_str());
2220 : #endif
2221 5622 : bSimpleEnoughOut = false;
2222 : }
2223 :
2224 32761 : if (oSetVisitedEltDecl.find(poElt) !=
2225 65522 : oSetVisitedEltDecl.end())
2226 : {
2227 15086 : if (m_oSetEltsForTopClass.find(poElt) ==
2228 19864 : m_oSetEltsForTopClass.end() &&
2229 4778 : m_oSetSimpleEnoughElts.find(poElt) ==
2230 19864 : m_oSetSimpleEnoughElts.end() &&
2231 2836 : aoSetXPathEltsForTopClass.find(osXPath) ==
2232 17922 : aoSetXPathEltsForTopClass.end())
2233 : {
2234 2126 : CPLString osIgnored;
2235 1063 : if (!m_oForcedFlattenedXPathMatcher.MatchesRefXPath(
2236 : osXPath, osIgnored))
2237 : {
2238 : #ifdef DEBUG_VERBOSE
2239 : CPLDebug("GMLAS",
2240 : "%s (%s) must be exposed as "
2241 : "top-level (multiple time referenced)",
2242 : osXPath.c_str(),
2243 : transcode(poTypeDef->getNamespace())
2244 : .c_str());
2245 : #endif
2246 1063 : m_oSetEltsForTopClass.insert(poElt);
2247 1063 : oVectorEltsForTopClass.push_back(poElt);
2248 1063 : aoSetXPathEltsForTopClass.insert(osXPath);
2249 : }
2250 : }
2251 : }
2252 : else
2253 : {
2254 17675 : oSetVisitedEltDecl.insert(poElt);
2255 :
2256 35350 : if (!bAlreadyVisitedMG &&
2257 17675 : poEltCT->getParticle() != nullptr)
2258 : {
2259 17614 : int nSubCountSubElt = 0;
2260 :
2261 : // Process attributes
2262 : XSAttributeUseList *poAttrList =
2263 17614 : poEltCT->getAttributeUses();
2264 : const size_t nAttrListSize =
2265 17614 : (poAttrList != nullptr) ? poAttrList->size()
2266 17614 : : 0;
2267 19364 : for (size_t j = 0; j < nAttrListSize; ++j)
2268 : {
2269 : XSAttributeUse *poAttr =
2270 1750 : poAttrList->elementAt(j);
2271 1750 : GMLASField oField;
2272 1750 : if (!SetFieldFromAttribute(oField, poAttr,
2273 : osFullXPath))
2274 : {
2275 0 : return false;
2276 : }
2277 3476 : if (!IsIgnoredXPath(oField.GetXPath()) &&
2278 1726 : oField.GetFixedValue().empty())
2279 : {
2280 : #ifdef DEBUG_SUPER_VERBOSE
2281 : CPLDebug(
2282 : "GMLAS",
2283 : "FindElementsWithMustBeToLevel: %s",
2284 : oField.GetXPath().c_str());
2285 : #endif
2286 1604 : nSubCountSubElt++;
2287 : }
2288 : }
2289 :
2290 17614 : bool bSubSimpleEnoughOut = true;
2291 17614 : if (!FindElementsWithMustBeToLevel(
2292 : osFullXPath,
2293 : poEltCT->getParticle()->getModelGroupTerm(),
2294 : nRecursionCounter + 1, oSetVisitedEltDecl,
2295 : oSetVisitedModelGroups,
2296 : oVectorEltsForTopClass,
2297 : aoSetXPathEltsForTopClass, poModel,
2298 : bSubSimpleEnoughOut, nSubCountSubElt))
2299 : {
2300 0 : return false;
2301 : }
2302 17614 : if (bSubSimpleEnoughOut)
2303 : {
2304 : #ifdef DEBUG_VERBOSE
2305 : CPLDebug("GMLAS", "%s is inlinable: %d fields",
2306 : osXPath.c_str(), nSubCountSubElt);
2307 : #endif
2308 7716 : m_oSetSimpleEnoughElts.insert(poElt);
2309 :
2310 7716 : nCountSubEltsOut += nSubCountSubElt;
2311 : }
2312 9898 : else if (bSimpleEnoughOut)
2313 : {
2314 : #ifdef DEBUG_VERBOSE
2315 : CPLDebug("GMLAS",
2316 : "%s not inlinable because %s is not "
2317 : "inlinable",
2318 : osParentXPath.c_str(),
2319 : osXPath.c_str());
2320 : #endif
2321 0 : bSimpleEnoughOut = false;
2322 : }
2323 : }
2324 : }
2325 : }
2326 : else
2327 : {
2328 11538 : if (transcode(poElt->getName()) != szFEATURE_COLLECTION)
2329 : {
2330 11538 : poEltCT = reinterpret_cast<XSComplexTypeDefinition *>(
2331 : poTypeDef);
2332 : // Process attributes
2333 : XSAttributeUseList *poAttrList =
2334 11538 : poEltCT->getAttributeUses();
2335 : const size_t nAttrListSize =
2336 11538 : (poAttrList != nullptr) ? poAttrList->size() : 0;
2337 22282 : for (size_t j = 0;
2338 22282 : bSimpleEnoughOut && j < nAttrListSize; ++j)
2339 : {
2340 10744 : XSAttributeUse *poAttr = poAttrList->elementAt(j);
2341 10744 : GMLASField oField;
2342 10744 : if (!SetFieldFromAttribute(oField, poAttr,
2343 : osFullXPath))
2344 : {
2345 0 : return false;
2346 : }
2347 21476 : if (!IsIgnoredXPath(oField.GetXPath()) &&
2348 10732 : oField.GetFixedValue().empty())
2349 : {
2350 : #ifdef DEBUG_SUPER_VERBOSE
2351 : CPLDebug("GMLAS",
2352 : "FindElementsWithMustBeToLevel: %s",
2353 : oField.GetXPath().c_str());
2354 : #endif
2355 10725 : nCountSubEltsOut++;
2356 : }
2357 : }
2358 : }
2359 : }
2360 :
2361 44299 : CPLString osTargetElement;
2362 44299 : if (poElt->getAnnotation() != nullptr)
2363 : {
2364 : CPLString osAnnot(transcode(
2365 800 : poElt->getAnnotation()->getAnnotationString()));
2366 :
2367 : #ifdef DEBUG_SUPER_VERBOSE
2368 : CPLDebug("GMLAS", "Annot: %s", osAnnot.c_str());
2369 : #endif
2370 400 : CPLXMLNode *psRoot = CPLParseXMLString(osAnnot);
2371 400 : CPLStripXMLNamespace(psRoot, nullptr, TRUE);
2372 : osTargetElement = CPLGetXMLValue(
2373 400 : psRoot, "=annotation.appinfo.targetElement", "");
2374 400 : CPLDestroyXMLNode(psRoot);
2375 : #ifdef DEBUG_VERBOSE
2376 : if (!osTargetElement.empty())
2377 : CPLDebug("GMLAS", "targetElement: %s",
2378 : osTargetElement.c_str());
2379 : #endif
2380 : }
2381 :
2382 : // If we have a element of type gml:ReferenceType that has
2383 : // a targetElement in its annotation.appinfo, then create
2384 : // a dedicated field to have cross-layer relationships.
2385 88812 : if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
2386 88812 : transcode(poTypeDef->getName()) == "ReferenceType" &&
2387 41 : !osTargetElement.empty())
2388 : {
2389 : XSElementDeclaration *poTargetElt =
2390 10 : GetTopElementDeclarationFromXPath(osTargetElement,
2391 10 : poModel);
2392 : // TODO: even for non abstract we should probably
2393 : // handle substitutions
2394 10 : if (poTargetElt != nullptr && !poTargetElt->getAbstract())
2395 : {
2396 : const CPLString osTargetEltXPath(
2397 8 : MakeXPath(transcode(poTargetElt->getNamespace()),
2398 8 : transcode(poTargetElt->getName())));
2399 :
2400 4 : if (IsIgnoredXPath(osTargetEltXPath))
2401 : {
2402 : #ifdef DEBUG_VERBOSE
2403 : CPLDebug("GMLAS", "%s is in ignored xpaths",
2404 : osTargetEltXPath.c_str());
2405 : #endif
2406 0 : continue;
2407 : }
2408 :
2409 : // Make sure we will instantiate the referenced
2410 : // element
2411 4 : if (m_oSetEltsForTopClass.find(poTargetElt) ==
2412 12 : m_oSetEltsForTopClass.end() &&
2413 4 : aoSetXPathEltsForTopClass.find(osTargetEltXPath) ==
2414 8 : aoSetXPathEltsForTopClass.end())
2415 : {
2416 : #ifdef DEBUG_VERBOSE
2417 : CPLDebug(
2418 : "GMLAS", "%d: Adding %s as (%s) needed type",
2419 : __LINE__, osTargetElement.c_str(),
2420 : transcode(
2421 : poTargetElt->getTypeDefinition()->getName())
2422 : .c_str());
2423 : #endif
2424 4 : oSetVisitedEltDecl.insert(poTargetElt);
2425 4 : m_oSetEltsForTopClass.insert(poTargetElt);
2426 4 : oVectorEltsForTopClass.push_back(poTargetElt);
2427 4 : aoSetXPathEltsForTopClass.insert(osTargetEltXPath);
2428 : }
2429 :
2430 : XSComplexTypeDefinition *poTargetEltCT =
2431 4 : IsEltCompatibleOfFC(poTargetElt);
2432 8 : if (!bAlreadyVisitedMG && poTargetEltCT &&
2433 4 : poTargetEltCT->getParticle() != nullptr)
2434 : {
2435 4 : bool bSubSimpleEnoughOut = true;
2436 4 : int nSubCountSubElt = 0;
2437 4 : if (!FindElementsWithMustBeToLevel(
2438 : osTargetEltXPath,
2439 : poTargetEltCT->getParticle()
2440 : ->getModelGroupTerm(),
2441 : nRecursionCounter + 1, oSetVisitedEltDecl,
2442 : oSetVisitedModelGroups,
2443 : oVectorEltsForTopClass,
2444 : aoSetXPathEltsForTopClass, poModel,
2445 : bSubSimpleEnoughOut, nSubCountSubElt))
2446 : {
2447 0 : return false;
2448 : }
2449 : }
2450 : }
2451 : }
2452 : }
2453 : }
2454 19256 : else if (!bAlreadyVisitedMG &&
2455 8054 : poParticle->getTermType() == XSParticle::TERM_MODELGROUP)
2456 : {
2457 : // This could be refined to detect if the repeated element might not
2458 : // be simplifiable as an array
2459 7863 : if (bSimpleEnoughOut && bRepeatedParticle)
2460 : {
2461 : #ifdef DEBUG_VERBOSE
2462 : CPLDebug(
2463 : "GMLAS",
2464 : "%s not inlinable because there is a repeated particle",
2465 : osParentXPath.c_str());
2466 : #endif
2467 457 : bSimpleEnoughOut = false;
2468 : }
2469 :
2470 7863 : XSModelGroup *psSubModelGroup = poParticle->getModelGroupTerm();
2471 7863 : if (!FindElementsWithMustBeToLevel(
2472 : osParentXPath, psSubModelGroup, nRecursionCounter + 1,
2473 : oSetVisitedEltDecl, oSetVisitedModelGroups,
2474 : oVectorEltsForTopClass, aoSetXPathEltsForTopClass, poModel,
2475 : bSimpleEnoughOut, nCountSubEltsOut))
2476 : {
2477 0 : return false;
2478 : }
2479 : }
2480 : else
2481 : {
2482 : // This could be refined to detect if the repeated element might not
2483 : // be simplifiable as an array
2484 3339 : if (bSimpleEnoughOut && bRepeatedParticle)
2485 : {
2486 : #ifdef DEBUG_VERBOSE
2487 : CPLDebug(
2488 : "GMLAS",
2489 : "%s not inlinable because there is a repeated particle",
2490 : osParentXPath.c_str());
2491 : #endif
2492 423 : bSimpleEnoughOut = false;
2493 : }
2494 : }
2495 : }
2496 :
2497 27547 : if (bSimpleEnoughOut && nCountSubEltsOut > m_nMaximumFieldsForFlattening)
2498 : {
2499 1570 : CPLString osIgnored;
2500 785 : if (!m_oForcedFlattenedXPathMatcher.MatchesRefXPath(osParentXPath,
2501 : osIgnored))
2502 : {
2503 : #ifdef DEBUG_VERBOSE
2504 : CPLDebug("GMLAS",
2505 : "%s not inlinable because it has more than %d fields",
2506 : osParentXPath.c_str(), m_nMaximumFieldsForFlattening);
2507 : #endif
2508 785 : bSimpleEnoughOut = false;
2509 : }
2510 : }
2511 :
2512 27547 : return true;
2513 : }
2514 :
2515 : /************************************************************************/
2516 : /* IsGMLNamespace() */
2517 : /************************************************************************/
2518 :
2519 691278 : bool GMLASSchemaAnalyzer::IsGMLNamespace(const CPLString &osURI)
2520 : {
2521 691278 : if (osURI.find(szGML_URI) == 0)
2522 2181 : return true;
2523 : // Below is mostly for unit tests were we use xmlns:gml="http://fake_gml"
2524 689097 : const auto oIter = m_oMapURIToPrefix.find(osURI);
2525 689097 : return oIter != m_oMapURIToPrefix.end() && oIter->second == szGML_PREFIX;
2526 : }
2527 :
2528 : /************************************************************************/
2529 : /* BuildMapCountOccurrencesOfSameName() */
2530 : /************************************************************************/
2531 :
2532 47114 : void GMLASSchemaAnalyzer::BuildMapCountOccurrencesOfSameName(
2533 : XSModelGroup *poModelGroup,
2534 : std::map<CPLString, int> &oMapCountOccurrencesOfSameName)
2535 : {
2536 47114 : XSParticleList *poParticles = poModelGroup->getParticles();
2537 267136 : for (size_t i = 0; i < poParticles->size(); ++i)
2538 : {
2539 220022 : XSParticle *poParticle = poParticles->elementAt(i);
2540 220022 : if (poParticle->getTermType() == XSParticle::TERM_ELEMENT)
2541 : {
2542 202428 : XSElementDeclaration *poElt = poParticle->getElementTerm();
2543 202428 : const CPLString osEltName(transcode(poElt->getName()));
2544 202428 : oMapCountOccurrencesOfSameName[osEltName]++;
2545 : }
2546 17594 : else if (poParticle->getTermType() == XSParticle::TERM_MODELGROUP)
2547 : {
2548 17431 : XSModelGroup *psSubModelGroup = poParticle->getModelGroupTerm();
2549 17431 : BuildMapCountOccurrencesOfSameName(psSubModelGroup,
2550 : oMapCountOccurrencesOfSameName);
2551 : }
2552 : }
2553 47114 : }
2554 :
2555 : /************************************************************************/
2556 : /* ComposeMinOccurs() */
2557 : /************************************************************************/
2558 :
2559 3838 : static int ComposeMinOccurs(int nVal1, int nVal2)
2560 : {
2561 3838 : return nVal1 * nVal2;
2562 : }
2563 :
2564 : /************************************************************************/
2565 : /* ComposeMaxOccurs() */
2566 : /************************************************************************/
2567 :
2568 3838 : static int ComposeMaxOccurs(int nVal1, int nVal2)
2569 : {
2570 3838 : if (nVal1 == MAXOCCURS_UNLIMITED || nVal2 == MAXOCCURS_UNLIMITED)
2571 3120 : return MAXOCCURS_UNLIMITED;
2572 718 : return nVal1 * nVal2;
2573 : }
2574 :
2575 : /************************************************************************/
2576 : /* ExploreModelGroup() */
2577 : /************************************************************************/
2578 :
2579 47117 : bool GMLASSchemaAnalyzer::ExploreModelGroup(
2580 : XSModelGroup *poModelGroup, XSAttributeUseList *poMainAttrList,
2581 : GMLASFeatureClass &oClass, int nRecursionCounter,
2582 : std::set<XSModelGroup *> &oSetVisitedModelGroups, XSModel *poModel,
2583 : const std::map<CPLString, int> &oMapCountOccurrencesOfSameName)
2584 : {
2585 47117 : if (oSetVisitedModelGroups.find(poModelGroup) !=
2586 94234 : oSetVisitedModelGroups.end())
2587 : {
2588 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s already visited",
2589 0 : oClass.GetXPath().c_str());
2590 0 : return false;
2591 : }
2592 47117 : oSetVisitedModelGroups.insert(poModelGroup);
2593 :
2594 47117 : if (nRecursionCounter == 100)
2595 : {
2596 : // Presumably an hostile schema
2597 0 : CPLError(CE_Failure, CPLE_AppDefined,
2598 : "Schema analysis failed due to too deeply nested model");
2599 0 : return false;
2600 : }
2601 :
2602 47117 : if (poMainAttrList != nullptr)
2603 : {
2604 234 : const size_t nMainAttrListSize = poMainAttrList->size();
2605 1189 : for (size_t j = 0; j < nMainAttrListSize; ++j)
2606 : {
2607 956 : GMLASField oField;
2608 956 : XSAttributeUse *poAttr = poMainAttrList->elementAt(j);
2609 956 : if (!SetFieldFromAttribute(oField, poAttr, oClass.GetXPath()))
2610 : {
2611 1 : return false;
2612 : }
2613 :
2614 955 : if (IsIgnoredXPath(oField.GetXPath()))
2615 : {
2616 : #ifdef DEBUG_VERBOSE
2617 : CPLDebug("GMLAS", "%s is in ignored xpaths",
2618 : oField.GetXPath().c_str());
2619 : #endif
2620 56 : if (!oField.GetFixedValue().empty() ||
2621 25 : !oField.GetDefaultValue().empty())
2622 : {
2623 8 : oField.SetIgnored();
2624 : }
2625 : else
2626 : {
2627 23 : continue;
2628 : }
2629 : }
2630 :
2631 932 : oClass.AddField(oField);
2632 : }
2633 : }
2634 :
2635 47116 : XSParticleList *poParticles = poModelGroup->getParticles();
2636 :
2637 : // Special case for GML 3.1.1 where gml:metaDataProperty should be
2638 : // a sequence of gml:_Metadata but for some reason they have used
2639 : // a sequence of any.
2640 47116 : if (oClass.GetXPath() == "gml:metaDataProperty" &&
2641 0 : poModelGroup->getCompositor() == XSModelGroup::COMPOSITOR_SEQUENCE &&
2642 47116 : poParticles->size() == 1 &&
2643 0 : poParticles->elementAt(0)->getTermType() == XSParticle::TERM_WILDCARD)
2644 : {
2645 : XSElementDeclaration *poGMLMetadata =
2646 0 : GetTopElementDeclarationFromXPath("gml:_MetaData", poModel);
2647 0 : if (poGMLMetadata != nullptr)
2648 : {
2649 0 : std::vector<XSElementDeclaration *> apoImplEltList;
2650 0 : GetConcreteImplementationTypes(poGMLMetadata, apoImplEltList);
2651 0 : CreateNonNestedRelationship(poGMLMetadata, apoImplEltList, oClass,
2652 : 1,
2653 : false, // doesn't need prefix
2654 : true, // force junction table
2655 : false // regular case
2656 : );
2657 :
2658 0 : return true;
2659 : }
2660 : }
2661 :
2662 : const bool bIsChoice =
2663 47116 : (poModelGroup->getCompositor() == XSModelGroup::COMPOSITOR_CHOICE);
2664 47116 : int nGroup = 0;
2665 :
2666 265335 : for (size_t i = 0; i < poParticles->size(); ++i)
2667 : {
2668 219691 : XSParticle *poParticle = poParticles->elementAt(i);
2669 425116 : const bool bRepeatedParticle = poParticle->getMaxOccursUnbounded() ||
2670 205425 : poParticle->getMaxOccurs() > 1;
2671 219691 : const int nMinOccurs = static_cast<int>(poParticle->getMinOccurs());
2672 : const int nMaxOccurs =
2673 219691 : poParticle->getMaxOccursUnbounded()
2674 425116 : ? MAXOCCURS_UNLIMITED
2675 205425 : : static_cast<int>(poParticle->getMaxOccurs());
2676 :
2677 219691 : if (poParticle->getTermType() == XSParticle::TERM_ELEMENT)
2678 : {
2679 202165 : XSElementDeclaration *poElt = poParticle->getElementTerm();
2680 202165 : const CPLString osEltName(transcode(poElt->getName()));
2681 :
2682 202165 : const auto oIter = oMapCountOccurrencesOfSameName.find(osEltName);
2683 : const bool bEltNameWillNeedPrefix =
2684 404330 : oIter != oMapCountOccurrencesOfSameName.end() &&
2685 202165 : oIter->second > 1;
2686 202165 : const CPLString osEltNS(transcode(poElt->getNamespace()));
2687 : const CPLString osPrefixedEltName((bEltNameWillNeedPrefix
2688 404326 : ? GetPrefix(osEltNS) + "_"
2689 808652 : : CPLString()) +
2690 202165 : osEltName);
2691 202165 : const CPLString osOnlyElementXPath(MakeXPath(osEltNS, osEltName));
2692 404330 : const CPLString osElementXPath(oClass.GetXPath() + "/" +
2693 202165 : osOnlyElementXPath);
2694 : #ifdef DEBUG_VERBOSE
2695 : CPLDebug("GMLAS", "Iterating through %s", osElementXPath.c_str());
2696 : #endif
2697 :
2698 202165 : if (IsIgnoredXPath(osElementXPath))
2699 : {
2700 : #ifdef DEBUG_VERBOSE
2701 : CPLDebug("GMLAS", "%s is in ignored xpaths",
2702 : osElementXPath.c_str());
2703 : #endif
2704 8 : continue;
2705 : }
2706 :
2707 202157 : CPLString osTargetElement;
2708 202157 : if (poElt->getAnnotation() != nullptr)
2709 : {
2710 : CPLString osAnnot(
2711 1428 : transcode(poElt->getAnnotation()->getAnnotationString()));
2712 :
2713 : #ifdef DEBUG_SUPER_VERBOSE
2714 : CPLDebug("GMLAS", "Annot: %s", osAnnot.c_str());
2715 : #endif
2716 714 : CPLXMLNode *psRoot = CPLParseXMLString(osAnnot);
2717 714 : CPLStripXMLNamespace(psRoot, nullptr, TRUE);
2718 : osTargetElement = CPLGetXMLValue(
2719 714 : psRoot, "=annotation.appinfo.targetElement", "");
2720 714 : CPLDestroyXMLNode(psRoot);
2721 : #ifdef DEBUG_VERBOSE
2722 : if (!osTargetElement.empty())
2723 : CPLDebug("GMLAS", "targetElement: %s",
2724 : osTargetElement.c_str());
2725 : #endif
2726 : }
2727 :
2728 202157 : XSTypeDefinition *poTypeDef = poElt->getTypeDefinition();
2729 :
2730 202157 : std::vector<XSElementDeclaration *> apoImplEltList;
2731 202157 : GetConcreteImplementationTypes(poElt, apoImplEltList);
2732 :
2733 : std::vector<XSElementDeclaration *> apoChildrenElements =
2734 202157 : GetConstraintChildrenElements(osElementXPath);
2735 :
2736 : // Special case for a GML geometry property
2737 202157 : OGRwkbGeometryType eGeomType =
2738 : wkbNone; // to make Visual Studio happy
2739 202157 : CPL_IGNORE_RET_VAL(eGeomType); // to make cppcheck happy
2740 :
2741 202157 : if (!apoChildrenElements.empty())
2742 : {
2743 3 : CreateNonNestedRelationship(
2744 : poElt, apoChildrenElements, oClass, nMaxOccurs,
2745 : bEltNameWillNeedPrefix,
2746 : false, // do not force junction table
2747 : true // special case for children elements
2748 : );
2749 : }
2750 :
2751 203001 : else if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
2752 847 : (eGeomType = GetOGRGeometryType(poTypeDef)) != wkbNone)
2753 : {
2754 632 : GMLASField oField;
2755 316 : oField.SetName(osPrefixedEltName);
2756 316 : oField.SetMinOccurs(nMinOccurs);
2757 316 : oField.SetMaxOccurs(nMaxOccurs);
2758 316 : oField.SetType(GMLAS_FT_GEOMETRY, szFAKEXS_GEOMETRY);
2759 316 : if (nMaxOccurs > 1 || nMaxOccurs == MAXOCCURS_UNLIMITED)
2760 : {
2761 : // Repeated geometry property can happen in some schemas
2762 : // like
2763 : // inspire.ec.europa.eu/schemas/ge_gp/4.0/GeophysicsCore.xsd
2764 : // or
2765 : // http://ngwd-bdnes.cits.nrcan.gc.ca/service/gwml/schemas/2.1/gwml2-flow.xsd
2766 21 : oField.SetGeomType(wkbUnknown);
2767 21 : oField.SetArray(true);
2768 : }
2769 : else
2770 295 : oField.SetGeomType(eGeomType);
2771 316 : oField.SetXPath(osElementXPath);
2772 316 : oField.SetDocumentation(GetAnnotationDoc(poElt));
2773 :
2774 316 : oClass.AddField(oField);
2775 : }
2776 :
2777 202414 : else if (IsGMLNamespace(osEltNS) &&
2778 576 : (eGeomType = GetOGRGeometryTypeFromGMLEltName(
2779 : osEltName)) != wkbNone)
2780 : {
2781 46 : GMLASField oField;
2782 23 : oField.SetName(osPrefixedEltName);
2783 23 : oField.SetMinOccurs(nMinOccurs);
2784 23 : oField.SetMaxOccurs(nMaxOccurs);
2785 :
2786 23 : oField.SetType(GMLAS_FT_GEOMETRY, szFAKEXS_GEOMETRY);
2787 23 : oField.SetGeomType(eGeomType);
2788 23 : oField.SetArray(nMaxOccurs > 1 ||
2789 : nMaxOccurs == MAXOCCURS_UNLIMITED);
2790 :
2791 23 : oField.SetXPath(osElementXPath);
2792 23 : oField.SetIncludeThisEltInBlob(true);
2793 23 : oField.SetDocumentation(GetAnnotationDoc(poElt));
2794 :
2795 23 : oClass.AddField(oField);
2796 : }
2797 :
2798 : // Any GML abstract type
2799 202243 : else if (poElt->getAbstract() && IsGMLNamespace(osEltNS) &&
2800 232 : osEltName != "_Feature" &&
2801 202243 : osEltName != "AbstractFeature" &&
2802 109 : osEltName != "AbstractTimeObject")
2803 : {
2804 180 : GMLASField oField;
2805 90 : oField.SetName(osPrefixedEltName);
2806 90 : oField.SetMinOccurs(nMinOccurs);
2807 90 : oField.SetMaxOccurs(nMaxOccurs);
2808 90 : if (osEltName == "AbstractGeometry")
2809 : {
2810 31 : oField.SetType(GMLAS_FT_GEOMETRY, szFAKEXS_GEOMETRY);
2811 31 : oField.SetGeomType(wkbUnknown);
2812 31 : oField.SetArray(nMaxOccurs > 1 ||
2813 : nMaxOccurs == MAXOCCURS_UNLIMITED);
2814 : }
2815 : else
2816 : {
2817 59 : oField.SetType(GMLAS_FT_ANYTYPE, szXS_ANY_TYPE);
2818 : }
2819 90 : oField.SetIncludeThisEltInBlob(true);
2820 90 : oField.SetDocumentation(GetAnnotationDoc(poElt));
2821 :
2822 851 : for (size_t j = 0; j < apoImplEltList.size(); j++)
2823 : {
2824 761 : XSElementDeclaration *poSubElt = apoImplEltList[j];
2825 761 : oField.AddAlternateXPath(
2826 1522 : oClass.GetXPath() + "/" +
2827 1522 : MakeXPath(transcode(poSubElt->getNamespace()),
2828 1522 : transcode(poSubElt->getName())));
2829 : }
2830 :
2831 90 : oClass.AddField(oField);
2832 : }
2833 :
2834 : // Are there substitution groups for this element ?
2835 : // or is this element already identified as being a top-level one ?
2836 418573 : else if (!apoImplEltList.empty() ||
2837 201503 : (m_oSetEltsForTopClass.find(poElt) !=
2838 216848 : m_oSetEltsForTopClass.end() &&
2839 15345 : m_oSetSimpleEnoughElts.find(poElt) ==
2840 217070 : m_oSetSimpleEnoughElts.end()))
2841 : {
2842 15562 : CreateNonNestedRelationship(
2843 : poElt, apoImplEltList, oClass, nMaxOccurs,
2844 : bEltNameWillNeedPrefix,
2845 : false, // do not force junction table
2846 : false // regular case
2847 : );
2848 : }
2849 :
2850 : // Abstract element without realizations !
2851 186163 : else if (poElt->getAbstract())
2852 : {
2853 : // Do nothing with it since it cannot be instantiated
2854 : // in a valid way.
2855 57 : CPLDebug("GMLAS",
2856 : "Ignoring %s that is abstract without realizations",
2857 : osElementXPath.c_str());
2858 : }
2859 :
2860 : // Simple type like string, int, etc...
2861 186106 : else if (poTypeDef->getTypeCategory() ==
2862 : XSTypeDefinition::SIMPLE_TYPE)
2863 : {
2864 141902 : XSSimpleTypeDefinition *poST =
2865 : reinterpret_cast<XSSimpleTypeDefinition *>(poTypeDef);
2866 283804 : GMLASField oField;
2867 141902 : SetFieldTypeAndWidthFromDefinition(poST, oField);
2868 141902 : oField.SetMinOccurs((bIsChoice) ? 0 : nMinOccurs);
2869 141902 : oField.SetMaxOccurs(nMaxOccurs);
2870 141902 : oField.SetDocumentation(GetAnnotationDoc(poElt));
2871 :
2872 141902 : bool bNeedAuxTable = false;
2873 141902 : const bool bIsList = (poST->getVariety() ==
2874 141902 : XSSimpleTypeDefinition::VARIETY_LIST);
2875 141902 : if (bIsList)
2876 : {
2877 294 : SetFieldTypeAndWidthFromDefinition(poST->getItemType(),
2878 : oField);
2879 582 : if (bRepeatedParticle || !m_bUseArrays ||
2880 288 : !IsCompatibleOfArray(oField.GetType()))
2881 : {
2882 : // Really particular case. This is a workaround
2883 58 : oField.SetType(GMLAS_FT_STRING, szXS_STRING);
2884 : }
2885 : else
2886 : {
2887 236 : oField.SetList(true);
2888 236 : oField.SetArray(true);
2889 : }
2890 : }
2891 :
2892 144357 : if (m_bUseArrays && bRepeatedParticle &&
2893 2455 : IsCompatibleOfArray(oField.GetType()))
2894 : {
2895 2178 : oField.SetArray(true);
2896 : }
2897 139724 : else if (bRepeatedParticle)
2898 : {
2899 289 : bNeedAuxTable = true;
2900 : }
2901 141902 : if (bNeedAuxTable)
2902 : {
2903 578 : GMLASFeatureClass oNestedClass;
2904 289 : oNestedClass.SetName(oClass.GetName() + "_" +
2905 : osPrefixedEltName);
2906 289 : oNestedClass.SetXPath(osElementXPath);
2907 578 : GMLASField oUniqueField;
2908 289 : oUniqueField.SetName("value");
2909 289 : oUniqueField.SetMinOccurs(1);
2910 289 : oUniqueField.SetMaxOccurs(1);
2911 289 : oUniqueField.SetXPath(osElementXPath);
2912 289 : oUniqueField.SetType(oField.GetType(),
2913 289 : oField.GetTypeName());
2914 289 : oNestedClass.AddField(oUniqueField);
2915 289 : oNestedClass.SetDocumentation(GetAnnotationDoc(poElt));
2916 :
2917 289 : oClass.AddNestedClass(oNestedClass);
2918 :
2919 289 : oField.SetType(GMLAS_FT_STRING, "");
2920 289 : oField.SetName(osPrefixedEltName);
2921 289 : oField.SetXPath(osElementXPath);
2922 289 : oField.SetCategory(
2923 : GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
2924 289 : oField.SetRelatedClassXPath(oField.GetXPath());
2925 289 : oClass.AddField(oField);
2926 : }
2927 : else
2928 : {
2929 141613 : oField.SetName(osPrefixedEltName);
2930 141613 : oField.SetXPath(osElementXPath);
2931 141613 : if (!bIsChoice && nMinOccurs > 0 && !poElt->getNillable())
2932 : {
2933 42045 : oField.SetNotNullable(true);
2934 : }
2935 141613 : oClass.AddField(oField);
2936 :
2937 : // If the element has minOccurs=0 and is nillable, then we
2938 : // need an extra field to be able to distinguish between the
2939 : // case of the missing element or the element with
2940 : // xsi:nil="true"
2941 141711 : if (nMinOccurs == 0 && poElt->getNillable() &&
2942 98 : !m_bUseNullState)
2943 : {
2944 196 : GMLASField oFieldNil;
2945 98 : oFieldNil.SetName(osPrefixedEltName + "_" + szNIL);
2946 98 : oFieldNil.SetXPath(osElementXPath + "/" + szAT_XSI_NIL);
2947 98 : oFieldNil.SetType(GMLAS_FT_BOOLEAN, "boolean");
2948 98 : oFieldNil.SetMinOccurs(0);
2949 98 : oFieldNil.SetMaxOccurs(1);
2950 98 : oClass.AddField(oFieldNil);
2951 : }
2952 : }
2953 : }
2954 :
2955 : // Complex type (element with attributes, composed element, etc...)
2956 44204 : else if (poTypeDef->getTypeCategory() ==
2957 : XSTypeDefinition::COMPLEX_TYPE)
2958 : {
2959 44204 : XSComplexTypeDefinition *poEltCT =
2960 : reinterpret_cast<XSComplexTypeDefinition *>(poTypeDef);
2961 44204 : std::vector<GMLASField> aoFields;
2962 44204 : bool bNothingMoreToDo = false;
2963 44204 : std::vector<GMLASFeatureClass> aoNestedClasses;
2964 :
2965 : const int nMinOccursEltParticle =
2966 44204 : poEltCT->getParticle()
2967 71081 : ? static_cast<int>(
2968 26877 : poEltCT->getParticle()->getMinOccurs())
2969 44204 : : -1;
2970 : const int nMaxOccursEltParticle =
2971 44204 : poEltCT->getParticle()
2972 71081 : ? (poEltCT->getParticle()->getMaxOccursUnbounded()
2973 53560 : ? MAXOCCURS_UNLIMITED
2974 : : static_cast<int>(
2975 26683 : poEltCT->getParticle()->getMaxOccurs()))
2976 44204 : : -1;
2977 :
2978 44204 : const bool bEltRepeatedParticle =
2979 44204 : nMaxOccursEltParticle > 1 ||
2980 : nMaxOccursEltParticle == MAXOCCURS_UNLIMITED;
2981 44204 : const bool bMoveNestedClassToTop =
2982 44204 : !bRepeatedParticle && !bEltRepeatedParticle;
2983 :
2984 : // Process attributes
2985 44204 : XSAttributeUseList *poAttrList = poEltCT->getAttributeUses();
2986 : const size_t nAttrListSize =
2987 44204 : (poAttrList != nullptr) ? poAttrList->size() : 0;
2988 65010 : for (size_t j = 0; j < nAttrListSize; ++j)
2989 : {
2990 20806 : XSAttributeUse *poAttr = poAttrList->elementAt(j);
2991 20806 : GMLASField oField;
2992 : CPLString osNamePrefix(bMoveNestedClassToTop
2993 : ? osPrefixedEltName
2994 20806 : : CPLString());
2995 20806 : if (!SetFieldFromAttribute(oField, poAttr, osElementXPath,
2996 : osNamePrefix))
2997 : {
2998 0 : return false;
2999 : }
3000 20806 : if (nMinOccurs == 0 || bIsChoice)
3001 : {
3002 7876 : oField.SetMinOccurs(0);
3003 7876 : oField.SetNotNullable(false);
3004 : }
3005 :
3006 20806 : if (IsIgnoredXPath(oField.GetXPath()))
3007 : {
3008 : #ifdef DEBUG_VERBOSE
3009 : CPLDebug("GMLAS", "%s is in ignored xpaths",
3010 : oField.GetXPath().c_str());
3011 : #endif
3012 145 : if (!oField.GetFixedValue().empty() ||
3013 66 : !oField.GetDefaultValue().empty())
3014 : {
3015 14 : oField.SetIgnored();
3016 : }
3017 : else
3018 : {
3019 65 : continue;
3020 : }
3021 : }
3022 :
3023 20741 : aoFields.push_back(oField);
3024 : }
3025 :
3026 : // Deal with anyAttributes (or any element that also imply it)
3027 44204 : XSWildcard *poAttrWildcard = poEltCT->getAttributeWildcard();
3028 44204 : if (poAttrWildcard != nullptr)
3029 : {
3030 592 : GMLASField oField;
3031 296 : oField.SetType(GMLASField::GetTypeFromString(szXS_STRING),
3032 : szFAKEXS_JSON_DICT);
3033 296 : if (!bMoveNestedClassToTop)
3034 : {
3035 71 : oField.SetName("anyAttributes");
3036 : }
3037 : else
3038 : {
3039 225 : oField.SetName(osPrefixedEltName + "_anyAttributes");
3040 : }
3041 296 : oField.SetXPath(osElementXPath + "/" + szAT_ANY_ATTR);
3042 296 : oField.SetDocumentation(
3043 592 : GetAnnotationDoc(poAttrWildcard->getAnnotation()));
3044 :
3045 296 : aoFields.push_back(oField);
3046 : }
3047 :
3048 44204 : XSSimpleTypeDefinition *poST = poEltCT->getSimpleType();
3049 44204 : if (poST != nullptr)
3050 : {
3051 : /* Case of an element, generally with attributes */
3052 :
3053 34026 : GMLASField oField;
3054 17013 : SetFieldTypeAndWidthFromDefinition(poST, oField);
3055 160 : if (bRepeatedParticle && nAttrListSize == 0 &&
3056 17219 : m_bUseArrays && IsCompatibleOfArray(oField.GetType()) &&
3057 46 : oField.GetCategory() !=
3058 : GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK)
3059 : {
3060 : /* We have a complex type, but no attributes, and */
3061 : /* compatible of arrays, so move it to top level! */
3062 46 : oField.SetName(osPrefixedEltName);
3063 46 : oField.SetArray(true);
3064 46 : oField.SetMinOccurs(nMinOccurs);
3065 46 : oField.SetMaxOccurs(nMaxOccurs);
3066 : }
3067 16967 : else if (bRepeatedParticle)
3068 : {
3069 114 : oField.SetName("value");
3070 114 : oField.SetMinOccurs(1);
3071 114 : oField.SetMaxOccurs(1);
3072 114 : oField.SetNotNullable(true);
3073 : }
3074 : else
3075 : {
3076 16853 : if (nMinOccurs == 0)
3077 : {
3078 9965 : for (size_t j = 0; j < aoFields.size(); j++)
3079 : {
3080 5123 : aoFields[j].SetMinOccurs(0);
3081 5123 : aoFields[j].SetNotNullable(false);
3082 : }
3083 : }
3084 :
3085 16853 : oField.SetName(osPrefixedEltName);
3086 16853 : oField.SetMinOccurs((bIsChoice) ? 0 : nMinOccurs);
3087 16853 : oField.SetMaxOccurs(nMaxOccurs);
3088 :
3089 : // If the element has minOccurs=0 and is nillable, then
3090 : // we need an extra field to be able to distinguish
3091 : // between the case of the missing element or the
3092 : // element with xsi:nil="true"
3093 17061 : if (nMinOccurs == 0 && poElt->getNillable() &&
3094 208 : !m_bUseNullState)
3095 : {
3096 416 : GMLASField oFieldNil;
3097 208 : oFieldNil.SetName(osPrefixedEltName + "_" + szNIL);
3098 208 : oFieldNil.SetXPath(osElementXPath + "/" +
3099 : szAT_XSI_NIL);
3100 208 : oFieldNil.SetType(GMLAS_FT_BOOLEAN, "boolean");
3101 208 : oFieldNil.SetMinOccurs(0);
3102 208 : oFieldNil.SetMaxOccurs(1);
3103 208 : aoFields.push_back(oFieldNil);
3104 : }
3105 : }
3106 17013 : oField.SetXPath(osElementXPath);
3107 17013 : oField.SetDocumentation(GetAnnotationDoc(poElt));
3108 :
3109 17013 : aoFields.push_back(oField);
3110 17013 : if (oField.IsArray())
3111 : {
3112 46 : oClass.AddField(oField);
3113 46 : bNothingMoreToDo = true;
3114 : }
3115 : }
3116 27191 : else if (IsAnyType(poEltCT))
3117 : {
3118 626 : GMLASField oField;
3119 313 : oField.SetType(GMLAS_FT_ANYTYPE, szXS_ANY_TYPE);
3120 313 : if (bRepeatedParticle)
3121 : {
3122 52 : oField.SetName("value");
3123 52 : oField.SetMinOccurs(1);
3124 52 : oField.SetMaxOccurs(1);
3125 52 : oField.SetNotNullable(true);
3126 : }
3127 : else
3128 : {
3129 261 : if (nMinOccurs == 0)
3130 : {
3131 216 : for (size_t j = 0; j < aoFields.size(); j++)
3132 : {
3133 48 : aoFields[j].SetMinOccurs(0);
3134 48 : aoFields[j].SetNotNullable(false);
3135 : }
3136 : }
3137 :
3138 261 : oField.SetName(osPrefixedEltName);
3139 261 : oField.SetMinOccurs(nMinOccurs);
3140 261 : oField.SetMaxOccurs(nMaxOccurs);
3141 : }
3142 313 : oField.SetXPath(osElementXPath);
3143 313 : oField.SetDocumentation(GetAnnotationDoc(poElt));
3144 :
3145 313 : aoFields.push_back(oField);
3146 : }
3147 :
3148 : // Is it an element that we already visited ? (cycle)
3149 53442 : else if (poEltCT->getParticle() != nullptr &&
3150 0 : oSetVisitedModelGroups.find(
3151 26564 : poEltCT->getParticle()->getModelGroupTerm()) !=
3152 53442 : oSetVisitedModelGroups.end())
3153 : {
3154 0 : CreateNonNestedRelationship(
3155 : poElt, apoImplEltList, oClass,
3156 : bMoveNestedClassToTop ? 1 : MAXOCCURS_UNLIMITED,
3157 : bEltNameWillNeedPrefix,
3158 : true, // force junction table
3159 : false // regular case
3160 : );
3161 :
3162 0 : bNothingMoreToDo = true;
3163 : }
3164 :
3165 : else
3166 : {
3167 26878 : GMLASFeatureClass oNestedClass;
3168 26878 : oNestedClass.SetName(oClass.GetName() + "_" +
3169 : osPrefixedEltName);
3170 26878 : oNestedClass.SetXPath(osElementXPath);
3171 26878 : oNestedClass.SetDocumentation(GetAnnotationDoc(poElt));
3172 :
3173 : // NULL can happen, for example for gml:ReferenceType
3174 : // that is an empty sequence with just attributes
3175 26878 : if (poEltCT->getParticle() != nullptr)
3176 : {
3177 : #ifdef DEBUG_VERBOSE
3178 : CPLDebug("GMLAS", "Exploring %s",
3179 : osElementXPath.c_str());
3180 : #endif
3181 : std::set<XSModelGroup *> oSetNewVisitedModelGroups(
3182 26564 : oSetVisitedModelGroups);
3183 :
3184 : std::map<CPLString, int>
3185 26564 : oMapCountOccurrencesOfSameNameSub;
3186 26564 : BuildMapCountOccurrencesOfSameName(
3187 : poEltCT->getParticle()->getModelGroupTerm(),
3188 : oMapCountOccurrencesOfSameNameSub);
3189 :
3190 26564 : if (!ExploreModelGroup(
3191 : poEltCT->getParticle()->getModelGroupTerm(),
3192 : nullptr, oNestedClass, nRecursionCounter + 1,
3193 : oSetNewVisitedModelGroups, poModel,
3194 : oMapCountOccurrencesOfSameNameSub))
3195 : {
3196 0 : return false;
3197 : }
3198 : }
3199 :
3200 : // If we have a element of type gml:ReferenceType that has
3201 : // a targetElement in its annotation.appinfo, then create
3202 : // a dedicated field to have cross-layer relationships.
3203 53921 : if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
3204 53921 : transcode(poTypeDef->getName()) == "ReferenceType" &&
3205 56 : !osTargetElement.empty())
3206 : {
3207 : XSElementDeclaration *poTargetElt =
3208 10 : GetTopElementDeclarationFromXPath(osTargetElement,
3209 : poModel);
3210 : // TODO: even for non abstract we should probably
3211 : // handle substitutions
3212 18 : if (poTargetElt != nullptr &&
3213 8 : !poTargetElt->getAbstract())
3214 : {
3215 4 : bool bHasRequiredId = false;
3216 : XSComplexTypeDefinition *poTargetEltCT =
3217 4 : IsEltCompatibleOfFC(poTargetElt);
3218 4 : if (poTargetEltCT)
3219 : {
3220 : XSAttributeUseList *poTargetEltAttrList =
3221 4 : poTargetEltCT->getAttributeUses();
3222 : const size_t nTEAttrListSize =
3223 : (poTargetEltAttrList != nullptr)
3224 4 : ? poTargetEltAttrList->size()
3225 4 : : 0;
3226 6 : for (size_t j = 0; j < nTEAttrListSize; ++j)
3227 : {
3228 : XSAttributeUse *poTEAttr =
3229 4 : poTargetEltAttrList->elementAt(j);
3230 : XSAttributeDeclaration *poTEAttrDecl =
3231 4 : poTEAttr->getAttrDeclaration();
3232 : XSSimpleTypeDefinition *poTEAttrType =
3233 4 : poTEAttrDecl->getTypeDefinition();
3234 8 : if (transcode(poTEAttrType->getName()) ==
3235 12 : szXS_ID &&
3236 4 : poTEAttr->getRequired())
3237 : {
3238 2 : bHasRequiredId = true;
3239 2 : break;
3240 : }
3241 : }
3242 : }
3243 4 : if (bHasRequiredId && !m_bAlwaysGenerateOGRId)
3244 : {
3245 : // If the element is nillable, then we
3246 : // need an extra field to be able to distinguish
3247 : // between the case of the missing element or
3248 : // the element with xsi:nil="true"
3249 2 : if (poElt->getNillable() && !m_bUseNullState)
3250 : {
3251 0 : GMLASField oFieldNil;
3252 0 : oFieldNil.SetName(osPrefixedEltName + "_" +
3253 : szNIL);
3254 0 : oFieldNil.SetXPath(osElementXPath + "/" +
3255 : szAT_XSI_NIL);
3256 0 : oFieldNil.SetType(GMLAS_FT_BOOLEAN,
3257 : "boolean");
3258 0 : oFieldNil.SetMinOccurs(0);
3259 0 : oFieldNil.SetMaxOccurs(1);
3260 0 : aoFields.push_back(oFieldNil);
3261 : }
3262 :
3263 4 : GMLASField oField;
3264 : // Fake xpath
3265 2 : oField.SetXPath(
3266 : GMLASField::
3267 4 : MakePKIDFieldXPathFromXLinkHrefXPath(
3268 4 : osElementXPath + "/" +
3269 : szAT_XLINK_HREF));
3270 2 : oField.SetName(osPrefixedEltName +
3271 : szPKID_SUFFIX);
3272 2 : oField.SetMinOccurs(0);
3273 2 : oField.SetMaxOccurs(1);
3274 2 : oField.SetType(GMLAS_FT_STRING, szXS_STRING);
3275 2 : oField.SetCategory(
3276 : GMLASField::
3277 : PATH_TO_CHILD_ELEMENT_WITH_LINK);
3278 2 : oField.SetRelatedClassXPath(osTargetElement);
3279 2 : aoFields.push_back(oField);
3280 : }
3281 : }
3282 10 : else if (poTargetElt != nullptr &&
3283 4 : poTargetElt->getAbstract())
3284 : {
3285 : // If the element is nillable, then we
3286 : // need an extra field to be able to distinguish
3287 : // between the case of the missing element or the
3288 : // element with xsi:nil="true"
3289 4 : if (poElt->getNillable() && !m_bUseNullState)
3290 : {
3291 4 : GMLASField oFieldNil;
3292 2 : oFieldNil.SetName(osPrefixedEltName + "_" +
3293 : szNIL);
3294 2 : oFieldNil.SetXPath(osElementXPath + "/" +
3295 : szAT_XSI_NIL);
3296 2 : oFieldNil.SetType(GMLAS_FT_BOOLEAN, "boolean");
3297 2 : oFieldNil.SetMinOccurs(0);
3298 2 : oFieldNil.SetMaxOccurs(1);
3299 2 : aoFields.push_back(oFieldNil);
3300 : }
3301 :
3302 : // e.g importing
3303 : // http://inspire.ec.europa.eu/schemas/ad/4.0
3304 : // references bu-base:AbstractConstruction, but
3305 : // sometimes there are no realization available for
3306 : // it, so no need to be verbose about that.
3307 : std::vector<XSElementDeclaration *>
3308 8 : apoImplTargetEltList;
3309 4 : GetConcreteImplementationTypes(
3310 : poTargetElt, apoImplTargetEltList);
3311 4 : if (!apoImplTargetEltList.empty())
3312 : {
3313 0 : CPLDebug("GMLAS",
3314 : "Not handled: targetElement %s of %s "
3315 : "is abstract but has substitutions",
3316 : osTargetElement.c_str(),
3317 : osElementXPath.c_str());
3318 : }
3319 : }
3320 : else
3321 : {
3322 : // This shouldn't happen with consistent schemas
3323 : // but as targetElement is in <annotation>, no
3324 : // general-purpose XSD validator can ensure this
3325 2 : CPLDebug("GMLAS",
3326 : "%s is a targetElement of %s, "
3327 : "but cannot be found",
3328 : osTargetElement.c_str(),
3329 : osElementXPath.c_str());
3330 : }
3331 : }
3332 :
3333 : // Can we move the nested class(es) one level up ?
3334 26878 : if (bMoveNestedClassToTop)
3335 : {
3336 : // Case of an element like
3337 : // <xs:element name="foo">
3338 : // <xs:complexType>
3339 : // <xs:sequence>
3340 :
3341 : const std::vector<GMLASField> &osNestedClassFields =
3342 22911 : oNestedClass.GetFields();
3343 412337 : for (size_t j = 0; j < osNestedClassFields.size(); j++)
3344 : {
3345 778852 : GMLASField oField(osNestedClassFields[j]);
3346 389426 : oField.SetName(osPrefixedEltName + "_" +
3347 389426 : oField.GetName());
3348 991278 : if (nMinOccurs == 0 ||
3349 601852 : (poEltCT->getParticle() != nullptr &&
3350 300926 : poEltCT->getParticle()->getMinOccurs() == 0))
3351 : {
3352 88671 : oField.SetMinOccurs(0);
3353 88671 : oField.SetNotNullable(false);
3354 : }
3355 389426 : aoFields.push_back(oField);
3356 : }
3357 :
3358 22911 : aoNestedClasses = oNestedClass.GetNestedClasses();
3359 : }
3360 : else
3361 : {
3362 : // Case of an element like
3363 : // <xs:element name="foo">
3364 : // <xs:complexType>
3365 : // <xs:sequence maxOccurs="unbounded">
3366 : // or
3367 : // <xs:element name="foo" maxOccurs="unbounded">
3368 : // <xs:complexType>
3369 : // <xs:sequence>
3370 : // or even
3371 : // <xs:element name="foo" maxOccurs="unbounded">
3372 : // <xs:complexType>
3373 : // <xs:sequence maxOccurs="unbounded">
3374 7595 : if (m_bUseArrays && nAttrListSize == 0 &&
3375 6615 : oNestedClass.GetNestedClasses().empty() &&
3376 3120 : oNestedClass.GetFields().size() == 1 &&
3377 141 : IsCompatibleOfArray(
3378 8067 : oNestedClass.GetFields()[0].GetType()) &&
3379 47 : oNestedClass.GetFields()[0].GetCategory() !=
3380 : GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK)
3381 : {
3382 : // In the case the sequence has a single element,
3383 : // compatible of array type, and no attribute and
3384 : // no nested classes, then add an array attribute
3385 : // at the top-level
3386 92 : GMLASField oField(oNestedClass.GetFields()[0]);
3387 46 : oField.SetName(osPrefixedEltName + "_" +
3388 46 : oField.GetName());
3389 92 : if (oField.GetMaxOccurs() == 1 &&
3390 92 : bEltRepeatedParticle &&
3391 46 : poEltCT->getParticle() != nullptr)
3392 : {
3393 46 : oField.SetMaxOccurs(nMaxOccursEltParticle);
3394 : }
3395 46 : oField.SetArray(true);
3396 46 : oClass.AddField(oField);
3397 : }
3398 : else
3399 : {
3400 3921 : if (!aoFields.empty() && bEltRepeatedParticle)
3401 : {
3402 : // We have attributes and the sequence is
3403 : // repeated
3404 : // <xs:element name="foo"
3405 : // maxOccurs="unbounded">
3406 : // <xs:complexType>
3407 : // <xs:sequence maxOccurs="unbounded">
3408 : // ...
3409 : // </xs:sequence>
3410 : // <xs:attribute .../>
3411 : // </xs:complexType>
3412 : // </xs:element>
3413 : // So we need to create an
3414 : // intermediate class to store them
3415 106 : GMLASFeatureClass oIntermediateNestedClass;
3416 53 : oIntermediateNestedClass.SetName(
3417 106 : oClass.GetName() + "_" + osPrefixedEltName);
3418 53 : oIntermediateNestedClass.SetXPath(
3419 : osElementXPath);
3420 :
3421 53 : oIntermediateNestedClass.PrependFields(
3422 : aoFields);
3423 :
3424 159 : oNestedClass.SetName(oClass.GetName() + "_" +
3425 106 : osPrefixedEltName +
3426 : "_sequence");
3427 106 : oNestedClass.SetXPath(oNestedClass.GetXPath() +
3428 106 : szEXTRA_SUFFIX +
3429 : "sequence");
3430 53 : oNestedClass.SetIsRepeatedSequence(true);
3431 :
3432 106 : GMLASField oField;
3433 53 : oField.SetXPath(osElementXPath);
3434 53 : oField.SetCategory(
3435 : GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
3436 53 : if (nMaxOccursEltParticle != 1)
3437 53 : oField.SetRepetitionOnSequence(true);
3438 53 : oField.SetMinOccurs(nMinOccursEltParticle);
3439 53 : oField.SetMaxOccurs(nMaxOccursEltParticle);
3440 53 : oField.SetRelatedClassXPath(
3441 : oNestedClass.GetXPath());
3442 53 : oIntermediateNestedClass.AddField(oField);
3443 :
3444 53 : oIntermediateNestedClass.AddNestedClass(
3445 : oNestedClass);
3446 :
3447 53 : oClass.AddNestedClass(oIntermediateNestedClass);
3448 : }
3449 : else
3450 : {
3451 3868 : oNestedClass.SetIsRepeatedSequence(
3452 : bEltRepeatedParticle);
3453 3868 : oNestedClass.PrependFields(aoFields);
3454 :
3455 3868 : oClass.AddNestedClass(oNestedClass);
3456 : }
3457 :
3458 7842 : GMLASField oField;
3459 3921 : oField.SetName(osPrefixedEltName);
3460 3921 : oField.SetXPath(osElementXPath);
3461 3921 : if (bRepeatedParticle)
3462 : {
3463 3820 : if (poEltCT->getParticle() != nullptr)
3464 : {
3465 3792 : oField.SetMinOccurs(ComposeMinOccurs(
3466 : nMinOccurs, nMinOccursEltParticle));
3467 3792 : oField.SetMaxOccurs(ComposeMaxOccurs(
3468 : nMaxOccurs, nMaxOccursEltParticle));
3469 : }
3470 : else
3471 : {
3472 28 : oField.SetMinOccurs(nMinOccurs);
3473 28 : oField.SetMaxOccurs(nMaxOccurs);
3474 : }
3475 : }
3476 101 : else if (poEltCT->getParticle() != nullptr)
3477 : {
3478 101 : if (nMaxOccursEltParticle != 1)
3479 101 : oField.SetRepetitionOnSequence(true);
3480 101 : oField.SetMinOccurs(nMinOccursEltParticle);
3481 101 : oField.SetMaxOccurs(nMaxOccursEltParticle);
3482 : }
3483 3921 : oField.SetCategory(
3484 : GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
3485 3921 : oField.SetRelatedClassXPath(oField.GetXPath());
3486 3921 : oClass.AddField(oField);
3487 : }
3488 :
3489 3967 : bNothingMoreToDo = true;
3490 : }
3491 : }
3492 :
3493 44204 : if (bNothingMoreToDo)
3494 : {
3495 : // Nothing to do
3496 : }
3497 40191 : else if (bRepeatedParticle)
3498 : {
3499 332 : GMLASFeatureClass oNestedClass;
3500 166 : oNestedClass.SetName(oClass.GetName() + "_" +
3501 : osPrefixedEltName);
3502 166 : oNestedClass.SetXPath(osElementXPath);
3503 166 : oNestedClass.AppendFields(aoFields);
3504 166 : oNestedClass.SetDocumentation(GetAnnotationDoc(poElt));
3505 166 : oClass.AddNestedClass(oNestedClass);
3506 :
3507 332 : GMLASField oField;
3508 166 : oField.SetName(osPrefixedEltName);
3509 166 : oField.SetXPath(osElementXPath);
3510 166 : oField.SetMinOccurs((bIsChoice) ? 0 : nMinOccurs);
3511 166 : oField.SetMaxOccurs(nMaxOccurs);
3512 166 : oField.SetCategory(
3513 : GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
3514 166 : oField.SetRelatedClassXPath(oField.GetXPath());
3515 166 : oClass.AddField(oField);
3516 : }
3517 : else
3518 : {
3519 40025 : oClass.AppendFields(aoFields);
3520 42993 : for (size_t j = 0; j < aoNestedClasses.size(); j++)
3521 : {
3522 2968 : oClass.AddNestedClass(aoNestedClasses[j]);
3523 : }
3524 : }
3525 : }
3526 : }
3527 17526 : else if (poParticle->getTermType() == XSParticle::TERM_MODELGROUP)
3528 : {
3529 17365 : XSModelGroup *psSubModelGroup = poParticle->getModelGroupTerm();
3530 17365 : if (bRepeatedParticle)
3531 : {
3532 1683 : GMLASFeatureClass oNestedClass;
3533 1683 : CPLString osGroupName;
3534 : XSModelGroupDefinition *psGroupDefinition =
3535 1683 : GetGroupDefinition(psSubModelGroup);
3536 1683 : if (psGroupDefinition != nullptr)
3537 : {
3538 141 : osGroupName = transcode(psGroupDefinition->getName());
3539 141 : oNestedClass.SetDocumentation(
3540 282 : GetAnnotationDoc(psGroupDefinition->getAnnotation()));
3541 : }
3542 : else
3543 : {
3544 : // Is it a <xs:choice maxOccurs=">1|unbounded"
3545 1542 : if (psSubModelGroup->getCompositor() ==
3546 : XSModelGroup::COMPOSITOR_CHOICE)
3547 : {
3548 : std::set<XSModelGroup *> oSetNewVisitedModelGroups(
3549 1542 : oSetVisitedModelGroups);
3550 1542 : GMLASFeatureClass oTmpClass;
3551 1542 : oTmpClass.SetName(oClass.GetName());
3552 1542 : oTmpClass.SetXPath(oClass.GetXPath());
3553 1542 : if (!ExploreModelGroup(psSubModelGroup, nullptr,
3554 : oTmpClass, nRecursionCounter + 1,
3555 : oSetNewVisitedModelGroups,
3556 : poModel,
3557 : oMapCountOccurrencesOfSameName))
3558 : {
3559 0 : return false;
3560 : }
3561 1542 : bool bHasArray = false;
3562 : std::vector<GMLASField> &oTmpFields =
3563 1542 : oTmpClass.GetFields();
3564 151220 : for (size_t j = 0; j < oTmpFields.size(); ++j)
3565 : {
3566 149748 : if (oTmpFields[j].IsArray())
3567 : {
3568 70 : bHasArray = true;
3569 70 : break;
3570 : }
3571 : }
3572 1542 : if (!bHasArray)
3573 : {
3574 150940 : for (size_t j = 0; j < oTmpFields.size(); ++j)
3575 : {
3576 149468 : oTmpFields[j].SetMayAppearOutOfOrder(true);
3577 149468 : oClass.AddField(oTmpFields[j]);
3578 : }
3579 1472 : return true;
3580 : }
3581 : }
3582 :
3583 70 : nGroup++;
3584 70 : osGroupName = CPLSPrintf("_group%d", nGroup);
3585 : }
3586 211 : oNestedClass.SetName(oClass.GetName() + "_" + osGroupName);
3587 211 : oNestedClass.SetIsGroup(true);
3588 211 : oNestedClass.SetIsRepeatedSequence(true);
3589 : // Caution: we will change it afterwards !
3590 211 : oNestedClass.SetXPath(oClass.GetXPath());
3591 : std::set<XSModelGroup *> oSetNewVisitedModelGroups(
3592 211 : oSetVisitedModelGroups);
3593 211 : if (!ExploreModelGroup(psSubModelGroup, nullptr, oNestedClass,
3594 : nRecursionCounter + 1,
3595 : oSetNewVisitedModelGroups, poModel,
3596 : oMapCountOccurrencesOfSameName))
3597 : {
3598 0 : return false;
3599 : }
3600 : // This is a nasty hack. We set a unique fake xpath *AFTER*
3601 : // processing the group, so that we can add a fake GROUP field
3602 : // pointing to the nested class
3603 211 : oNestedClass.SetXPath(oClass.GetXPath() + szEXTRA_SUFFIX +
3604 : osGroupName);
3605 :
3606 257 : if (m_bUseArrays && oNestedClass.GetFields().size() == 1 &&
3607 46 : IsCompatibleOfArray(oNestedClass.GetFields()[0].GetType()))
3608 : {
3609 92 : GMLASField oField(oNestedClass.GetFields()[0]);
3610 46 : oField.SetMinOccurs(
3611 : ComposeMinOccurs(oField.GetMinOccurs(), nMinOccurs));
3612 46 : oField.SetMaxOccurs(
3613 : ComposeMaxOccurs(oField.GetMaxOccurs(), nMaxOccurs));
3614 46 : oField.SetArray(true);
3615 46 : oClass.AddField(oField);
3616 : }
3617 : else
3618 : {
3619 165 : oClass.AddNestedClass(oNestedClass);
3620 :
3621 330 : GMLASField oField;
3622 165 : oField.SetCategory(GMLASField::GROUP);
3623 165 : oField.SetMinOccurs(nMinOccurs);
3624 165 : oField.SetMaxOccurs(nMaxOccurs);
3625 165 : oField.SetRelatedClassXPath(oNestedClass.GetXPath());
3626 165 : oClass.AddField(oField);
3627 : }
3628 : }
3629 : else
3630 : {
3631 : std::set<XSModelGroup *> oSetNewVisitedModelGroups(
3632 15682 : oSetVisitedModelGroups);
3633 15682 : if (!ExploreModelGroup(psSubModelGroup, nullptr, oClass,
3634 : nRecursionCounter + 1,
3635 : oSetNewVisitedModelGroups, poModel,
3636 : oMapCountOccurrencesOfSameName))
3637 : {
3638 0 : return false;
3639 : }
3640 : }
3641 : }
3642 161 : else if (poParticle->getTermType() == XSParticle::TERM_WILDCARD)
3643 : {
3644 : /* Special case for a layer that matches everything, as found */
3645 : /* in swe:extension */
3646 161 : XSWildcard *poWildcard = poParticle->getWildcardTerm();
3647 322 : GMLASField oField;
3648 161 : oField.SetXPath(oClass.GetXPath() + szMATCH_ALL);
3649 161 : oField.SetName("value");
3650 161 : oField.SetType(GMLAS_FT_ANYTYPE, szXS_ANY_TYPE);
3651 161 : oField.SetIncludeThisEltInBlob(true);
3652 161 : oField.SetMinOccurs(nMinOccurs);
3653 161 : oField.SetMaxOccurs(1);
3654 161 : oField.SetDocumentation(
3655 322 : GetAnnotationDoc(poWildcard->getAnnotation()));
3656 161 : oClass.AddField(oField);
3657 : }
3658 : }
3659 :
3660 45644 : return true;
3661 : }
|