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

Generated by: LCOV version 1.14