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