LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gmlas - ogrgmlasschemaanalyzer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1398 1521 91.9 %
Date: 2024-11-21 22:18:42 Functions: 48 48 100.0 %

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

Generated by: LCOV version 1.14