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 173 : static XSModel *getGrammarPool(XMLGrammarPool *pool)
20 : {
21 : bool changed;
22 346 : 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 838 : GMLASPrefixMappingHander(
57 : std::map<CPLString, CPLString> &oMapURIToPrefix,
58 : const std::map<CPLString, CPLString> &oMapDocNSURIToPrefix,
59 : CPLString &osGMLVersionFound)
60 838 : : m_oMapURIToPrefix(oMapURIToPrefix),
61 : m_oMapDocNSURIToPrefix(oMapDocNSURIToPrefix),
62 838 : m_osGMLVersionFound(osGMLVersionFound)
63 : {
64 838 : }
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 803529 : void GMLASPrefixMappingHander::startElement(const XMLCh *const uri,
80 : const XMLCh *const localname,
81 : const XMLCh *const /*qname*/,
82 : const Attributes &attrs)
83 : {
84 803529 : if (!m_osGMLVersionFound.empty())
85 20339 : return;
86 :
87 1566380 : const CPLString osURI(transcode(uri));
88 1566380 : const CPLString osLocalname(transcode(localname));
89 783190 : if (osURI == szXS_URI && osLocalname == "schema")
90 : {
91 514 : bool bIsGML = false;
92 1028 : std::string osVersion;
93 2255 : for (unsigned int i = 0; i < attrs.getLength(); i++)
94 : {
95 3482 : const std::string osAttrLocalName(transcode(attrs.getLocalName(i)));
96 1741 : if (osAttrLocalName == "targetNamespace")
97 : {
98 481 : bIsGML = transcode(attrs.getValue(i)) == szGML_URI;
99 : }
100 1260 : else if (osAttrLocalName == "version")
101 : {
102 280 : osVersion = transcode(attrs.getValue(i));
103 : }
104 : }
105 514 : if (bIsGML && !osVersion.empty())
106 : {
107 0 : m_osGMLVersionFound = std::move(osVersion);
108 : }
109 : }
110 : }
111 :
112 : /************************************************************************/
113 : /* startPrefixMapping() */
114 : /************************************************************************/
115 :
116 2167 : void GMLASPrefixMappingHander::startPrefixMapping(const XMLCh *const prefix,
117 : const XMLCh *const uri)
118 : {
119 4334 : const CPLString osURI(transcode(uri));
120 4334 : CPLString osPrefix(transcode(prefix));
121 2167 : if (osPrefix.empty())
122 : {
123 176 : const auto oIter = m_oMapDocNSURIToPrefix.find(osURI);
124 176 : if (oIter != m_oMapDocNSURIToPrefix.end())
125 : {
126 8 : osPrefix = oIter->second;
127 : }
128 : }
129 2167 : if (!osPrefix.empty())
130 : {
131 1999 : const auto oIter = m_oMapURIToPrefix.find(osURI);
132 1999 : if (oIter == m_oMapURIToPrefix.end())
133 : {
134 548 : m_oMapURIToPrefix[osURI] = osPrefix;
135 548 : CPLDebug("GMLAS", "Registering prefix=%s for uri=%s",
136 : osPrefix.c_str(), osURI.c_str());
137 : }
138 1451 : else if (oIter->second != osPrefix)
139 : {
140 106 : CPLDebug("GMLAS",
141 : "Existing prefix=%s for uri=%s (new prefix %s not used)",
142 53 : oIter->second.c_str(), osURI.c_str(), osPrefix.c_str());
143 : }
144 : }
145 2167 : }
146 :
147 : /************************************************************************/
148 : /* CollectNamespacePrefixes() */
149 : /************************************************************************/
150 :
151 838 : 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 1676 : 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 838 : SAX2XMLReader *poReader = XMLReaderFactory::createXMLReader();
162 :
163 : GMLASPrefixMappingHander contentHandler(
164 1676 : oMapURIToPrefix, oMapDocNSURIToPrefix, osGMLVersionFound);
165 838 : poReader->setContentHandler(&contentHandler);
166 :
167 1676 : GMLASErrorHandler oErrorHandler;
168 838 : poReader->setErrorHandler(&oErrorHandler);
169 :
170 838 : poReader->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution, true);
171 :
172 1676 : std::string osErrorMsg;
173 : try
174 : {
175 838 : 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 838 : if (!osErrorMsg.empty())
197 : {
198 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", osErrorMsg.c_str());
199 : }
200 838 : delete poReader;
201 838 : }
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 183 : GMLASAnalyzerEntityResolver(
214 : const CPLString &osBasePath,
215 : std::map<CPLString, CPLString> &oMapURIToPrefix,
216 : const std::map<CPLString, CPLString> &oMapDocNSURIToPrefix,
217 : GMLASXSDCache &oCache)
218 183 : : GMLASBaseEntityResolver(osBasePath, oCache),
219 : m_oMapURIToPrefix(oMapURIToPrefix),
220 183 : m_oMapDocNSURIToPrefix(oMapDocNSURIToPrefix)
221 : {
222 183 : }
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 838 : void GMLASAnalyzerEntityResolver::DoExtraSchemaProcessing(
234 : const CPLString &osFilename, const std::shared_ptr<VSIVirtualHandle> &fp)
235 : {
236 838 : CollectNamespacePrefixes(osFilename, fp, m_oMapURIToPrefix,
237 838 : m_oMapDocNSURIToPrefix, m_osGMLVersionFound);
238 838 : fp->Seek(0, SEEK_SET);
239 838 : }
240 :
241 : /************************************************************************/
242 : /* GMLASSchemaAnalyzer() */
243 : /************************************************************************/
244 :
245 187 : GMLASSchemaAnalyzer::GMLASSchemaAnalyzer(
246 : GMLASXPathMatcher &oIgnoredXPathMatcher,
247 : GMLASXPathMatcher &oChildrenElementsConstraintsXPathMatcher,
248 : const std::map<CPLString, std::vector<CPLString>>
249 : &oMapChildrenElementsConstraints,
250 : GMLASXPathMatcher &oForcedFlattenedXPathMatcher,
251 187 : 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 187 : m_bAlwaysGenerateOGRId(ALWAYS_GENERATE_OGR_ID_DEFAULT)
264 : {
265 : // A few hardcoded namespace uri->prefix mappings
266 187 : m_oMapURIToPrefix[szXMLNS_URI] = szXMLNS_PREFIX;
267 187 : m_oMapURIToPrefix[szXSI_URI] = szXSI_PREFIX;
268 187 : }
269 :
270 : /************************************************************************/
271 : /* GetPrefix() */
272 : /************************************************************************/
273 :
274 403056 : CPLString GMLASSchemaAnalyzer::GetPrefix(const CPLString &osNamespaceURI)
275 : {
276 403056 : if (osNamespaceURI.empty())
277 29883 : return "";
278 373173 : const auto oIter = m_oMapURIToPrefix.find(osNamespaceURI);
279 373173 : if (oIter != m_oMapURIToPrefix.end())
280 373170 : 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 387646 : CPLString GMLASSchemaAnalyzer::MakeXPath(const CPLString &osNamespaceURI,
315 : const CPLString &osName)
316 : {
317 775292 : const CPLString osPrefix(GetPrefix(osNamespaceURI));
318 387646 : if (osPrefix.empty())
319 29879 : return osName;
320 715534 : 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 6407 : bool GMLASSchemaAnalyzer::LaunderFieldNames(GMLASFeatureClass &oClass)
353 : {
354 6407 : 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 12814 : std::map<CPLString, std::list<int>> oMapNameToFieldIndex;
362 194062 : for (int i = 0; i < static_cast<int>(aoFields.size()); i++)
363 : {
364 187655 : if (aoFields[i].GetCategory() == GMLASField::REGULAR)
365 : {
366 168184 : oMapNameToFieldIndex[aoFields[i].GetName()].push_back(i);
367 : }
368 : }
369 :
370 12814 : std::set<CPLString> oSetDuplicates;
371 174498 : for (const auto &oIter : oMapNameToFieldIndex)
372 : {
373 : // Has it duplicates ?
374 168091 : const size_t nOccurrences = oIter.second.size();
375 168091 : if (nOccurrences > 1)
376 : {
377 48 : oSetDuplicates.insert(oIter.first);
378 : }
379 : }
380 :
381 6500 : 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 12814 : std::set<CPLString> oSetNames;
496 194062 : for (const auto &oField : aoFields)
497 : {
498 187655 : if (oField.GetCategory() == GMLASField::REGULAR)
499 : {
500 168184 : const auto &osName = oField.GetName();
501 168184 : CPLAssert(oSetNames.find(osName) == oSetNames.end());
502 168184 : oSetNames.insert(osName);
503 : }
504 : }
505 : }
506 : #endif
507 :
508 : // Now check if we must truncate names
509 6407 : if (m_nIdentifierMaxLength >= MIN_VALUE_OF_MAX_IDENTIFIER_LENGTH)
510 : {
511 10660 : for (size_t i = 0; i < aoFields.size(); i++)
512 : {
513 9537 : int nNameSize = static_cast<int>(aoFields[i].GetName().size());
514 : /* Somewhat arbitrary limitation to avoid performance issues in */
515 : /* OGRGMLASTruncateIdentifier() */
516 9537 : 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 9537 : if (nNameSize > m_nIdentifierMaxLength)
524 : {
525 6684 : aoFields[i].SetName(OGRGMLASTruncateIdentifier(
526 3342 : aoFields[i].GetName(), m_nIdentifierMaxLength));
527 : }
528 : }
529 : }
530 :
531 6407 : if (m_bPGIdentifierLaundering)
532 : {
533 188453 : for (size_t i = 0; i < aoFields.size(); i++)
534 : {
535 : char *pszLaundered =
536 182402 : OGRPGCommonLaunderName(aoFields[i].GetName(), "GMLAS", false);
537 182402 : aoFields[i].SetName(pszLaundered);
538 182402 : CPLFree(pszLaundered);
539 : }
540 : }
541 :
542 : // Detect duplicated field names
543 12814 : std::map<CPLString, std::vector<int>> oSetNames;
544 194062 : for (int i = 0; i < static_cast<int>(aoFields.size()); i++)
545 : {
546 187655 : if (aoFields[i].GetCategory() == GMLASField::REGULAR)
547 : {
548 336368 : CPLString osName(aoFields[i].GetName());
549 168184 : if (m_bCaseInsensitiveIdentifier)
550 168184 : osName.toupper();
551 168184 : oSetNames[osName].push_back(i);
552 : }
553 : }
554 :
555 : // Iterate over the unique names
556 174582 : for (const auto &oIter : oSetNames)
557 : {
558 : // Has it duplicates ?
559 168175 : const size_t nOccurrences = oIter.second.size();
560 168175 : 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 6407 : std::vector<GMLASFeatureClass> &aoNestedClasses = oClass.GetNestedClasses();
574 9716 : for (size_t i = 0; i < aoNestedClasses.size(); i++)
575 : {
576 3309 : if (!LaunderFieldNames(aoNestedClasses[i]))
577 0 : return false;
578 : }
579 6407 : return true;
580 : }
581 :
582 : /************************************************************************/
583 : /* CollectClassesReferences() */
584 : /************************************************************************/
585 :
586 15271 : void GMLASSchemaAnalyzer::CollectClassesReferences(
587 : GMLASFeatureClass &oClass, std::vector<GMLASFeatureClass *> &aoClasses)
588 : {
589 15271 : aoClasses.push_back(&oClass);
590 15271 : std::vector<GMLASFeatureClass> &aoNestedClasses = oClass.GetNestedClasses();
591 18580 : for (size_t i = 0; i < aoNestedClasses.size(); i++)
592 : {
593 3309 : CollectClassesReferences(aoNestedClasses[i], aoClasses);
594 : }
595 15271 : }
596 :
597 : /************************************************************************/
598 : /* LaunderClassNames() */
599 : /************************************************************************/
600 :
601 172 : void GMLASSchemaAnalyzer::LaunderClassNames()
602 : {
603 344 : std::vector<GMLASFeatureClass *> aoClasses;
604 12134 : for (size_t i = 0; i < m_aoClasses.size(); i++)
605 : {
606 11962 : CollectClassesReferences(m_aoClasses[i], aoClasses);
607 : }
608 :
609 172 : if (m_nIdentifierMaxLength >= MIN_VALUE_OF_MAX_IDENTIFIER_LENGTH)
610 : {
611 1367 : for (size_t i = 0; i < aoClasses.size(); i++)
612 : {
613 1279 : int nNameSize = static_cast<int>(aoClasses[i]->GetName().size());
614 1279 : if (nNameSize > m_nIdentifierMaxLength)
615 : {
616 732 : aoClasses[i]->SetName(OGRGMLASTruncateIdentifier(
617 366 : aoClasses[i]->GetName(), m_nIdentifierMaxLength));
618 : }
619 : }
620 : }
621 :
622 172 : if (m_bPGIdentifierLaundering)
623 : {
624 15047 : for (size_t i = 0; i < aoClasses.size(); i++)
625 : {
626 : char *pszLaundered =
627 14878 : OGRPGCommonLaunderName(aoClasses[i]->GetName(), "GMLAS", false);
628 14878 : aoClasses[i]->SetName(pszLaundered);
629 14878 : 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 344 : std::map<CPLString, std::vector<int>> oSetNames;
638 15443 : for (int i = 0; i < static_cast<int>(aoClasses.size()); i++)
639 : {
640 30542 : CPLString osName(aoClasses[i]->GetName());
641 15271 : if (m_bCaseInsensitiveIdentifier)
642 15271 : osName.toupper();
643 15271 : oSetNames[osName].push_back(i);
644 : }
645 :
646 : // Iterate over the unique names
647 15400 : for (const auto &oIter : oSetNames)
648 : {
649 : // Has it duplicates ?
650 15228 : const size_t nOccurrences = oIter.second.size();
651 15228 : if (nOccurrences > 1)
652 : {
653 85 : for (size_t i = 0; i < nOccurrences; i++)
654 : {
655 64 : GMLASFeatureClass *poClass = aoClasses[oIter.second[i]];
656 64 : poClass->SetName(OGRGMLASAddSerialNumber(
657 64 : poClass->GetName(), static_cast<int>(i + 1), nOccurrences,
658 : m_nIdentifierMaxLength));
659 : }
660 : }
661 : }
662 172 : }
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 444 : explicit GMLASUniquePtr(T *p) : m_p(p)
678 : {
679 444 : }
680 :
681 444 : ~GMLASUniquePtr()
682 : {
683 444 : delete m_p;
684 444 : }
685 :
686 2088 : T *operator->() const
687 : {
688 2088 : CPLAssert(m_p);
689 2088 : return m_p;
690 : }
691 :
692 695 : T *get() const
693 : {
694 695 : 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 3119 : GMLASSchemaAnalyzer::GetTopElementDeclarationFromXPath(const CPLString &osXPath,
711 : XSModel *poModel)
712 : {
713 3119 : const char *pszTypename = osXPath.c_str();
714 3119 : const char *pszColon = strrchr(pszTypename, ':');
715 3119 : XSElementDeclaration *poEltDecl = nullptr;
716 3119 : if (pszColon != nullptr)
717 : {
718 6168 : CPLString osNSPrefix = pszTypename;
719 3084 : osNSPrefix.resize(pszColon - pszTypename);
720 6168 : CPLString osName = pszColon + 1;
721 6168 : CPLString osNSURI;
722 :
723 9681 : for (const auto &oIterNS : m_oMapURIToPrefix)
724 : {
725 9681 : const CPLString &osIterNSURI(oIterNS.first);
726 9681 : const CPLString &osIterNSPrefix(oIterNS.second);
727 9681 : if (osNSPrefix == osIterNSPrefix)
728 : {
729 3084 : osNSURI = osIterNSURI;
730 3084 : break;
731 : }
732 : }
733 3084 : XMLCh *xmlNS = nullptr;
734 3084 : XMLCh *xmlName = nullptr;
735 : try
736 : {
737 3084 : xmlNS = XMLString::transcode(osNSURI);
738 3084 : xmlName = XMLString::transcode(osName);
739 3084 : 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 3084 : XMLString::release(&xmlNS);
747 3084 : 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 3119 : return poEltDecl;
764 : }
765 :
766 : /************************************************************************/
767 : /* IsEltCompatibleOfFC() */
768 : /************************************************************************/
769 :
770 : static XSComplexTypeDefinition *
771 61918 : IsEltCompatibleOfFC(XSElementDeclaration *poEltDecl)
772 : {
773 61918 : XSTypeDefinition *poTypeDef = poEltDecl->getTypeDefinition();
774 122819 : if (poTypeDef->getTypeCategory() == XSTypeDefinition::COMPLEX_TYPE &&
775 122819 : transcode(poEltDecl->getName()) != szFEATURE_COLLECTION)
776 : {
777 60792 : XSComplexTypeDefinition *poCT =
778 : reinterpret_cast<XSComplexTypeDefinition *>(poTypeDef);
779 : XSComplexTypeDefinition::CONTENT_TYPE eContentType(
780 60792 : poCT->getContentType());
781 60792 : if (eContentType == XSComplexTypeDefinition::CONTENTTYPE_ELEMENT ||
782 : eContentType == XSComplexTypeDefinition::CONTENTTYPE_MIXED)
783 : {
784 42212 : return poCT;
785 : }
786 : }
787 19706 : return nullptr;
788 : }
789 :
790 : /************************************************************************/
791 : /* DerivesFromGMLFeature() */
792 : /************************************************************************/
793 :
794 291 : bool GMLASSchemaAnalyzer::DerivesFromGMLFeature(XSElementDeclaration *poEltDecl)
795 : {
796 291 : XSElementDeclaration *poIter = poEltDecl;
797 : while (true)
798 : {
799 : XSElementDeclaration *poSubstGroup =
800 477 : poIter->getSubstitutionGroupAffiliation();
801 477 : if (poSubstGroup == nullptr)
802 190 : break;
803 287 : const CPLString osSubstNS(transcode(poSubstGroup->getNamespace()));
804 287 : const CPLString osSubstName(transcode(poSubstGroup->getName()));
805 287 : if (IsGMLNamespace(osSubstNS) && osSubstName == "_FeatureCollection")
806 : {
807 0 : return false;
808 : }
809 446 : if (IsGMLNamespace(osSubstNS) &&
810 159 : (osSubstName == "AbstractFeature" || osSubstName == "_Feature"))
811 : {
812 101 : return true;
813 : }
814 186 : poIter = poSubstGroup;
815 186 : }
816 190 : return false;
817 : }
818 :
819 : /************************************************************************/
820 : /* Analyze() */
821 : /************************************************************************/
822 :
823 183 : 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 366 : (new XMLGrammarPoolImpl(XMLPlatformUtils::fgMemoryManager)));
831 :
832 366 : std::vector<CPLString> aoNamespaces;
833 : GMLASAnalyzerEntityResolver oXSDEntityResolver(
834 366 : 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 183 : aoNamespaces.push_back("");
842 434 : for (size_t i = 0; i < aoXSDs.size(); i++)
843 : {
844 261 : const CPLString osURI(aoXSDs[i].first);
845 261 : const CPLString osXSDFilename(aoXSDs[i].second);
846 :
847 : GMLASUniquePtr<SAX2XMLReader> poParser(
848 : XMLReaderFactory::createXMLReader(XMLPlatformUtils::fgMemoryManager,
849 261 : poGrammarPool.get()));
850 :
851 : // Commonly useful configuration.
852 : //
853 261 : poParser->setFeature(XMLUni::fgSAX2CoreNameSpaces, true);
854 261 : poParser->setFeature(XMLUni::fgSAX2CoreNameSpacePrefixes, true);
855 261 : poParser->setFeature(XMLUni::fgSAX2CoreValidation, true);
856 :
857 : // Enable validation.
858 : //
859 261 : poParser->setFeature(XMLUni::fgXercesSchema, true);
860 :
861 : // coverity[unsafe_xml_parse_config]
862 261 : poParser->setFeature(XMLUni::fgXercesValidationErrorAsFatal, false);
863 :
864 : // Use the loaded grammar during parsing.
865 : //
866 261 : 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 261 : poParser->setFeature(XMLUni::fgXercesLoadSchema, false);
872 :
873 261 : poParser->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution,
874 261 : true);
875 :
876 261 : Grammar *poGrammar = nullptr;
877 261 : 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 502 : CPLString osGrammarURI(transcode(poGrammar->getTargetNamespace()));
891 251 : if (osGrammarURI.empty())
892 : {
893 28 : if (!osURI.empty())
894 1 : osGrammarURI = osURI;
895 : }
896 251 : if (!osGrammarURI.empty())
897 : {
898 : // Patch back the aoXSDs element in case we didn't know the
899 : // namespace URI initially
900 224 : if (osURI.empty())
901 27 : aoXSDs[i].first = osGrammarURI;
902 224 : aoNamespaces.push_back(osGrammarURI);
903 : }
904 : }
905 :
906 173 : m_osGMLVersionFound = oXSDEntityResolver.GetGMLVersionFound();
907 173 : m_oSetSchemaURLs = oXSDEntityResolver.GetSchemaURLS();
908 :
909 173 : m_oIgnoredXPathMatcher.SetDocumentMapURIToPrefix(m_oMapURIToPrefix);
910 173 : m_oChildrenElementsConstraintsXPathMatcher.SetDocumentMapURIToPrefix(
911 173 : m_oMapURIToPrefix);
912 173 : m_oForcedFlattenedXPathMatcher.SetDocumentMapURIToPrefix(m_oMapURIToPrefix);
913 173 : m_oDisabledFlattenedXPathMatcher.SetDocumentMapURIToPrefix(
914 173 : m_oMapURIToPrefix);
915 :
916 173 : XSModel *poModel = getGrammarPool(poGrammarPool.get());
917 173 : 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 173 : bool bFoundGMLFeature = false;
932 :
933 : // Second pass, in all namespaces, to figure out inheritance relationships
934 : // and group models that have names
935 346 : std::map<CPLString, CPLString> oMapURIToPrefixWithEmpty(m_oMapURIToPrefix);
936 173 : oMapURIToPrefixWithEmpty[""] = "";
937 1230 : for (const auto &oIterNS : oMapURIToPrefixWithEmpty)
938 : {
939 1057 : const CPLString &osNSURI(oIterNS.first);
940 2654 : if (osNSURI == szXS_URI || osNSURI == szXSI_URI ||
941 2654 : osNSURI == szXMLNS_URI || osNSURI == szXLINK_URI)
942 : {
943 522 : continue;
944 : }
945 :
946 535 : XMLCh *xmlNamespace = nullptr;
947 : try
948 : {
949 535 : 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 535 : poModel->getComponentsByNamespace(
960 : XSConstants::MODEL_GROUP_DEFINITION, xmlNamespace);
961 :
962 : // Remember group models that have names
963 1331 : for (XMLSize_t i = 0; poMapModelGroupDefinition != nullptr &&
964 593 : i < poMapModelGroupDefinition->getLength();
965 : i++)
966 : {
967 : XSModelGroupDefinition *modelGroupDefinition =
968 : reinterpret_cast<XSModelGroupDefinition *>(
969 203 : poMapModelGroupDefinition->item(i));
970 203 : m_oMapModelGroupToMGD[modelGroupDefinition->getModelGroup()] =
971 : modelGroupDefinition;
972 : }
973 :
974 535 : CPLDebug("GMLAS", "Discovering substitutions of %s (%s)",
975 : oIterNS.second.c_str(), osNSURI.c_str());
976 :
977 535 : XSNamedMap<XSObject> *poMapElements = poModel->getComponentsByNamespace(
978 : XSConstants::ELEMENT_DECLARATION, xmlNamespace);
979 :
980 4357 : for (XMLSize_t i = 0;
981 4357 : poMapElements != nullptr && i < poMapElements->getLength(); i++)
982 : {
983 : XSElementDeclaration *poEltDecl =
984 : reinterpret_cast<XSElementDeclaration *>(
985 3822 : poMapElements->item(i));
986 : XSElementDeclaration *poSubstGroup =
987 3822 : poEltDecl->getSubstitutionGroupAffiliation();
988 : const CPLString osEltXPath(
989 7644 : MakeXPath(transcode(poEltDecl->getNamespace()),
990 11466 : transcode(poEltDecl->getName())));
991 3822 : m_oMapXPathToEltDecl[osEltXPath] = poEltDecl;
992 3822 : if (poSubstGroup)
993 : {
994 961 : 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 319 : if (!bFoundGMLFeature && m_bInstantiateGMLFeaturesOnly &&
1006 1495 : !IsGMLNamespace(osNSURI) &&
1007 215 : DerivesFromGMLFeature(poEltDecl))
1008 : {
1009 33 : 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 33 : bFoundGMLFeature = true;
1016 : }
1017 : }
1018 : }
1019 :
1020 535 : 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 173 : bool bFoundElementsInFirstChoiceNamespaces = false;
1027 491 : for (size_t iNS = 0;
1028 491 : !bFoundElementsInFirstChoiceNamespaces && iNS < aoNamespaces.size();
1029 : iNS++)
1030 : {
1031 318 : XMLCh *xmlNamespace = nullptr;
1032 : try
1033 : {
1034 318 : 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 318 : XSNamedMap<XSObject> *poMapElements = poModel->getComponentsByNamespace(
1044 : XSConstants::ELEMENT_DECLARATION, xmlNamespace);
1045 318 : bFoundElementsInFirstChoiceNamespaces =
1046 318 : poMapElements != nullptr && poMapElements->getLength() > 0;
1047 318 : XMLString::release(&xmlNamespace);
1048 : }
1049 173 : 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 346 : std::set<XSElementDeclaration *> oSetVisitedEltDecl;
1072 346 : std::set<XSModelGroup *> oSetVisitedModelGroups;
1073 346 : 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 346 : std::set<CPLString> aoSetXPathEltsForTopClass;
1080 :
1081 : // Third and fourth passes
1082 519 : for (int iPass = 0; iPass < 2; ++iPass)
1083 : {
1084 1140 : for (size_t iNS = 0; iNS < aoNamespaces.size(); iNS++)
1085 : {
1086 794 : XMLCh *xmlNamespace = nullptr;
1087 : try
1088 : {
1089 794 : 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 794 : poModel->getComponentsByNamespace(
1101 : XSConstants::ELEMENT_DECLARATION, xmlNamespace);
1102 :
1103 5174 : for (XMLSize_t i = 0;
1104 5174 : poMapElements != nullptr && i < poMapElements->getLength();
1105 : i++)
1106 : {
1107 : XSElementDeclaration *poEltDecl =
1108 : reinterpret_cast<XSElementDeclaration *>(
1109 4380 : poMapElements->item(i));
1110 4380 : XSComplexTypeDefinition *poCT = IsEltCompatibleOfFC(poEltDecl);
1111 4380 : if (!poEltDecl->getAbstract() && poCT != nullptr)
1112 : {
1113 : const CPLString osXPath(
1114 7724 : MakeXPath(transcode(poEltDecl->getNamespace()),
1115 11586 : transcode(poEltDecl->getName())));
1116 3862 : if (!IsIgnoredXPath(osXPath))
1117 : {
1118 3938 : if (bFoundGMLFeature && m_bInstantiateGMLFeaturesOnly &&
1119 76 : !DerivesFromGMLFeature(poEltDecl))
1120 : {
1121 : // Do nothing
1122 : }
1123 3854 : 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 1927 : oSetVisitedEltDecl.insert(poEltDecl);
1136 1927 : if (aoSetXPathEltsForTopClass.find(osXPath) ==
1137 3854 : aoSetXPathEltsForTopClass.end())
1138 : {
1139 1927 : m_oSetEltsForTopClass.insert(poEltDecl);
1140 1927 : oVectorEltsForTopClass.push_back(poEltDecl);
1141 1927 : aoSetXPathEltsForTopClass.insert(osXPath);
1142 : }
1143 : }
1144 : else
1145 : {
1146 1927 : bool bSimpleEnoughOut = true;
1147 1927 : int nSubCountSubEltOut = 0;
1148 1927 : auto poParticle = poCT->getParticle();
1149 1927 : if (poParticle)
1150 : {
1151 1926 : CPL_IGNORE_RET_VAL(
1152 1926 : 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 794 : XMLString::release(&xmlNamespace);
1167 : }
1168 : }
1169 :
1170 : // Find ambiguous class names
1171 : {
1172 3272 : for (const auto &oIter : m_oSetEltsForTopClass)
1173 : {
1174 3099 : CPLString osName(transcode(oIter->getName()));
1175 3099 : m_oMapEltNamesToInstanceCount[osName]++;
1176 : }
1177 : }
1178 :
1179 : // Instantiate all needed typenames
1180 3271 : for (const auto &poEltDecl : oVectorEltsForTopClass)
1181 : {
1182 6198 : const CPLString osXPath(MakeXPath(transcode(poEltDecl->getNamespace()),
1183 6198 : transcode(poEltDecl->getName())));
1184 :
1185 3099 : bool bError = false;
1186 : bool bResolvedType =
1187 3099 : InstantiateClassFromEltDeclaration(poEltDecl, poModel, bError);
1188 3099 : if (bError)
1189 : {
1190 1 : return false;
1191 : }
1192 3098 : 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 172 : LaunderClassNames();
1203 :
1204 172 : return true;
1205 : }
1206 :
1207 : /************************************************************************/
1208 : /* GetAnnotationDoc() */
1209 : /************************************************************************/
1210 :
1211 407687 : static CPLString GetAnnotationDoc(const XSAnnotation *annotation)
1212 : {
1213 407687 : if (!annotation)
1214 223296 : return CPLString();
1215 368782 : CPLString osAnnot(transcode(annotation->getAnnotationString()));
1216 184391 : CPLXMLNode *psRoot = CPLParseXMLString(osAnnot);
1217 184391 : CPLStripXMLNamespace(psRoot, nullptr, TRUE);
1218 368782 : CPLString osDoc(CPLGetXMLValue(psRoot, "=annotation.documentation", ""));
1219 184391 : CPLDestroyXMLNode(psRoot);
1220 184391 : return osDoc.Trim();
1221 : }
1222 :
1223 : /************************************************************************/
1224 : /* GetAnnotationDoc() */
1225 : /************************************************************************/
1226 :
1227 189967 : static CPLString GetAnnotationDoc(const XSAnnotationList *annotationList)
1228 : {
1229 189967 : if (!annotationList)
1230 6632 : return CPLString();
1231 366670 : CPLString osRet;
1232 366670 : for (size_t i = 0; i < annotationList->size(); ++i)
1233 : {
1234 366670 : CPLString osDoc(GetAnnotationDoc(annotationList->elementAt(i)));
1235 183335 : if (!osDoc.empty())
1236 : {
1237 183232 : if (!osRet.empty())
1238 0 : osRet += "\n";
1239 183232 : osRet += osDoc;
1240 : }
1241 : }
1242 183335 : return osRet;
1243 : }
1244 :
1245 : /************************************************************************/
1246 : /* GetAnnotationDoc() */
1247 : /************************************************************************/
1248 :
1249 189967 : static CPLString GetAnnotationDoc(const XSElementDeclaration *poEltDecl)
1250 : {
1251 189967 : XSTypeDefinition *poTypeDef = poEltDecl->getTypeDefinition();
1252 189967 : CPLString osDoc = GetAnnotationDoc(poEltDecl->getAnnotation());
1253 189967 : XSAnnotationList *list = nullptr;
1254 204470 : while (poTypeDef != nullptr)
1255 : {
1256 204470 : if (poTypeDef->getTypeCategory() == XSTypeDefinition::COMPLEX_TYPE)
1257 : {
1258 54505 : XSComplexTypeDefinition *poCT =
1259 : reinterpret_cast<XSComplexTypeDefinition *>(poTypeDef);
1260 54505 : list = poCT->getAnnotations();
1261 : }
1262 149965 : else if (poTypeDef->getTypeCategory() == XSTypeDefinition::SIMPLE_TYPE)
1263 : {
1264 149965 : XSSimpleTypeDefinition *poST =
1265 : reinterpret_cast<XSSimpleTypeDefinition *>(poTypeDef);
1266 149965 : list = poST->getAnnotations();
1267 : }
1268 204470 : if (list != nullptr)
1269 183335 : break;
1270 21135 : XSTypeDefinition *poNewTypeDef = poTypeDef->getBaseType();
1271 21135 : if (poNewTypeDef == poTypeDef)
1272 6632 : break;
1273 14503 : poTypeDef = poNewTypeDef;
1274 : }
1275 379934 : const CPLString osDoc2 = GetAnnotationDoc(list);
1276 189967 : if (!osDoc.empty() && !osDoc2.empty())
1277 : {
1278 278 : osDoc += "\n";
1279 278 : osDoc += osDoc2;
1280 : }
1281 189689 : else if (!osDoc2.empty())
1282 182954 : osDoc = osDoc2;
1283 379934 : return osDoc;
1284 : }
1285 :
1286 : /************************************************************************/
1287 : /* InstantiateClassFromEltDeclaration() */
1288 : /************************************************************************/
1289 :
1290 3099 : bool GMLASSchemaAnalyzer::InstantiateClassFromEltDeclaration(
1291 : XSElementDeclaration *poEltDecl, XSModel *poModel, bool &bError)
1292 : {
1293 3099 : bError = false;
1294 3099 : XSComplexTypeDefinition *poCT = IsEltCompatibleOfFC(poEltDecl);
1295 3099 : if (!poEltDecl->getAbstract() && poCT != nullptr)
1296 : {
1297 6198 : GMLASFeatureClass oClass;
1298 6198 : const CPLString osEltName(transcode(poEltDecl->getName()));
1299 : const CPLString osXPath(
1300 6198 : MakeXPath(transcode(poEltDecl->getNamespace()), osEltName));
1301 :
1302 3099 : 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 3099 : if (m_oMapEltNamesToInstanceCount[osEltName] > 1)
1311 : {
1312 8 : CPLString osLaunderedXPath(osXPath);
1313 4 : osLaunderedXPath.replaceAll(':', '_');
1314 4 : oClass.SetName(osLaunderedXPath);
1315 : }
1316 : else
1317 3095 : oClass.SetName(osEltName);
1318 :
1319 : #ifdef DEBUG_VERBOSE
1320 : CPLDebug("GMLAS", "Instantiating element %s", osXPath.c_str());
1321 : #endif
1322 3099 : oClass.SetXPath(osXPath);
1323 3099 : oClass.SetIsTopLevelElt(
1324 3099 : GetTopElementDeclarationFromXPath(osXPath, poModel) != nullptr);
1325 :
1326 6198 : std::set<XSModelGroup *> oSetVisitedModelGroups;
1327 :
1328 3099 : oClass.SetDocumentation(GetAnnotationDoc(poEltDecl));
1329 :
1330 : // might be NULL on swe:values for example
1331 3099 : if (poCT->getParticle() != nullptr)
1332 : {
1333 3098 : std::map<CPLString, int> oMapCountOccurrencesOfSameName;
1334 3098 : BuildMapCountOccurrencesOfSameName(
1335 : poCT->getParticle()->getModelGroupTerm(),
1336 : oMapCountOccurrencesOfSameName);
1337 :
1338 3098 : OGRwkbGeometryType eGeomType = wkbUnknown;
1339 3136 : if (IsGMLNamespace(transcode(poCT->getNamespace())) &&
1340 38 : (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 3097 : 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 3098 : if (!LaunderFieldNames(oClass))
1369 0 : return false;
1370 :
1371 3098 : m_aoClasses.push_back(oClass);
1372 3098 : return true;
1373 : }
1374 0 : return false;
1375 : }
1376 :
1377 : /************************************************************************/
1378 : /* SetFieldTypeAndWidthFromDefinition() */
1379 : /************************************************************************/
1380 :
1381 194611 : void GMLASSchemaAnalyzer::SetFieldTypeAndWidthFromDefinition(
1382 : XSSimpleTypeDefinition *poST, GMLASField &oField)
1383 : {
1384 194611 : int nMaxLength = 0;
1385 342094 : while (
1386 1073410 : poST->getBaseType() != poST &&
1387 536705 : poST->getBaseType()->getTypeCategory() ==
1388 1073410 : XSTypeDefinition::SIMPLE_TYPE &&
1389 533800 : !XMLString::equals(poST->getNamespace(), PSVIUni::fgNamespaceXmlSchema))
1390 : {
1391 : const XMLCh *maxLength =
1392 342094 : poST->getLexicalFacetValue(XSSimpleTypeDefinition::FACET_LENGTH);
1393 342094 : if (maxLength == nullptr)
1394 : {
1395 342000 : maxLength = poST->getLexicalFacetValue(
1396 : XSSimpleTypeDefinition::FACET_MAXLENGTH);
1397 : }
1398 342094 : if (maxLength != nullptr)
1399 168269 : nMaxLength = MAX(nMaxLength, atoi(transcode(maxLength)));
1400 342094 : poST = reinterpret_cast<XSSimpleTypeDefinition *>(poST->getBaseType());
1401 : }
1402 :
1403 194611 : if (XMLString::equals(poST->getNamespace(), PSVIUni::fgNamespaceXmlSchema))
1404 : {
1405 389222 : CPLString osType(transcode(poST->getName()));
1406 194611 : 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 194611 : oField.SetWidth(nMaxLength);
1414 194611 : }
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 353 : bool GMLASSchemaAnalyzer::IsSame(const XSModelGroup *poModelGroup1,
1424 : const XSModelGroup *poModelGroup2)
1425 : {
1426 353 : if (poModelGroup1->getCompositor() != poModelGroup2->getCompositor())
1427 112 : return false;
1428 :
1429 241 : const XSParticleList *poParticleList1 = poModelGroup1->getParticles();
1430 241 : const XSParticleList *poParticleList2 = poModelGroup2->getParticles();
1431 241 : if (poParticleList1->size() != poParticleList2->size())
1432 76 : return false;
1433 :
1434 588 : for (size_t i = 0; i < poParticleList1->size(); ++i)
1435 : {
1436 447 : const XSParticle *poParticle1 = poParticleList1->elementAt(i);
1437 447 : const XSParticle *poParticle2 = poParticleList2->elementAt(i);
1438 894 : if (poParticle1->getTermType() != poParticle2->getTermType() ||
1439 894 : poParticle1->getMinOccurs() != poParticle2->getMinOccurs() ||
1440 1341 : poParticle1->getMaxOccurs() != poParticle2->getMaxOccurs() ||
1441 447 : poParticle1->getMaxOccursUnbounded() !=
1442 447 : poParticle2->getMaxOccursUnbounded())
1443 : {
1444 0 : return false;
1445 : }
1446 447 : switch (poParticle1->getTermType())
1447 : {
1448 0 : case XSParticle::TERM_EMPTY:
1449 0 : break;
1450 :
1451 447 : case XSParticle::TERM_ELEMENT:
1452 : {
1453 : const XSElementDeclaration *poElt1 =
1454 447 : const_cast<XSParticle *>(poParticle1)->getElementTerm();
1455 : const XSElementDeclaration *poElt2 =
1456 447 : const_cast<XSParticle *>(poParticle2)->getElementTerm();
1457 : // Pointer comparison works here
1458 447 : if (poElt1 != poElt2)
1459 24 : 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 1682 : GMLASSchemaAnalyzer::GetGroupDefinition(const XSModelGroup *poModelGroup)
1508 : {
1509 1894 : for (const auto &oIter : m_oMapModelGroupToMGD)
1510 : {
1511 353 : if (IsSame(poModelGroup, oIter.first))
1512 : {
1513 141 : return oIter.second;
1514 : }
1515 : }
1516 :
1517 1541 : return nullptr;
1518 : }
1519 :
1520 : /************************************************************************/
1521 : /* IsAnyType() */
1522 : /************************************************************************/
1523 :
1524 27123 : static bool IsAnyType(XSComplexTypeDefinition *poType)
1525 : {
1526 27123 : if (XMLString::equals(poType->getBaseType()->getNamespace(),
1527 73241 : PSVIUni::fgNamespaceXmlSchema) &&
1528 46118 : transcode(poType->getBaseType()->getName()) == szXS_ANY_TYPE)
1529 : {
1530 18995 : XSParticle *poParticle = poType->getParticle();
1531 18995 : if (poParticle != nullptr)
1532 : {
1533 18721 : XSModelGroup *poGroupTerm = poParticle->getModelGroupTerm();
1534 18721 : if (poGroupTerm != nullptr)
1535 : {
1536 18721 : XSParticleList *poParticles = poGroupTerm->getParticles();
1537 18721 : if (poParticles != nullptr)
1538 : {
1539 20296 : return poParticles->size() == 1 &&
1540 1575 : poParticles->elementAt(0)->getTermType() ==
1541 18721 : XSParticle::TERM_WILDCARD;
1542 : }
1543 : }
1544 : }
1545 274 : else if (poType->getDerivationMethod() ==
1546 : XSConstants::DERIVATION_EXTENSION)
1547 : {
1548 : // swe:values case
1549 0 : return true;
1550 : }
1551 : }
1552 8402 : return false;
1553 : }
1554 :
1555 : /************************************************************************/
1556 : /* SetFieldFromAttribute() */
1557 : /************************************************************************/
1558 :
1559 33877 : bool GMLASSchemaAnalyzer::SetFieldFromAttribute(GMLASField &oField,
1560 : XSAttributeUse *poAttr,
1561 : const CPLString &osXPathPrefix,
1562 : const CPLString &osNamePrefix)
1563 : {
1564 33877 : const XSAttributeDeclaration *poAttrDecl = poAttr->getAttrDeclaration();
1565 67754 : const CPLString osNS(transcode(poAttrDecl->getNamespace()));
1566 67754 : const CPLString osName(transcode(poAttrDecl->getName()));
1567 :
1568 33877 : if (osNamePrefix.empty())
1569 14767 : oField.SetName(osName);
1570 : else
1571 19110 : oField.SetName(osNamePrefix + "_" + osName);
1572 :
1573 33877 : XSSimpleTypeDefinition *poAttrType = poAttrDecl->getTypeDefinition();
1574 33877 : 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 33876 : SetFieldTypeAndWidthFromDefinition(poAttrType, oField);
1583 :
1584 33876 : oField.SetXPath(osXPathPrefix + "/@" + MakeXPath(osNS, osName));
1585 33876 : if (poAttr->getRequired())
1586 : {
1587 25859 : oField.SetNotNullable(true);
1588 : }
1589 33876 : oField.SetMinOccurs(oField.IsNotNullable() ? 1 : 0);
1590 33876 : oField.SetMaxOccurs(1);
1591 33876 : if (poAttr->getConstraintType() == XSConstants::VALUE_CONSTRAINT_FIXED)
1592 : {
1593 553 : oField.SetFixedValue(transcode(poAttr->getConstraintValue()));
1594 : }
1595 33323 : else if (poAttr->getConstraintType() ==
1596 : XSConstants::VALUE_CONSTRAINT_DEFAULT)
1597 : {
1598 246 : oField.SetDefaultValue(transcode(poAttr->getConstraintValue()));
1599 : }
1600 :
1601 : const bool bIsList =
1602 33876 : (poAttrType->getVariety() == XSSimpleTypeDefinition::VARIETY_LIST);
1603 33876 : 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 33876 : oField.SetDocumentation(GetAnnotationDoc(poAttrDecl->getAnnotation()));
1620 :
1621 33876 : return true;
1622 : }
1623 :
1624 : /************************************************************************/
1625 : /* GetConcreteImplementationTypes() */
1626 : /************************************************************************/
1627 :
1628 317758 : void GMLASSchemaAnalyzer::GetConcreteImplementationTypes(
1629 : XSElementDeclaration *poParentElt,
1630 : std::vector<XSElementDeclaration *> &apoImplEltList)
1631 : {
1632 317758 : const auto oIter = m_oMapParentEltToChildElt.find(poParentElt);
1633 317758 : if (oIter != m_oMapParentEltToChildElt.end())
1634 : {
1635 11575 : for (size_t j = 0; j < oIter->second.size(); j++)
1636 : {
1637 10238 : XSElementDeclaration *poSubElt = oIter->second[j];
1638 10238 : if (IsEltCompatibleOfFC(poSubElt))
1639 : {
1640 2376 : if (!poSubElt->getAbstract())
1641 : {
1642 1942 : apoImplEltList.push_back(poSubElt);
1643 : }
1644 : }
1645 10238 : GetConcreteImplementationTypes(poSubElt, apoImplEltList);
1646 : }
1647 : }
1648 317758 : }
1649 :
1650 : /************************************************************************/
1651 : /* GetConstraintChildrenElements() */
1652 : /************************************************************************/
1653 :
1654 : std::vector<XSElementDeclaration *>
1655 307516 : GMLASSchemaAnalyzer::GetConstraintChildrenElements(const CPLString &osFullXPath)
1656 : {
1657 :
1658 307516 : std::vector<XSElementDeclaration *> oVectorRes;
1659 615032 : CPLString osMatched;
1660 307516 : 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 615032 : return oVectorRes;
1712 : }
1713 :
1714 : /************************************************************************/
1715 : /* GetOGRGeometryType() */
1716 : /************************************************************************/
1717 :
1718 1441 : static OGRwkbGeometryType GetOGRGeometryType(XSTypeDefinition *poTypeDef)
1719 : {
1720 : const struct MyStruct
1721 : {
1722 : const char *pszName;
1723 : OGRwkbGeometryType eType;
1724 1441 : } 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 2882 : CPLString osName(transcode(poTypeDef->getName()));
1750 21478 : for (size_t i = 0; i < CPL_ARRAYSIZE(asArray); ++i)
1751 : {
1752 20651 : if (osName == asArray[i].pszName)
1753 614 : return asArray[i].eType;
1754 : }
1755 827 : 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 842 : GetOGRGeometryTypeFromGMLEltName(const CPLString &osEltName)
1775 : {
1776 : const struct MyStruct
1777 : {
1778 : const char *pszName;
1779 : OGRwkbGeometryType eType;
1780 842 : } 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 14374 : for (size_t i = 0; i < CPL_ARRAYSIZE(asArray); ++i)
1801 : {
1802 13578 : if (osEltName == asArray[i].pszName)
1803 46 : return asArray[i].eType;
1804 : }
1805 796 : return wkbNone;
1806 : }
1807 :
1808 : /************************************************************************/
1809 : /* CreateNonNestedRelationship() */
1810 : /************************************************************************/
1811 :
1812 15406 : 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 30812 : const CPLString osEltPrefix(GetPrefix(transcode(poElt->getNamespace())));
1819 30812 : const CPLString osEltName(transcode(poElt->getName()));
1820 : const CPLString osOnlyElementXPath(
1821 30812 : MakeXPath(transcode(poElt->getNamespace()), osEltName));
1822 30812 : const CPLString osElementXPath(oClass.GetXPath() + "/" +
1823 30812 : osOnlyElementXPath);
1824 :
1825 15406 : if (!poElt->getAbstract() && !bCaseOfConstraintChildren)
1826 : {
1827 15238 : apoImplEltList.insert(apoImplEltList.begin(), poElt);
1828 : }
1829 :
1830 30812 : std::set<CPLString> aoSetSubEltXPath;
1831 15406 : 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 13585 : for (size_t j = 0; j < apoImplEltList.size(); j++)
1837 : {
1838 6994 : XSElementDeclaration *poSubElt = apoImplEltList[j];
1839 6994 : const CPLString osSubEltName(transcode(poSubElt->getName()));
1840 : const CPLString osSubEltXPath(
1841 6994 : MakeXPath(transcode(poSubElt->getNamespace()), osSubEltName));
1842 :
1843 : // For AbstractFeature_SpatialDataSet_pkid in SpatialDataSet_member
1844 6994 : if (aoSetSubEltXPath.find(osSubEltXPath) != aoSetSubEltXPath.end())
1845 : {
1846 0 : continue;
1847 : }
1848 6994 : aoSetSubEltXPath.insert(osSubEltXPath);
1849 :
1850 13988 : const CPLString osRealFullXPath(oClass.GetXPath() + "/" +
1851 : ((bCaseOfConstraintChildren)
1852 13985 : ? osOnlyElementXPath + "/"
1853 27973 : : CPLString("")) +
1854 6994 : osSubEltXPath);
1855 :
1856 6994 : if (IsIgnoredXPath(osRealFullXPath))
1857 : {
1858 : #ifdef DEBUG_VERBOSE
1859 : CPLDebug("GMLAS", "%s is in ignored xpaths",
1860 : osRealFullXPath.c_str());
1861 : #endif
1862 1 : continue;
1863 : }
1864 :
1865 13986 : GMLASField oField;
1866 6993 : if (apoImplEltList.size() > 1 || bCaseOfConstraintChildren)
1867 : {
1868 524 : 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 1566 : oField.SetName(((bEltNameWillNeedPrefix) ? osEltPrefix + "_"
1880 1566 : : CPLString()) +
1881 2088 : transcode(poElt->getName()) + "_" +
1882 1044 : osSubEltName + "_pkid");
1883 : }
1884 : }
1885 : else
1886 : {
1887 6469 : oField.SetName(transcode(poElt->getName()) + "_pkid");
1888 : }
1889 6993 : oField.SetXPath(osRealFullXPath);
1890 6993 : oField.SetMinOccurs(0);
1891 6993 : oField.SetMaxOccurs(nMaxOccurs);
1892 6993 : oField.SetCategory(GMLASField::PATH_TO_CHILD_ELEMENT_WITH_LINK);
1893 6993 : oField.SetRelatedClassXPath(osSubEltXPath);
1894 6993 : oField.SetType(GMLAS_FT_STRING, szXS_STRING);
1895 6993 : oClass.AddField(oField);
1896 6591 : }
1897 : }
1898 : else
1899 : {
1900 : // If the field is repeated, we need to use junction
1901 : // tables
1902 17679 : for (size_t j = 0; j < apoImplEltList.size(); j++)
1903 : {
1904 8864 : XSElementDeclaration *poSubElt = apoImplEltList[j];
1905 8864 : const CPLString osSubEltName(transcode(poSubElt->getName()));
1906 : const CPLString osSubEltXPath(
1907 8864 : MakeXPath(transcode(poSubElt->getNamespace()), osSubEltName));
1908 :
1909 : // For AbstractFeature_SpatialDataSet_pkid in SpatialDataSet_member
1910 8864 : if (aoSetSubEltXPath.find(osSubEltXPath) != aoSetSubEltXPath.end())
1911 : {
1912 0 : continue;
1913 : }
1914 8864 : aoSetSubEltXPath.insert(osSubEltXPath);
1915 :
1916 : // Instantiate a junction table
1917 17728 : GMLASFeatureClass oJunctionTable;
1918 :
1919 8864 : 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 26469 : oJunctionTable.SetName(oClass.GetName() + "_" +
1930 35292 : 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 8864 : oJunctionTable.SetXPath(
1936 17728 : BuildJunctionTableXPath(osElementXPath, osSubEltXPath));
1937 8864 : oJunctionTable.SetParentXPath(oClass.GetXPath());
1938 8864 : oJunctionTable.SetChildXPath(osSubEltXPath);
1939 8864 : m_aoClasses.push_back(oJunctionTable);
1940 :
1941 : // Add an abstract field
1942 17728 : GMLASField oField;
1943 8864 : oField.SetName(
1944 17728 : ((bEltNameWillNeedPrefix) ? osEltPrefix + "_" : CPLString()) +
1945 17728 : osEltName + "_" + osSubEltName);
1946 26592 : oField.SetXPath(oClass.GetXPath() + "/" +
1947 : ((bCaseOfConstraintChildren)
1948 26591 : ? osOnlyElementXPath + "/"
1949 35455 : : CPLString("")) +
1950 : osSubEltXPath);
1951 8864 : oField.SetMinOccurs(0);
1952 8864 : oField.SetMaxOccurs(nMaxOccurs);
1953 8864 : oField.SetAbstractElementXPath(osElementXPath);
1954 8864 : oField.SetRelatedClassXPath(osSubEltXPath);
1955 8864 : oField.SetCategory(
1956 : GMLASField::PATH_TO_CHILD_ELEMENT_WITH_JUNCTION_TABLE);
1957 8864 : 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 15406 : }
2012 :
2013 : /************************************************************************/
2014 : /* IsIgnoredXPath() */
2015 : /************************************************************************/
2016 :
2017 355712 : bool GMLASSchemaAnalyzer::IsIgnoredXPath(const CPLString &osXPath)
2018 : {
2019 711424 : CPLString osIgnored;
2020 711424 : return m_oIgnoredXPathMatcher.MatchesRefXPath(osXPath, osIgnored);
2021 : }
2022 :
2023 : /************************************************************************/
2024 : /* FindElementsWithMustBeToLevel() */
2025 : /************************************************************************/
2026 :
2027 27457 : 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 27457 : const bool bAlreadyVisitedMG = (oSetVisitedModelGroups.find(poModelGroup) !=
2036 27457 : oSetVisitedModelGroups.end());
2037 :
2038 27457 : oSetVisitedModelGroups.insert(poModelGroup);
2039 :
2040 27457 : 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 54914 : CPLString osIgnored;
2050 27457 : if (m_oDisabledFlattenedXPathMatcher.MatchesRefXPath(osParentXPath,
2051 : osIgnored))
2052 : {
2053 0 : bSimpleEnoughOut = false;
2054 : }
2055 : }
2056 :
2057 27457 : XSParticleList *poParticles = poModelGroup->getParticles();
2058 144223 : for (size_t i = 0; i < poParticles->size(); ++i)
2059 : {
2060 116766 : XSParticle *poParticle = poParticles->elementAt(i);
2061 :
2062 220664 : const bool bRepeatedParticle = poParticle->getMaxOccursUnbounded() ||
2063 103898 : poParticle->getMaxOccurs() > 1;
2064 :
2065 116766 : if (poParticle->getTermType() == XSParticle::TERM_ELEMENT)
2066 : {
2067 105642 : XSElementDeclaration *poElt = poParticle->getElementTerm();
2068 105642 : XSTypeDefinition *poTypeDef = poElt->getTypeDefinition();
2069 105642 : const CPLString osEltName(transcode(poElt->getName()));
2070 105642 : const CPLString osEltNS(transcode(poElt->getNamespace()));
2071 105642 : const CPLString osXPath(MakeXPath(osEltNS, osEltName));
2072 211284 : const CPLString osFullXPath(osParentXPath + "/" + osXPath);
2073 :
2074 : #ifdef DEBUG_SUPER_VERBOSE
2075 : CPLDebug("GMLAS", "FindElementsWithMustBeToLevel: %s",
2076 : osFullXPath.c_str());
2077 : #endif
2078 :
2079 105642 : if (IsIgnoredXPath(osFullXPath))
2080 : {
2081 : #ifdef DEBUG_VERBOSE
2082 : CPLDebug("GMLAS", "%s is in ignored xpaths",
2083 : osFullXPath.c_str());
2084 : #endif
2085 4 : continue;
2086 : }
2087 :
2088 : // This could be refined to detect if the repeated element might not
2089 : // be simplifiable as an array
2090 105638 : 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 4280 : bSimpleEnoughOut = false;
2097 : }
2098 :
2099 : // We don't want to inline
2100 : // sub-classes with hundereds of attributes
2101 105638 : nCountSubEltsOut++;
2102 :
2103 105638 : std::vector<XSElementDeclaration *> apoImplEltList;
2104 105638 : GetConcreteImplementationTypes(poElt, apoImplEltList);
2105 :
2106 : std::vector<XSElementDeclaration *> apoChildrenElements =
2107 105638 : GetConstraintChildrenElements(osFullXPath);
2108 :
2109 : // Special case for a GML geometry property
2110 106224 : if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
2111 586 : GetOGRGeometryType(poTypeDef) != wkbNone)
2112 : {
2113 : // Do nothing
2114 : }
2115 105620 : else if (IsGMLNamespace(osEltNS) &&
2116 286 : GetOGRGeometryTypeFromGMLEltName(osEltName) != wkbNone)
2117 : {
2118 : // Do nothing
2119 : }
2120 : // Any GML abstract type
2121 105538 : else if (poElt->getAbstract() && IsGMLNamespace(osEltNS) &&
2122 68 : osEltName != "_Feature" &&
2123 105538 : osEltName != "AbstractFeature" &&
2124 30 : osEltName != "AbstractTimeObject")
2125 : {
2126 : // Do nothing
2127 : }
2128 : // Are there substitution groups for this element ?
2129 105284 : else if (!apoImplEltList.empty() || !apoChildrenElements.empty())
2130 : {
2131 148 : if (!apoChildrenElements.empty())
2132 : {
2133 3 : apoImplEltList = std::move(apoChildrenElements);
2134 : }
2135 145 : else if (!poElt->getAbstract())
2136 : {
2137 27 : apoImplEltList.insert(apoImplEltList.begin(), poElt);
2138 : }
2139 501 : for (size_t j = 0; j < apoImplEltList.size(); j++)
2140 : {
2141 353 : XSElementDeclaration *poSubElt = apoImplEltList[j];
2142 : const CPLString osSubEltXPath(
2143 706 : MakeXPath(transcode(poSubElt->getNamespace()),
2144 706 : transcode(poSubElt->getName())));
2145 :
2146 353 : 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 1 : continue;
2153 : }
2154 :
2155 : // Make sure we will instantiate the referenced element
2156 352 : if (m_oSetEltsForTopClass.find(poSubElt) ==
2157 845 : m_oSetEltsForTopClass.end() &&
2158 141 : aoSetXPathEltsForTopClass.find(osSubEltXPath) ==
2159 493 : 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 141 : oSetVisitedEltDecl.insert(poSubElt);
2175 141 : m_oSetEltsForTopClass.insert(poSubElt);
2176 141 : oVectorEltsForTopClass.push_back(poSubElt);
2177 141 : aoSetXPathEltsForTopClass.insert(osSubEltXPath);
2178 :
2179 : XSComplexTypeDefinition *poSubEltCT =
2180 141 : IsEltCompatibleOfFC(poSubElt);
2181 277 : if (!bAlreadyVisitedMG && poSubEltCT != nullptr &&
2182 136 : poSubEltCT->getParticle() != nullptr)
2183 : {
2184 136 : bool bSubSimpleEnoughOut = true;
2185 136 : int nSubCountSubElt = 0;
2186 136 : 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 210224 : else if (!poElt->getAbstract() &&
2204 105088 : poTypeDef->getTypeCategory() ==
2205 : XSTypeDefinition::COMPLEX_TYPE)
2206 : {
2207 44044 : nCountSubEltsOut--;
2208 :
2209 44044 : XSComplexTypeDefinition *poEltCT = IsEltCompatibleOfFC(poElt);
2210 44044 : if (poEltCT)
2211 : {
2212 : // Might be a bit extreme, but for now we don't inline
2213 : // classes that have subclasses.
2214 32520 : 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 5616 : bSimpleEnoughOut = false;
2222 : }
2223 :
2224 32520 : if (oSetVisitedEltDecl.find(poElt) !=
2225 65040 : oSetVisitedEltDecl.end())
2226 : {
2227 14952 : if (m_oSetEltsForTopClass.find(poElt) ==
2228 19690 : m_oSetEltsForTopClass.end() &&
2229 4738 : m_oSetSimpleEnoughElts.find(poElt) ==
2230 19690 : m_oSetSimpleEnoughElts.end() &&
2231 2798 : aoSetXPathEltsForTopClass.find(osXPath) ==
2232 17750 : aoSetXPathEltsForTopClass.end())
2233 : {
2234 2054 : CPLString osIgnored;
2235 1027 : 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 1027 : m_oSetEltsForTopClass.insert(poElt);
2247 1027 : oVectorEltsForTopClass.push_back(poElt);
2248 1027 : aoSetXPathEltsForTopClass.insert(osXPath);
2249 : }
2250 : }
2251 : }
2252 : else
2253 : {
2254 17568 : oSetVisitedEltDecl.insert(poElt);
2255 :
2256 35136 : if (!bAlreadyVisitedMG &&
2257 17568 : poEltCT->getParticle() != nullptr)
2258 : {
2259 17568 : int nSubCountSubElt = 0;
2260 :
2261 : // Process attributes
2262 : XSAttributeUseList *poAttrList =
2263 17568 : poEltCT->getAttributeUses();
2264 : const size_t nAttrListSize =
2265 17568 : (poAttrList != nullptr) ? poAttrList->size()
2266 17568 : : 0;
2267 19216 : for (size_t j = 0; j < nAttrListSize; ++j)
2268 : {
2269 : XSAttributeUse *poAttr =
2270 1648 : poAttrList->elementAt(j);
2271 1648 : GMLASField oField;
2272 1648 : if (!SetFieldFromAttribute(oField, poAttr,
2273 : osFullXPath))
2274 : {
2275 0 : return false;
2276 : }
2277 3290 : if (!IsIgnoredXPath(oField.GetXPath()) &&
2278 1642 : oField.GetFixedValue().empty())
2279 : {
2280 : #ifdef DEBUG_SUPER_VERBOSE
2281 : CPLDebug(
2282 : "GMLAS",
2283 : "FindElementsWithMustBeToLevel: %s",
2284 : oField.GetXPath().c_str());
2285 : #endif
2286 1520 : nSubCountSubElt++;
2287 : }
2288 : }
2289 :
2290 17568 : bool bSubSimpleEnoughOut = true;
2291 17568 : 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 17568 : if (bSubSimpleEnoughOut)
2303 : {
2304 : #ifdef DEBUG_VERBOSE
2305 : CPLDebug("GMLAS", "%s is inlinable: %d fields",
2306 : osXPath.c_str(), nSubCountSubElt);
2307 : #endif
2308 7713 : m_oSetSimpleEnoughElts.insert(poElt);
2309 :
2310 7713 : nCountSubEltsOut += nSubCountSubElt;
2311 : }
2312 9855 : 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 11524 : if (transcode(poElt->getName()) != szFEATURE_COLLECTION)
2329 : {
2330 11524 : poEltCT = reinterpret_cast<XSComplexTypeDefinition *>(
2331 : poTypeDef);
2332 : // Process attributes
2333 : XSAttributeUseList *poAttrList =
2334 11524 : poEltCT->getAttributeUses();
2335 : const size_t nAttrListSize =
2336 11524 : (poAttrList != nullptr) ? poAttrList->size() : 0;
2337 22259 : for (size_t j = 0;
2338 22259 : bSimpleEnoughOut && j < nAttrListSize; ++j)
2339 : {
2340 10735 : XSAttributeUse *poAttr = poAttrList->elementAt(j);
2341 10735 : GMLASField oField;
2342 10735 : if (!SetFieldFromAttribute(oField, poAttr,
2343 : osFullXPath))
2344 : {
2345 0 : return false;
2346 : }
2347 21464 : if (!IsIgnoredXPath(oField.GetXPath()) &&
2348 10729 : oField.GetFixedValue().empty())
2349 : {
2350 : #ifdef DEBUG_SUPER_VERBOSE
2351 : CPLDebug("GMLAS",
2352 : "FindElementsWithMustBeToLevel: %s",
2353 : oField.GetXPath().c_str());
2354 : #endif
2355 10722 : nCountSubEltsOut++;
2356 : }
2357 : }
2358 : }
2359 : }
2360 :
2361 44044 : CPLString osTargetElement;
2362 44044 : if (poElt->getAnnotation() != nullptr)
2363 : {
2364 : CPLString osAnnot(transcode(
2365 318 : poElt->getAnnotation()->getAnnotationString()));
2366 :
2367 : #ifdef DEBUG_SUPER_VERBOSE
2368 : CPLDebug("GMLAS", "Annot: %s", osAnnot.c_str());
2369 : #endif
2370 159 : CPLXMLNode *psRoot = CPLParseXMLString(osAnnot);
2371 159 : CPLStripXMLNamespace(psRoot, nullptr, TRUE);
2372 : osTargetElement = CPLGetXMLValue(
2373 159 : psRoot, "=annotation.appinfo.targetElement", "");
2374 159 : 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 88289 : if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
2386 88289 : 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 19114 : else if (!bAlreadyVisitedMG &&
2455 7990 : 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 7823 : 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 7823 : XSModelGroup *psSubModelGroup = poParticle->getModelGroupTerm();
2471 7823 : 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 3301 : 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 27457 : 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 27457 : return true;
2513 : }
2514 :
2515 : /************************************************************************/
2516 : /* IsGMLNamespace() */
2517 : /************************************************************************/
2518 :
2519 689757 : bool GMLASSchemaAnalyzer::IsGMLNamespace(const CPLString &osURI)
2520 : {
2521 689757 : if (osURI.find(szGML_URI) == 0)
2522 2053 : return true;
2523 : // Below is mostly for unit tests were we use xmlns:gml="http://fake_gml"
2524 687704 : const auto oIter = m_oMapURIToPrefix.find(osURI);
2525 687704 : return oIter != m_oMapURIToPrefix.end() && oIter->second == szGML_PREFIX;
2526 : }
2527 :
2528 : /************************************************************************/
2529 : /* BuildMapCountOccurrencesOfSameName() */
2530 : /************************************************************************/
2531 :
2532 47016 : void GMLASSchemaAnalyzer::BuildMapCountOccurrencesOfSameName(
2533 : XSModelGroup *poModelGroup,
2534 : std::map<CPLString, int> &oMapCountOccurrencesOfSameName)
2535 : {
2536 47016 : XSParticleList *poParticles = poModelGroup->getParticles();
2537 266662 : for (size_t i = 0; i < poParticles->size(); ++i)
2538 : {
2539 219646 : XSParticle *poParticle = poParticles->elementAt(i);
2540 219646 : if (poParticle->getTermType() == XSParticle::TERM_ELEMENT)
2541 : {
2542 202133 : XSElementDeclaration *poElt = poParticle->getElementTerm();
2543 202133 : const CPLString osEltName(transcode(poElt->getName()));
2544 202133 : oMapCountOccurrencesOfSameName[osEltName]++;
2545 : }
2546 17513 : else if (poParticle->getTermType() == XSParticle::TERM_MODELGROUP)
2547 : {
2548 17380 : XSModelGroup *psSubModelGroup = poParticle->getModelGroupTerm();
2549 17380 : BuildMapCountOccurrencesOfSameName(psSubModelGroup,
2550 : oMapCountOccurrencesOfSameName);
2551 : }
2552 : }
2553 47016 : }
2554 :
2555 : /************************************************************************/
2556 : /* ComposeMinOccurs() */
2557 : /************************************************************************/
2558 :
2559 3837 : static int ComposeMinOccurs(int nVal1, int nVal2)
2560 : {
2561 3837 : return nVal1 * nVal2;
2562 : }
2563 :
2564 : /************************************************************************/
2565 : /* ComposeMaxOccurs() */
2566 : /************************************************************************/
2567 :
2568 3837 : static int ComposeMaxOccurs(int nVal1, int nVal2)
2569 : {
2570 3837 : if (nVal1 == MAXOCCURS_UNLIMITED || nVal2 == MAXOCCURS_UNLIMITED)
2571 3119 : return MAXOCCURS_UNLIMITED;
2572 718 : return nVal1 * nVal2;
2573 : }
2574 :
2575 : /************************************************************************/
2576 : /* ExploreModelGroup() */
2577 : /************************************************************************/
2578 :
2579 47020 : 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 47020 : if (oSetVisitedModelGroups.find(poModelGroup) !=
2586 94040 : 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 47020 : oSetVisitedModelGroups.insert(poModelGroup);
2593 :
2594 47020 : 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 47020 : if (poMainAttrList != nullptr)
2603 : {
2604 214 : const size_t nMainAttrListSize = poMainAttrList->size();
2605 1112 : for (size_t j = 0; j < nMainAttrListSize; ++j)
2606 : {
2607 899 : GMLASField oField;
2608 899 : XSAttributeUse *poAttr = poMainAttrList->elementAt(j);
2609 899 : if (!SetFieldFromAttribute(oField, poAttr, oClass.GetXPath()))
2610 : {
2611 1 : return false;
2612 : }
2613 :
2614 898 : 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 23 : if (!oField.GetFixedValue().empty() ||
2621 10 : !oField.GetDefaultValue().empty())
2622 : {
2623 5 : oField.SetIgnored();
2624 : }
2625 : else
2626 : {
2627 8 : continue;
2628 : }
2629 : }
2630 :
2631 890 : oClass.AddField(oField);
2632 : }
2633 : }
2634 :
2635 47019 : 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 47019 : if (oClass.GetXPath() == "gml:metaDataProperty" &&
2641 0 : poModelGroup->getCompositor() == XSModelGroup::COMPOSITOR_SEQUENCE &&
2642 47019 : 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 47019 : (poModelGroup->getCompositor() == XSModelGroup::COMPOSITOR_CHOICE);
2664 47019 : int nGroup = 0;
2665 :
2666 264877 : for (size_t i = 0; i < poParticles->size(); ++i)
2667 : {
2668 219329 : XSParticle *poParticle = poParticles->elementAt(i);
2669 424537 : const bool bRepeatedParticle = poParticle->getMaxOccursUnbounded() ||
2670 205208 : poParticle->getMaxOccurs() > 1;
2671 219329 : const int nMinOccurs = static_cast<int>(poParticle->getMinOccurs());
2672 : const int nMaxOccurs =
2673 219329 : poParticle->getMaxOccursUnbounded()
2674 424537 : ? MAXOCCURS_UNLIMITED
2675 205208 : : static_cast<int>(poParticle->getMaxOccurs());
2676 :
2677 219329 : if (poParticle->getTermType() == XSParticle::TERM_ELEMENT)
2678 : {
2679 201882 : XSElementDeclaration *poElt = poParticle->getElementTerm();
2680 201882 : const CPLString osEltName(transcode(poElt->getName()));
2681 :
2682 201882 : const auto oIter = oMapCountOccurrencesOfSameName.find(osEltName);
2683 : const bool bEltNameWillNeedPrefix =
2684 403764 : oIter != oMapCountOccurrencesOfSameName.end() &&
2685 201882 : oIter->second > 1;
2686 201882 : const CPLString osEltNS(transcode(poElt->getNamespace()));
2687 : const CPLString osPrefixedEltName((bEltNameWillNeedPrefix
2688 403760 : ? GetPrefix(osEltNS) + "_"
2689 807520 : : CPLString()) +
2690 201882 : osEltName);
2691 201882 : const CPLString osOnlyElementXPath(MakeXPath(osEltNS, osEltName));
2692 403764 : const CPLString osElementXPath(oClass.GetXPath() + "/" +
2693 201882 : osOnlyElementXPath);
2694 : #ifdef DEBUG_VERBOSE
2695 : CPLDebug("GMLAS", "Iterating through %s", osElementXPath.c_str());
2696 : #endif
2697 :
2698 201882 : if (IsIgnoredXPath(osElementXPath))
2699 : {
2700 : #ifdef DEBUG_VERBOSE
2701 : CPLDebug("GMLAS", "%s is in ignored xpaths",
2702 : osElementXPath.c_str());
2703 : #endif
2704 4 : continue;
2705 : }
2706 :
2707 201878 : CPLString osTargetElement;
2708 201878 : if (poElt->getAnnotation() != nullptr)
2709 : {
2710 : CPLString osAnnot(
2711 976 : transcode(poElt->getAnnotation()->getAnnotationString()));
2712 :
2713 : #ifdef DEBUG_SUPER_VERBOSE
2714 : CPLDebug("GMLAS", "Annot: %s", osAnnot.c_str());
2715 : #endif
2716 488 : CPLXMLNode *psRoot = CPLParseXMLString(osAnnot);
2717 488 : CPLStripXMLNamespace(psRoot, nullptr, TRUE);
2718 : osTargetElement = CPLGetXMLValue(
2719 488 : psRoot, "=annotation.appinfo.targetElement", "");
2720 488 : CPLDestroyXMLNode(psRoot);
2721 : #ifdef DEBUG_VERBOSE
2722 : if (!osTargetElement.empty())
2723 : CPLDebug("GMLAS", "targetElement: %s",
2724 : osTargetElement.c_str());
2725 : #endif
2726 : }
2727 :
2728 201878 : XSTypeDefinition *poTypeDef = poElt->getTypeDefinition();
2729 :
2730 201878 : std::vector<XSElementDeclaration *> apoImplEltList;
2731 201878 : GetConcreteImplementationTypes(poElt, apoImplEltList);
2732 :
2733 : std::vector<XSElementDeclaration *> apoChildrenElements =
2734 201878 : GetConstraintChildrenElements(osElementXPath);
2735 :
2736 : // Special case for a GML geometry property
2737 201878 : OGRwkbGeometryType eGeomType =
2738 : wkbNone; // to make Visual Studio happy
2739 201878 : CPL_IGNORE_RET_VAL(eGeomType); // to make cppcheck happy
2740 :
2741 201878 : 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 202692 : else if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
2752 817 : (eGeomType = GetOGRGeometryType(poTypeDef)) != wkbNone)
2753 : {
2754 618 : GMLASField oField;
2755 309 : oField.SetName(osPrefixedEltName);
2756 309 : oField.SetMinOccurs(nMinOccurs);
2757 309 : oField.SetMaxOccurs(nMaxOccurs);
2758 309 : oField.SetType(GMLAS_FT_GEOMETRY, szFAKEXS_GEOMETRY);
2759 309 : 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 288 : oField.SetGeomType(eGeomType);
2771 309 : oField.SetXPath(osElementXPath);
2772 309 : oField.SetDocumentation(GetAnnotationDoc(poElt));
2773 :
2774 309 : oClass.AddField(oField);
2775 : }
2776 :
2777 202122 : else if (IsGMLNamespace(osEltNS) &&
2778 556 : (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 201960 : else if (poElt->getAbstract() && IsGMLNamespace(osEltNS) &&
2800 230 : osEltName != "_Feature" &&
2801 201960 : osEltName != "AbstractFeature" &&
2802 108 : osEltName != "AbstractTimeObject")
2803 : {
2804 178 : GMLASField oField;
2805 89 : oField.SetName(osPrefixedEltName);
2806 89 : oField.SetMinOccurs(nMinOccurs);
2807 89 : oField.SetMaxOccurs(nMaxOccurs);
2808 89 : 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 58 : oField.SetType(GMLAS_FT_ANYTYPE, szXS_ANY_TYPE);
2818 : }
2819 89 : oField.SetIncludeThisEltInBlob(true);
2820 89 : oField.SetDocumentation(GetAnnotationDoc(poElt));
2821 :
2822 824 : for (size_t j = 0; j < apoImplEltList.size(); j++)
2823 : {
2824 735 : XSElementDeclaration *poSubElt = apoImplEltList[j];
2825 735 : oField.AddAlternateXPath(
2826 1470 : oClass.GetXPath() + "/" +
2827 1470 : MakeXPath(transcode(poSubElt->getNamespace()),
2828 1470 : transcode(poSubElt->getName())));
2829 : }
2830 :
2831 89 : 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 417880 : else if (!apoImplEltList.empty() ||
2837 201236 : (m_oSetEltsForTopClass.find(poElt) !=
2838 216426 : m_oSetEltsForTopClass.end() &&
2839 15190 : m_oSetSimpleEnoughElts.find(poElt) ==
2840 216644 : m_oSetSimpleEnoughElts.end()))
2841 : {
2842 15403 : 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 186051 : else if (poElt->getAbstract())
2852 : {
2853 : // Do nothing with it since it cannot be instantiated
2854 : // in a valid way.
2855 48 : CPLDebug("GMLAS",
2856 : "Ignoring %s that is abstract without realizations",
2857 : osElementXPath.c_str());
2858 : }
2859 :
2860 : // Simple type like string, int, etc...
2861 186003 : else if (poTypeDef->getTypeCategory() ==
2862 : XSTypeDefinition::SIMPLE_TYPE)
2863 : {
2864 141883 : XSSimpleTypeDefinition *poST =
2865 : reinterpret_cast<XSSimpleTypeDefinition *>(poTypeDef);
2866 283766 : GMLASField oField;
2867 141883 : SetFieldTypeAndWidthFromDefinition(poST, oField);
2868 141883 : oField.SetMinOccurs((bIsChoice) ? 0 : nMinOccurs);
2869 141883 : oField.SetMaxOccurs(nMaxOccurs);
2870 141883 : oField.SetDocumentation(GetAnnotationDoc(poElt));
2871 :
2872 141883 : bool bNeedAuxTable = false;
2873 141883 : const bool bIsList = (poST->getVariety() ==
2874 141883 : XSSimpleTypeDefinition::VARIETY_LIST);
2875 141883 : 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 144338 : if (m_bUseArrays && bRepeatedParticle &&
2893 2455 : IsCompatibleOfArray(oField.GetType()))
2894 : {
2895 2178 : oField.SetArray(true);
2896 : }
2897 139705 : else if (bRepeatedParticle)
2898 : {
2899 289 : bNeedAuxTable = true;
2900 : }
2901 141883 : 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 141594 : oField.SetName(osPrefixedEltName);
2930 141594 : oField.SetXPath(osElementXPath);
2931 141594 : if (!bIsChoice && nMinOccurs > 0 && !poElt->getNillable())
2932 : {
2933 42045 : oField.SetNotNullable(true);
2934 : }
2935 141594 : 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 141692 : 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 44120 : else if (poTypeDef->getTypeCategory() ==
2957 : XSTypeDefinition::COMPLEX_TYPE)
2958 : {
2959 44120 : XSComplexTypeDefinition *poEltCT =
2960 : reinterpret_cast<XSComplexTypeDefinition *>(poTypeDef);
2961 44120 : std::vector<GMLASField> aoFields;
2962 44120 : bool bNothingMoreToDo = false;
2963 44120 : std::vector<GMLASFeatureClass> aoNestedClasses;
2964 :
2965 : const int nMinOccursEltParticle =
2966 44120 : poEltCT->getParticle()
2967 70967 : ? static_cast<int>(
2968 26847 : poEltCT->getParticle()->getMinOccurs())
2969 44120 : : -1;
2970 : const int nMaxOccursEltParticle =
2971 44120 : poEltCT->getParticle()
2972 70967 : ? (poEltCT->getParticle()->getMaxOccursUnbounded()
2973 53500 : ? MAXOCCURS_UNLIMITED
2974 : : static_cast<int>(
2975 26653 : poEltCT->getParticle()->getMaxOccurs()))
2976 44120 : : -1;
2977 :
2978 44120 : const bool bEltRepeatedParticle =
2979 44120 : nMaxOccursEltParticle > 1 ||
2980 : nMaxOccursEltParticle == MAXOCCURS_UNLIMITED;
2981 44120 : const bool bMoveNestedClassToTop =
2982 44120 : !bRepeatedParticle && !bEltRepeatedParticle;
2983 :
2984 : // Process attributes
2985 44120 : XSAttributeUseList *poAttrList = poEltCT->getAttributeUses();
2986 : const size_t nAttrListSize =
2987 44120 : (poAttrList != nullptr) ? poAttrList->size() : 0;
2988 64715 : for (size_t j = 0; j < nAttrListSize; ++j)
2989 : {
2990 20595 : XSAttributeUse *poAttr = poAttrList->elementAt(j);
2991 20595 : GMLASField oField;
2992 : CPLString osNamePrefix(bMoveNestedClassToTop
2993 : ? osPrefixedEltName
2994 20595 : : CPLString());
2995 20595 : if (!SetFieldFromAttribute(oField, poAttr, osElementXPath,
2996 : osNamePrefix))
2997 : {
2998 0 : return false;
2999 : }
3000 20595 : if (nMinOccurs == 0 || bIsChoice)
3001 : {
3002 7676 : oField.SetMinOccurs(0);
3003 7676 : oField.SetNotNullable(false);
3004 : }
3005 :
3006 20595 : 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 46 : if (!oField.GetFixedValue().empty() ||
3013 21 : !oField.GetDefaultValue().empty())
3014 : {
3015 5 : oField.SetIgnored();
3016 : }
3017 : else
3018 : {
3019 20 : continue;
3020 : }
3021 : }
3022 :
3023 20575 : aoFields.push_back(oField);
3024 : }
3025 :
3026 : // Deal with anyAttributes (or any element that also imply it)
3027 44120 : XSWildcard *poAttrWildcard = poEltCT->getAttributeWildcard();
3028 44120 : if (poAttrWildcard != nullptr)
3029 : {
3030 472 : GMLASField oField;
3031 236 : oField.SetType(GMLASField::GetTypeFromString(szXS_STRING),
3032 : szFAKEXS_JSON_DICT);
3033 236 : if (!bMoveNestedClassToTop)
3034 : {
3035 48 : oField.SetName("anyAttributes");
3036 : }
3037 : else
3038 : {
3039 188 : oField.SetName(osPrefixedEltName + "_anyAttributes");
3040 : }
3041 236 : oField.SetXPath(osElementXPath + "/" + szAT_ANY_ATTR);
3042 236 : oField.SetDocumentation(
3043 472 : GetAnnotationDoc(poAttrWildcard->getAnnotation()));
3044 :
3045 236 : aoFields.push_back(oField);
3046 : }
3047 :
3048 44120 : XSSimpleTypeDefinition *poST = poEltCT->getSimpleType();
3049 44120 : if (poST != nullptr)
3050 : {
3051 : /* Case of an element, generally with attributes */
3052 :
3053 33994 : GMLASField oField;
3054 16997 : SetFieldTypeAndWidthFromDefinition(poST, oField);
3055 153 : if (bRepeatedParticle && nAttrListSize == 0 &&
3056 17196 : 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 16951 : else if (bRepeatedParticle)
3068 : {
3069 107 : oField.SetName("value");
3070 107 : oField.SetMinOccurs(1);
3071 107 : oField.SetMaxOccurs(1);
3072 107 : oField.SetNotNullable(true);
3073 : }
3074 : else
3075 : {
3076 16844 : if (nMinOccurs == 0)
3077 : {
3078 9943 : for (size_t j = 0; j < aoFields.size(); j++)
3079 : {
3080 5108 : aoFields[j].SetMinOccurs(0);
3081 5108 : aoFields[j].SetNotNullable(false);
3082 : }
3083 : }
3084 :
3085 16844 : oField.SetName(osPrefixedEltName);
3086 16844 : oField.SetMinOccurs((bIsChoice) ? 0 : nMinOccurs);
3087 16844 : 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 17052 : 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 16997 : oField.SetXPath(osElementXPath);
3107 16997 : oField.SetDocumentation(GetAnnotationDoc(poElt));
3108 :
3109 16997 : aoFields.push_back(oField);
3110 16997 : if (oField.IsArray())
3111 : {
3112 46 : oClass.AddField(oField);
3113 46 : bNothingMoreToDo = true;
3114 : }
3115 : }
3116 27123 : else if (IsAnyType(poEltCT))
3117 : {
3118 618 : GMLASField oField;
3119 309 : oField.SetType(GMLAS_FT_ANYTYPE, szXS_ANY_TYPE);
3120 309 : if (bRepeatedParticle)
3121 : {
3122 48 : oField.SetName("value");
3123 48 : oField.SetMinOccurs(1);
3124 48 : oField.SetMaxOccurs(1);
3125 48 : 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 309 : oField.SetXPath(osElementXPath);
3143 309 : oField.SetDocumentation(GetAnnotationDoc(poElt));
3144 :
3145 309 : aoFields.push_back(oField);
3146 : }
3147 :
3148 : // Is it an element that we already visited ? (cycle)
3149 53352 : else if (poEltCT->getParticle() != nullptr &&
3150 0 : oSetVisitedModelGroups.find(
3151 26538 : poEltCT->getParticle()->getModelGroupTerm()) !=
3152 53352 : 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 26814 : GMLASFeatureClass oNestedClass;
3168 26814 : oNestedClass.SetName(oClass.GetName() + "_" +
3169 : osPrefixedEltName);
3170 26814 : oNestedClass.SetXPath(osElementXPath);
3171 26814 : oNestedClass.SetDocumentation(GetAnnotationDoc(poElt));
3172 :
3173 : // NULL can happen, for example for gml:ReferenceType
3174 : // that is an empty sequence with just attributes
3175 26814 : 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 26538 : oSetVisitedModelGroups);
3183 :
3184 : std::map<CPLString, int>
3185 26538 : oMapCountOccurrencesOfSameNameSub;
3186 26538 : BuildMapCountOccurrencesOfSameName(
3187 : poEltCT->getParticle()->getModelGroupTerm(),
3188 : oMapCountOccurrencesOfSameNameSub);
3189 :
3190 26538 : 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 53793 : if (IsGMLNamespace(transcode(poTypeDef->getNamespace())) &&
3204 53793 : 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 26814 : 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 22867 : oNestedClass.GetFields();
3343 412029 : for (size_t j = 0; j < osNestedClassFields.size(); j++)
3344 : {
3345 778324 : GMLASField oField(osNestedClassFields[j]);
3346 389162 : oField.SetName(osPrefixedEltName + "_" +
3347 389162 : oField.GetName());
3348 990650 : if (nMinOccurs == 0 ||
3349 601488 : (poEltCT->getParticle() != nullptr &&
3350 300744 : poEltCT->getParticle()->getMinOccurs() == 0))
3351 : {
3352 88589 : oField.SetMinOccurs(0);
3353 88589 : oField.SetNotNullable(false);
3354 : }
3355 389162 : aoFields.push_back(oField);
3356 : }
3357 :
3358 22867 : 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 7575 : if (m_bUseArrays && nAttrListSize == 0 &&
3375 6615 : oNestedClass.GetNestedClasses().empty() &&
3376 3120 : oNestedClass.GetFields().size() == 1 &&
3377 141 : IsCompatibleOfArray(
3378 8027 : 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 3901 : 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 3848 : oNestedClass.SetIsRepeatedSequence(
3452 : bEltRepeatedParticle);
3453 3848 : oNestedClass.PrependFields(aoFields);
3454 :
3455 3848 : oClass.AddNestedClass(oNestedClass);
3456 : }
3457 :
3458 7802 : GMLASField oField;
3459 3901 : oField.SetName(osPrefixedEltName);
3460 3901 : oField.SetXPath(osElementXPath);
3461 3901 : if (bRepeatedParticle)
3462 : {
3463 3800 : if (poEltCT->getParticle() != nullptr)
3464 : {
3465 3791 : oField.SetMinOccurs(ComposeMinOccurs(
3466 : nMinOccurs, nMinOccursEltParticle));
3467 3791 : oField.SetMaxOccurs(ComposeMaxOccurs(
3468 : nMaxOccurs, nMaxOccursEltParticle));
3469 : }
3470 : else
3471 : {
3472 9 : oField.SetMinOccurs(nMinOccurs);
3473 9 : 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 3901 : oField.SetCategory(
3484 : GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
3485 3901 : oField.SetRelatedClassXPath(oField.GetXPath());
3486 3901 : oClass.AddField(oField);
3487 : }
3488 :
3489 3947 : bNothingMoreToDo = true;
3490 : }
3491 : }
3492 :
3493 44120 : if (bNothingMoreToDo)
3494 : {
3495 : // Nothing to do
3496 : }
3497 40127 : else if (bRepeatedParticle)
3498 : {
3499 310 : GMLASFeatureClass oNestedClass;
3500 155 : oNestedClass.SetName(oClass.GetName() + "_" +
3501 : osPrefixedEltName);
3502 155 : oNestedClass.SetXPath(osElementXPath);
3503 155 : oNestedClass.AppendFields(aoFields);
3504 155 : oNestedClass.SetDocumentation(GetAnnotationDoc(poElt));
3505 155 : oClass.AddNestedClass(oNestedClass);
3506 :
3507 310 : GMLASField oField;
3508 155 : oField.SetName(osPrefixedEltName);
3509 155 : oField.SetXPath(osElementXPath);
3510 155 : oField.SetMinOccurs((bIsChoice) ? 0 : nMinOccurs);
3511 155 : oField.SetMaxOccurs(nMaxOccurs);
3512 155 : oField.SetCategory(
3513 : GMLASField::PATH_TO_CHILD_ELEMENT_NO_LINK);
3514 155 : oField.SetRelatedClassXPath(oField.GetXPath());
3515 155 : oClass.AddField(oField);
3516 : }
3517 : else
3518 : {
3519 39972 : oClass.AppendFields(aoFields);
3520 42918 : for (size_t j = 0; j < aoNestedClasses.size(); j++)
3521 : {
3522 2946 : oClass.AddNestedClass(aoNestedClasses[j]);
3523 : }
3524 : }
3525 : }
3526 : }
3527 17447 : else if (poParticle->getTermType() == XSParticle::TERM_MODELGROUP)
3528 : {
3529 17315 : XSModelGroup *psSubModelGroup = poParticle->getModelGroupTerm();
3530 17315 : if (bRepeatedParticle)
3531 : {
3532 1682 : GMLASFeatureClass oNestedClass;
3533 1682 : CPLString osGroupName;
3534 : XSModelGroupDefinition *psGroupDefinition =
3535 1682 : GetGroupDefinition(psSubModelGroup);
3536 1682 : 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 1541 : if (psSubModelGroup->getCompositor() ==
3546 : XSModelGroup::COMPOSITOR_CHOICE)
3547 : {
3548 : std::set<XSModelGroup *> oSetNewVisitedModelGroups(
3549 1541 : oSetVisitedModelGroups);
3550 1541 : GMLASFeatureClass oTmpClass;
3551 1541 : oTmpClass.SetName(oClass.GetName());
3552 1541 : oTmpClass.SetXPath(oClass.GetXPath());
3553 1541 : if (!ExploreModelGroup(psSubModelGroup, nullptr,
3554 : oTmpClass, nRecursionCounter + 1,
3555 : oSetNewVisitedModelGroups,
3556 : poModel,
3557 : oMapCountOccurrencesOfSameName))
3558 : {
3559 0 : return false;
3560 : }
3561 1541 : bool bHasArray = false;
3562 : std::vector<GMLASField> &oTmpFields =
3563 1541 : oTmpClass.GetFields();
3564 151197 : for (size_t j = 0; j < oTmpFields.size(); ++j)
3565 : {
3566 149726 : if (oTmpFields[j].IsArray())
3567 : {
3568 70 : bHasArray = true;
3569 70 : break;
3570 : }
3571 : }
3572 1541 : if (!bHasArray)
3573 : {
3574 150917 : for (size_t j = 0; j < oTmpFields.size(); ++j)
3575 : {
3576 149446 : oTmpFields[j].SetMayAppearOutOfOrder(true);
3577 149446 : oClass.AddField(oTmpFields[j]);
3578 : }
3579 1471 : 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 15633 : oSetVisitedModelGroups);
3633 15633 : if (!ExploreModelGroup(psSubModelGroup, nullptr, oClass,
3634 : nRecursionCounter + 1,
3635 : oSetNewVisitedModelGroups, poModel,
3636 : oMapCountOccurrencesOfSameName))
3637 : {
3638 0 : return false;
3639 : }
3640 : }
3641 : }
3642 132 : else if (poParticle->getTermType() == XSParticle::TERM_WILDCARD)
3643 : {
3644 : /* Special case for a layer that matches everything, as found */
3645 : /* in swe:extension */
3646 132 : XSWildcard *poWildcard = poParticle->getWildcardTerm();
3647 264 : GMLASField oField;
3648 132 : oField.SetXPath(oClass.GetXPath() + szMATCH_ALL);
3649 132 : oField.SetName("value");
3650 132 : oField.SetType(GMLAS_FT_ANYTYPE, szXS_ANY_TYPE);
3651 132 : oField.SetIncludeThisEltInBlob(true);
3652 132 : oField.SetMinOccurs(nMinOccurs);
3653 132 : oField.SetMaxOccurs(1);
3654 132 : oField.SetDocumentation(
3655 264 : GetAnnotationDoc(poWildcard->getAnnotation()));
3656 132 : oClass.AddField(oField);
3657 : }
3658 : }
3659 :
3660 45548 : return true;
3661 : }
|