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

Generated by: LCOV version 1.14