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

Generated by: LCOV version 1.14