LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gmlas - ogrgmlasschemaanalyzer.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1405 1521 92.4 %
Date: 2025-07-02 23:05:47 Functions: 48 48 100.0 %

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

Generated by: LCOV version 1.14