LCOV - code coverage report
Current view: top level - ogr/ogrsf_frmts/gmlutils - parsexsd.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 442 532 83.1 %
Date: 2024-11-21 22:18:42 Functions: 15 15 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GML Reader
       4             :  * Purpose:  Implementation of GMLParseXSD()
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2005, Frank Warmerdam
       9             :  * Copyright (c) 2010-2014, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "cpl_port.h"
      15             : #include "parsexsd.h"
      16             : 
      17             : #include <cmath>
      18             : #include <cstdlib>
      19             : #include <cstring>
      20             : #include <set>
      21             : #include <string>
      22             : #include <utility>
      23             : 
      24             : #include "cpl_conv.h"
      25             : #include "cpl_error.h"
      26             : #include "cpl_http.h"
      27             : #include "cpl_minixml.h"
      28             : #include "cpl_string.h"
      29             : #include "ogr_core.h"
      30             : 
      31             : /************************************************************************/
      32             : /*                              StripNS()                               */
      33             : /*                                                                      */
      34             : /*      Return potentially shortened form of string with namespace      */
      35             : /*      stripped off if there is one.  Returns pointer into             */
      36             : /*      original string.                                                */
      37             : /************************************************************************/
      38        2817 : static const char *StripNS(const char *pszFullValue)
      39             : 
      40             : {
      41        2817 :     const char *pszColon = strstr(pszFullValue, ":");
      42        2817 :     if (pszColon == nullptr)
      43         219 :         return pszFullValue;
      44             :     else
      45        2598 :         return pszColon + 1;
      46             : }
      47             : 
      48             : /************************************************************************/
      49             : /*                   GetSimpleTypeProperties()                          */
      50             : /************************************************************************/
      51             : 
      52         228 : static bool GetSimpleTypeProperties(CPLXMLNode *psTypeNode,
      53             :                                     GMLPropertyType *pGMLType, int *pnWidth,
      54             :                                     int *pnPrecision)
      55             : {
      56             :     const char *pszBase =
      57         228 :         StripNS(CPLGetXMLValue(psTypeNode, "restriction.base", ""));
      58             : 
      59         228 :     if (EQUAL(pszBase, "decimal"))
      60             :     {
      61          67 :         *pGMLType = GMLPT_Real;
      62             :         const char *pszWidth =
      63          67 :             CPLGetXMLValue(psTypeNode, "restriction.totalDigits.value", "0");
      64             :         const char *pszPrecision =
      65          67 :             CPLGetXMLValue(psTypeNode, "restriction.fractionDigits.value", "0");
      66          67 :         *pnWidth = atoi(pszWidth);
      67          67 :         *pnPrecision = atoi(pszPrecision);
      68          67 :         return true;
      69             :     }
      70             : 
      71         161 :     else if (EQUAL(pszBase, "float"))
      72             :     {
      73           1 :         *pGMLType = GMLPT_Float;
      74           1 :         return true;
      75             :     }
      76             : 
      77         160 :     else if (EQUAL(pszBase, "double"))
      78             :     {
      79           0 :         *pGMLType = GMLPT_Real;
      80           0 :         return true;
      81             :     }
      82             : 
      83         160 :     else if (EQUAL(pszBase, "integer"))
      84             :     {
      85          47 :         *pGMLType = GMLPT_Integer;
      86             :         const char *pszWidth =
      87          47 :             CPLGetXMLValue(psTypeNode, "restriction.totalDigits.value", "0");
      88          47 :         *pnWidth = atoi(pszWidth);
      89          47 :         return true;
      90             :     }
      91             : 
      92         113 :     else if (EQUAL(pszBase, "long"))
      93             :     {
      94          12 :         *pGMLType = GMLPT_Integer64;
      95             :         const char *pszWidth =
      96          12 :             CPLGetXMLValue(psTypeNode, "restriction.totalDigits.value", "0");
      97          12 :         *pnWidth = atoi(pszWidth);
      98          12 :         return true;
      99             :     }
     100             : 
     101         101 :     else if (EQUAL(pszBase, "unsignedLong"))
     102             :     {
     103             :         // Optimistically map to signed integer...
     104           0 :         *pGMLType = GMLPT_Integer64;
     105             :         const char *pszWidth =
     106           0 :             CPLGetXMLValue(psTypeNode, "restriction.totalDigits.value", "0");
     107           0 :         *pnWidth = atoi(pszWidth);
     108           0 :         return true;
     109             :     }
     110             : 
     111         101 :     else if (EQUAL(pszBase, "string"))
     112             :     {
     113          97 :         *pGMLType = GMLPT_String;
     114             :         const char *pszWidth =
     115          97 :             CPLGetXMLValue(psTypeNode, "restriction.maxLength.value", "0");
     116          97 :         *pnWidth = atoi(pszWidth);
     117          97 :         return true;
     118             :     }
     119             : 
     120           4 :     else if (EQUAL(pszBase, "date"))
     121             :     {
     122           0 :         *pGMLType = GMLPT_Date;
     123           0 :         return true;
     124             :     }
     125             : 
     126           4 :     else if (EQUAL(pszBase, "time"))
     127             :     {
     128           0 :         *pGMLType = GMLPT_Time;
     129           0 :         return true;
     130             :     }
     131             : 
     132           4 :     else if (EQUAL(pszBase, "dateTime"))
     133             :     {
     134           0 :         *pGMLType = GMLPT_DateTime;
     135           0 :         return true;
     136             :     }
     137             : 
     138           4 :     else if (EQUAL(pszBase, "boolean"))
     139             :     {
     140           3 :         *pGMLType = GMLPT_Boolean;
     141           3 :         return true;
     142             :     }
     143             : 
     144           1 :     else if (EQUAL(pszBase, "short"))
     145             :     {
     146           1 :         *pGMLType = GMLPT_Short;
     147           1 :         return true;
     148             :     }
     149             : 
     150           0 :     return false;
     151             : }
     152             : 
     153             : /************************************************************************/
     154             : /*                      LookForSimpleType()                             */
     155             : /************************************************************************/
     156             : 
     157          11 : static bool LookForSimpleType(CPLXMLNode *psSchemaNode,
     158             :                               const char *pszStrippedNSType,
     159             :                               GMLPropertyType *pGMLType, int *pnWidth,
     160             :                               int *pnPrecision)
     161             : {
     162          11 :     CPLXMLNode *psThis = psSchemaNode->psChild;
     163         102 :     for (; psThis != nullptr; psThis = psThis->psNext)
     164             :     {
     165         220 :         if (psThis->eType == CXT_Element &&
     166          95 :             EQUAL(psThis->pszValue, "simpleType") &&
     167           2 :             EQUAL(CPLGetXMLValue(psThis, "name", ""), pszStrippedNSType))
     168             :         {
     169           2 :             break;
     170             :         }
     171             :     }
     172          11 :     if (psThis == nullptr)
     173           9 :         return false;
     174             : 
     175           2 :     return GetSimpleTypeProperties(psThis, pGMLType, pnWidth, pnPrecision);
     176             : }
     177             : 
     178             : /************************************************************************/
     179             : /*                      GetSingleChildElement()                         */
     180             : /************************************************************************/
     181             : 
     182             : /* Returns the child element whose name is pszExpectedValue only if */
     183             : /* there is only one child that is an element. */
     184           3 : static CPLXMLNode *GetSingleChildElement(CPLXMLNode *psNode,
     185             :                                          const char *pszExpectedValue)
     186             : {
     187           3 :     if (psNode == nullptr)
     188           0 :         return nullptr;
     189             : 
     190           3 :     CPLXMLNode *psIter = psNode->psChild;
     191           3 :     if (psIter == nullptr)
     192           0 :         return nullptr;
     193             : 
     194           3 :     CPLXMLNode *psChild = nullptr;
     195           9 :     while (psIter != nullptr)
     196             :     {
     197           6 :         if (psIter->eType == CXT_Element)
     198             :         {
     199           3 :             if (psChild != nullptr)
     200           0 :                 return nullptr;
     201           3 :             if (pszExpectedValue != nullptr &&
     202           3 :                 strcmp(psIter->pszValue, pszExpectedValue) != 0)
     203           0 :                 return nullptr;
     204           3 :             psChild = psIter;
     205             :         }
     206           6 :         psIter = psIter->psNext;
     207             :     }
     208           3 :     return psChild;
     209             : }
     210             : 
     211             : /************************************************************************/
     212             : /*                      CheckMinMaxOccursCardinality()                  */
     213             : /************************************************************************/
     214             : 
     215           2 : static int CheckMinMaxOccursCardinality(CPLXMLNode *psNode)
     216             : {
     217           2 :     const char *pszMinOccurs = CPLGetXMLValue(psNode, "minOccurs", nullptr);
     218           2 :     const char *pszMaxOccurs = CPLGetXMLValue(psNode, "maxOccurs", nullptr);
     219           1 :     return (pszMinOccurs == nullptr || EQUAL(pszMinOccurs, "0") ||
     220           4 :             EQUAL(pszMinOccurs, "1")) &&
     221           3 :            (pszMaxOccurs == nullptr || EQUAL(pszMaxOccurs, "1"));
     222             : }
     223             : 
     224             : /************************************************************************/
     225             : /*                     GetListTypeFromSingleType()                      */
     226             : /************************************************************************/
     227             : 
     228          11 : static GMLPropertyType GetListTypeFromSingleType(GMLPropertyType eType)
     229             : {
     230          11 :     if (eType == GMLPT_String)
     231           2 :         return GMLPT_StringList;
     232           9 :     if (eType == GMLPT_Integer || eType == GMLPT_Short)
     233           2 :         return GMLPT_IntegerList;
     234           7 :     if (eType == GMLPT_Integer64)
     235           2 :         return GMLPT_Integer64List;
     236           5 :     if (eType == GMLPT_Real || eType == GMLPT_Float)
     237           2 :         return GMLPT_RealList;
     238           3 :     if (eType == GMLPT_Boolean)
     239           1 :         return GMLPT_BooleanList;
     240           2 :     if (eType == GMLPT_FeatureProperty)
     241           2 :         return GMLPT_FeaturePropertyList;
     242           0 :     return eType;
     243             : }
     244             : 
     245             : /************************************************************************/
     246             : /*                      ParseFeatureType()                              */
     247             : /************************************************************************/
     248             : 
     249             : typedef struct
     250             : {
     251             :     const char *pszName;
     252             :     OGRwkbGeometryType eType;
     253             : } AssocNameType;
     254             : 
     255             : static const AssocNameType apsPropertyTypes[] = {
     256             :     {"GeometryPropertyType", wkbUnknown},
     257             :     {"PointPropertyType", wkbPoint},
     258             :     {"LineStringPropertyType", wkbLineString},
     259             :     {"CurvePropertyType", wkbCompoundCurve},
     260             :     {"PolygonPropertyType", wkbPolygon},
     261             :     {"SurfacePropertyType", wkbCurvePolygon},
     262             :     {"MultiPointPropertyType", wkbMultiPoint},
     263             :     {"MultiLineStringPropertyType", wkbMultiLineString},
     264             :     {"MultiCurvePropertyType", wkbMultiCurve},
     265             :     {"MultiPolygonPropertyType", wkbMultiPolygon},
     266             :     {"MultiSurfacePropertyType", wkbMultiSurface},
     267             :     {"MultiGeometryPropertyType", wkbGeometryCollection},
     268             :     {"GeometryAssociationType", wkbUnknown},
     269             :     {nullptr, wkbUnknown},
     270             : };
     271             : 
     272             : /* Found in FME .xsd  (e.g. <element ref="gml:curveProperty" minOccurs="0"/>) */
     273             : static const AssocNameType apsRefTypes[] = {
     274             :     {"pointProperty", wkbPoint},
     275             :     {"curveProperty", wkbLineString},  // Should we promote to wkbCompoundCurve?
     276             :     {"surfaceProperty", wkbPolygon},   // Should we promote to wkbCurvePolygon?
     277             :     {"multiPointProperty", wkbMultiPoint},
     278             :     {"multiCurveProperty", wkbMultiLineString},
     279             :     // Should we promote to wkbMultiSurface?
     280             :     {"multiSurfaceProperty", wkbMultiPolygon},
     281             :     {nullptr, wkbUnknown},
     282             : };
     283             : 
     284             : static GMLFeatureClass *GMLParseFeatureType(CPLXMLNode *psSchemaNode,
     285             :                                             const char *pszName,
     286             :                                             CPLXMLNode *psThis);
     287             : 
     288         556 : static GMLFeatureClass *GMLParseFeatureType(CPLXMLNode *psSchemaNode,
     289             :                                             const char *pszName,
     290             :                                             const char *pszType)
     291             : {
     292         556 :     CPLXMLNode *psThis = psSchemaNode->psChild;
     293        4927 :     for (; psThis != nullptr; psThis = psThis->psNext)
     294             :     {
     295       12238 :         if (psThis->eType == CXT_Element &&
     296        5849 :             EQUAL(psThis->pszValue, "complexType") &&
     297         922 :             EQUAL(CPLGetXMLValue(psThis, "name", ""), pszType))
     298             :         {
     299         556 :             break;
     300             :         }
     301             :     }
     302         556 :     if (psThis == nullptr)
     303           0 :         return nullptr;
     304             : 
     305         556 :     return GMLParseFeatureType(psSchemaNode, pszName, psThis);
     306             : }
     307             : 
     308         570 : static GMLFeatureClass *GMLParseFeatureType(CPLXMLNode *psSchemaNode,
     309             :                                             const char *pszName,
     310             :                                             CPLXMLNode *psComplexType)
     311             : {
     312             : 
     313             :     /* -------------------------------------------------------------------- */
     314             :     /*      Grab the sequence of extensions greatgrandchild.                */
     315             :     /* -------------------------------------------------------------------- */
     316             :     CPLXMLNode *psAttrSeq =
     317         570 :         CPLGetXMLNode(psComplexType, "complexContent.extension.sequence");
     318             : 
     319         570 :     if (psAttrSeq == nullptr)
     320             :     {
     321           0 :         return nullptr;
     322             :     }
     323             : 
     324             :     /* -------------------------------------------------------------------- */
     325             :     /*      We are pretty sure this going to be a valid Feature class       */
     326             :     /*      now, so create it.                                              */
     327             :     /* -------------------------------------------------------------------- */
     328         570 :     GMLFeatureClass *poClass = new GMLFeatureClass(pszName);
     329             : 
     330             :     /* -------------------------------------------------------------------- */
     331             :     /*      Loop over each of the attribute elements being defined for      */
     332             :     /*      this feature class.                                             */
     333             :     /* -------------------------------------------------------------------- */
     334         570 :     int nAttributeIndex = 0;
     335             : 
     336         570 :     bool bGotUnrecognizedType = false;
     337             : 
     338         570 :     CPLXMLNode *psAttrDef = psAttrSeq->psChild;
     339        2793 :     for (; psAttrDef != nullptr; psAttrDef = psAttrDef->psNext)
     340             :     {
     341        2232 :         if (strcmp(psAttrDef->pszValue, "group") == 0)
     342             :         {
     343             :             /* Too complex schema for us. Aborts parsing */
     344           0 :             delete poClass;
     345           9 :             return nullptr;
     346             :         }
     347             : 
     348             :         /* Parse stuff like:
     349             :         <xs:choice>
     350             :             <xs:element ref="gml:polygonProperty"/>
     351             :             <xs:element ref="gml:multiPolygonProperty"/>
     352             :         </xs:choice>
     353             :         as found in
     354             :         https://downloadagiv.blob.core.windows.net/overstromingsgebieden-en-oeverzones/2014_01/Overstromingsgebieden_en_oeverzones_2014_01_GML.zip
     355             :         */
     356        2232 :         if (strcmp(psAttrDef->pszValue, "choice") == 0)
     357             :         {
     358           1 :             CPLXMLNode *psChild = psAttrDef->psChild;
     359           1 :             bool bPolygon = false;
     360           1 :             bool bMultiPolygon = false;
     361           3 :             for (; psChild; psChild = psChild->psNext)
     362             :             {
     363           2 :                 if (psChild->eType != CXT_Element)
     364           0 :                     continue;
     365           2 :                 if (strcmp(psChild->pszValue, "element") == 0)
     366             :                 {
     367             :                     const char *pszRef =
     368           2 :                         CPLGetXMLValue(psChild, "ref", nullptr);
     369           2 :                     if (pszRef != nullptr)
     370             :                     {
     371           2 :                         if (strcmp(pszRef, "gml:polygonProperty") == 0)
     372             :                         {
     373           1 :                             bPolygon = true;
     374             :                         }
     375           1 :                         else if (strcmp(pszRef, "gml:multiPolygonProperty") ==
     376             :                                  0)
     377             :                         {
     378           1 :                             bMultiPolygon = true;
     379             :                         }
     380             :                         else
     381             :                         {
     382           0 :                             delete poClass;
     383           0 :                             return nullptr;
     384             :                         }
     385             :                     }
     386             :                     else
     387             :                     {
     388           0 :                         delete poClass;
     389           0 :                         return nullptr;
     390             :                     }
     391             :                 }
     392             :             }
     393           1 :             if (bPolygon && bMultiPolygon)
     394             :             {
     395           2 :                 poClass->AddGeometryProperty(new GMLGeometryPropertyDefn(
     396           1 :                     "", "", wkbMultiPolygon, nAttributeIndex, true));
     397             : 
     398           1 :                 nAttributeIndex++;
     399             :             }
     400        1997 :             continue;
     401             :         }
     402             : 
     403        2231 :         if (!EQUAL(psAttrDef->pszValue, "element"))
     404          93 :             continue;
     405             : 
     406             :         // MapServer WFS writes element type as an attribute of element
     407             :         // not as a simpleType definition.
     408        2138 :         const char *pszType = CPLGetXMLValue(psAttrDef, "type", nullptr);
     409        2138 :         const char *pszElementName = CPLGetXMLValue(psAttrDef, "name", nullptr);
     410             :         bool bNullable =
     411        2138 :             EQUAL(CPLGetXMLValue(psAttrDef, "minOccurs", "1"), "0");
     412             :         const char *pszMaxOccurs =
     413        2138 :             CPLGetXMLValue(psAttrDef, "maxOccurs", nullptr);
     414        2138 :         if (pszType != nullptr)
     415             :         {
     416        1893 :             const char *pszStrippedNSType = StripNS(pszType);
     417        1893 :             int nWidth = 0;
     418        1893 :             int nPrecision = 0;
     419             : 
     420        1893 :             GMLPropertyType gmlType = GMLPT_Untyped;
     421        1893 :             if (EQUAL(pszStrippedNSType, "string") ||
     422        1387 :                 EQUAL(pszStrippedNSType, "Character"))
     423         506 :                 gmlType = GMLPT_String;
     424        1387 :             else if (EQUAL(pszStrippedNSType, "date"))
     425          63 :                 gmlType = GMLPT_Date;
     426        1324 :             else if (EQUAL(pszStrippedNSType, "time"))
     427           2 :                 gmlType = GMLPT_Time;
     428        1322 :             else if (EQUAL(pszStrippedNSType, "dateTime"))
     429         151 :                 gmlType = GMLPT_DateTime;
     430        1171 :             else if (EQUAL(pszStrippedNSType, "real") ||
     431        1171 :                      EQUAL(pszStrippedNSType, "double") ||
     432        1050 :                      EQUAL(pszStrippedNSType, "decimal"))
     433         153 :                 gmlType = GMLPT_Real;
     434        1018 :             else if (EQUAL(pszStrippedNSType, "float"))
     435         112 :                 gmlType = GMLPT_Float;
     436         906 :             else if (EQUAL(pszStrippedNSType, "int") ||
     437         775 :                      EQUAL(pszStrippedNSType, "integer"))
     438         132 :                 gmlType = GMLPT_Integer;
     439         774 :             else if (EQUAL(pszStrippedNSType, "long"))
     440           9 :                 gmlType = GMLPT_Integer64;
     441         765 :             else if (EQUAL(pszStrippedNSType, "unsignedLong"))
     442             :             {
     443             :                 // Optimistically map to signed integer
     444           0 :                 gmlType = GMLPT_Integer64;
     445             :             }
     446         765 :             else if (EQUAL(pszStrippedNSType, "short"))
     447         116 :                 gmlType = GMLPT_Short;
     448         649 :             else if (EQUAL(pszStrippedNSType, "boolean"))
     449         112 :                 gmlType = GMLPT_Boolean;
     450             :             // TODO: Would be nice to have a binary type.
     451         537 :             else if (EQUAL(pszStrippedNSType, "hexBinary"))
     452           0 :                 gmlType = GMLPT_String;
     453         537 :             else if (strcmp(pszType, "gml:FeaturePropertyType") == 0)
     454             :             {
     455           3 :                 gmlType = GMLPT_FeatureProperty;
     456             :             }
     457         534 :             else if (STARTS_WITH(pszType, "gml:"))
     458             :             {
     459         523 :                 const AssocNameType *psIter = apsPropertyTypes;
     460        1622 :                 while (psIter->pszName)
     461             :                 {
     462        1622 :                     if (strncmp(pszType + 4, psIter->pszName,
     463        1622 :                                 strlen(psIter->pszName)) == 0)
     464             :                     {
     465         523 :                         OGRwkbGeometryType eType = psIter->eType;
     466        1046 :                         std::string osSRSName;
     467             : 
     468             :                         // Look if there's a comment restricting to subclasses.
     469         523 :                         for (const CPLXMLNode *psIter2 = psAttrDef->psNext;
     470         927 :                              psIter2 != nullptr; psIter2 = psIter2->psNext)
     471             :                         {
     472         404 :                             if (psIter2->eType == CXT_Comment)
     473             :                             {
     474         102 :                                 if (strstr(psIter2->pszValue,
     475             :                                            "restricted to Polygon"))
     476          17 :                                     eType = wkbPolygon;
     477          85 :                                 else if (strstr(psIter2->pszValue,
     478             :                                                 "restricted to LineString"))
     479          25 :                                     eType = wkbLineString;
     480          60 :                                 else if (strstr(psIter2->pszValue,
     481             :                                                 "restricted to MultiPolygon"))
     482          13 :                                     eType = wkbMultiPolygon;
     483          47 :                                 else if (strstr(
     484          47 :                                              psIter2->pszValue,
     485             :                                              "restricted to MultiLineString"))
     486          11 :                                     eType = wkbMultiLineString;
     487             :                                 else
     488             :                                 {
     489             :                                     const char *pszSRSName =
     490          36 :                                         strstr(psIter2->pszValue, "srsName=\"");
     491          36 :                                     if (pszSRSName)
     492             :                                     {
     493             :                                         osSRSName =
     494          32 :                                             pszSRSName + strlen("srsName=\"");
     495          32 :                                         const auto nPos = osSRSName.find('"');
     496          32 :                                         if (nPos != std::string::npos)
     497          32 :                                             osSRSName.resize(nPos);
     498             :                                         else
     499           0 :                                             osSRSName.clear();
     500             :                                     }
     501             :                                 }
     502             :                             }
     503             :                         }
     504             : 
     505             :                         // Try to get coordinate precision from a construct like:
     506             :                         /*
     507             :                             <xs:element name="wkb_geometry" type="gml:SurfacePropertyType" nillable="true" minOccurs="0" maxOccurs="1">
     508             :                                 <xs:annotation>
     509             :                                   <xs:appinfo source="http://ogr.maptools.org/">
     510             :                                     <ogr:xy_coordinate_resolution>8.9e-9</ogr:xy_coordinate_resolution>
     511             :                                     <ogr:z_coordinate_resolution>1e-3</ogr:z_coordinate_resolution>
     512             :                                     <ogr:m_coordinate_resolution>1e-3</ogr:m_coordinate_resolution>
     513             :                                   </xs:appinfo>
     514             :                                 </xs:annotation>
     515             :                             </xs:element>
     516             :                         */
     517        1046 :                         OGRGeomCoordinatePrecision oGeomCoordPrec;
     518             :                         const auto psAnnotation =
     519         523 :                             CPLGetXMLNode(psAttrDef, "annotation");
     520         523 :                         if (psAnnotation)
     521             :                         {
     522           1 :                             for (const CPLXMLNode *psIterAppinfo =
     523             :                                      psAnnotation->psChild;
     524           2 :                                  psIterAppinfo;
     525           1 :                                  psIterAppinfo = psIterAppinfo->psNext)
     526             :                             {
     527           3 :                                 if (psIterAppinfo->eType == CXT_Element &&
     528           1 :                                     strcmp(psIterAppinfo->pszValue,
     529           2 :                                            "appinfo") == 0 &&
     530           1 :                                     strcmp(CPLGetXMLValue(psIterAppinfo,
     531             :                                                           "source", ""),
     532             :                                            "http://ogr.maptools.org/") == 0)
     533             :                                 {
     534           1 :                                     if (const char *pszXYRes = CPLGetXMLValue(
     535             :                                             psIterAppinfo,
     536             :                                             "xy_coordinate_resolution",
     537             :                                             nullptr))
     538             :                                     {
     539           1 :                                         const double dfVal = CPLAtof(pszXYRes);
     540           1 :                                         if (dfVal > 0 && std::isfinite(dfVal))
     541           1 :                                             oGeomCoordPrec.dfXYResolution =
     542             :                                                 dfVal;
     543             :                                     }
     544           1 :                                     if (const char *pszZRes = CPLGetXMLValue(
     545             :                                             psIterAppinfo,
     546             :                                             "z_coordinate_resolution", nullptr))
     547             :                                     {
     548           1 :                                         const double dfVal = CPLAtof(pszZRes);
     549           1 :                                         if (dfVal > 0 && std::isfinite(dfVal))
     550           1 :                                             oGeomCoordPrec.dfZResolution =
     551             :                                                 dfVal;
     552             :                                     }
     553             :                                 }
     554             :                             }
     555             :                         }
     556             : 
     557             :                         GMLGeometryPropertyDefn *poDefn =
     558             :                             new GMLGeometryPropertyDefn(
     559             :                                 pszElementName, pszElementName, eType,
     560         523 :                                 nAttributeIndex, bNullable, oGeomCoordPrec);
     561         523 :                         poDefn->SetSRSName(osSRSName);
     562             : 
     563         523 :                         if (poClass->AddGeometryProperty(poDefn) < 0)
     564           0 :                             delete poDefn;
     565             :                         else
     566         523 :                             nAttributeIndex++;
     567             : 
     568         523 :                         break;
     569             :                     }
     570             : 
     571        1099 :                     psIter++;
     572             :                 }
     573             : 
     574         523 :                 if (psIter->pszName == nullptr)
     575             :                 {
     576             :                     // Can be a non geometry gml type.
     577             :                     // Too complex schema for us. Aborts parsing.
     578           0 :                     delete poClass;
     579           9 :                     return nullptr;
     580             :                 }
     581             : 
     582         523 :                 if (poClass->GetGeometryPropertyCount() == 0)
     583           0 :                     bGotUnrecognizedType = true;
     584             : 
     585        1884 :                 continue;
     586             :             }
     587             : 
     588             :             /* Integraph stuff */
     589          11 :             else if (strcmp(pszType, "G:Point_MultiPointPropertyType") == 0 ||
     590          11 :                      strcmp(pszType, "gmgml:Point_MultiPointPropertyType") == 0)
     591             :             {
     592             :                 GMLGeometryPropertyDefn *poDefn = new GMLGeometryPropertyDefn(
     593             :                     pszElementName, pszElementName, wkbMultiPoint,
     594           0 :                     nAttributeIndex, bNullable);
     595             : 
     596           0 :                 if (poClass->AddGeometryProperty(poDefn) < 0)
     597           0 :                     delete poDefn;
     598             :                 else
     599           0 :                     nAttributeIndex++;
     600             : 
     601           0 :                 continue;
     602             :             }
     603          11 :             else if (strcmp(pszType,
     604          11 :                             "G:LineString_MultiLineStringPropertyType") == 0 ||
     605          11 :                      strcmp(pszType,
     606             :                             "gmgml:LineString_MultiLineStringPropertyType") ==
     607             :                          0)
     608             :             {
     609             :                 GMLGeometryPropertyDefn *poDefn = new GMLGeometryPropertyDefn(
     610             :                     pszElementName, pszElementName, wkbMultiLineString,
     611           0 :                     nAttributeIndex, bNullable);
     612             : 
     613           0 :                 if (poClass->AddGeometryProperty(poDefn) < 0)
     614           0 :                     delete poDefn;
     615             :                 else
     616           0 :                     nAttributeIndex++;
     617             : 
     618           0 :                 continue;
     619             :             }
     620          11 :             else if (strcmp(pszType, "G:Polygon_MultiPolygonPropertyType") ==
     621          11 :                          0 ||
     622          11 :                      strcmp(pszType,
     623          11 :                             "gmgml:Polygon_MultiPolygonPropertyType") == 0 ||
     624          11 :                      strcmp(pszType, "gmgml:Polygon_Surface_MultiSurface_"
     625             :                                      "CompositeSurfacePropertyType") == 0)
     626             :             {
     627             :                 GMLGeometryPropertyDefn *poDefn = new GMLGeometryPropertyDefn(
     628             :                     pszElementName, pszElementName, wkbMultiPolygon,
     629           0 :                     nAttributeIndex, bNullable);
     630             : 
     631           0 :                 if (poClass->AddGeometryProperty(poDefn) < 0)
     632           0 :                     delete poDefn;
     633             :                 else
     634           0 :                     nAttributeIndex++;
     635             : 
     636           0 :                 continue;
     637             :             }
     638             : 
     639             :             // ERDAS Apollo stufflike in
     640             :             // http://apollo.erdas.com/erdas-apollo/vector/WORLDWIDE?SERVICE=WFS&VERSION=1.0.0&REQUEST=DescribeFeatureType&TYPENAME=wfs:cntry98)
     641          11 :             else if (strcmp(pszType, "wfs:MixedPolygonPropertyType") == 0)
     642             :             {
     643             :                 GMLGeometryPropertyDefn *poDefn = new GMLGeometryPropertyDefn(
     644             :                     pszElementName, pszElementName, wkbMultiPolygon,
     645           0 :                     nAttributeIndex, bNullable);
     646             : 
     647           0 :                 if (poClass->AddGeometryProperty(poDefn) < 0)
     648           0 :                     delete poDefn;
     649             :                 else
     650           0 :                     nAttributeIndex++;
     651             : 
     652           0 :                 continue;
     653             :             }
     654             : 
     655             :             else
     656             :             {
     657          11 :                 gmlType = GMLPT_Untyped;
     658          11 :                 if (!LookForSimpleType(psSchemaNode, pszStrippedNSType,
     659             :                                        &gmlType, &nWidth, &nPrecision))
     660             :                 {
     661             :                     // Too complex schema for us. Aborts parsing.
     662           9 :                     delete poClass;
     663           9 :                     return nullptr;
     664             :                 }
     665             :             }
     666             : 
     667        1361 :             if (pszElementName == nullptr)
     668           0 :                 pszElementName = "unnamed";
     669        1361 :             const char *pszPropertyName = pszElementName;
     670        1361 :             if (gmlType == GMLPT_FeatureProperty)
     671             :             {
     672           3 :                 pszPropertyName = CPLSPrintf("%s_href", pszElementName);
     673             :             }
     674             :             GMLPropertyDefn *poProp =
     675        1361 :                 new GMLPropertyDefn(pszPropertyName, pszElementName);
     676             : 
     677        1361 :             if (pszMaxOccurs != nullptr && strcmp(pszMaxOccurs, "1") != 0)
     678           2 :                 gmlType = GetListTypeFromSingleType(gmlType);
     679             : 
     680        1361 :             poProp->SetType(gmlType);
     681        1361 :             poProp->SetWidth(nWidth);
     682        1361 :             poProp->SetPrecision(nPrecision);
     683        1361 :             poProp->SetNullable(bNullable);
     684             : 
     685             :             const CPLXMLNode *psAnnotation =
     686        1361 :                 CPLGetXMLNode(psAttrDef, "annotation");
     687        1361 :             if (psAnnotation)
     688             :             {
     689             :                 const char *pszDocumentation =
     690           3 :                     CPLGetXMLValue(psAnnotation, "documentation", nullptr);
     691           3 :                 if (pszDocumentation)
     692           3 :                     poProp->SetDocumentation(pszDocumentation);
     693             :             }
     694             : 
     695        1361 :             if (poClass->AddProperty(poProp) < 0)
     696           0 :                 delete poProp;
     697             :             else
     698        1361 :                 nAttributeIndex++;
     699             : 
     700        1361 :             continue;
     701             :         }
     702             : 
     703             :         // For now we skip geometries.  Fixup later.
     704         245 :         CPLXMLNode *psSimpleType = CPLGetXMLNode(psAttrDef, "simpleType");
     705         245 :         if (psSimpleType == nullptr)
     706             :         {
     707          19 :             const char *pszRef = CPLGetXMLValue(psAttrDef, "ref", nullptr);
     708             : 
     709             :             // FME .xsd
     710          19 :             if (pszRef != nullptr && STARTS_WITH(pszRef, "gml:"))
     711             :             {
     712          18 :                 const AssocNameType *psIter = apsRefTypes;
     713          63 :                 while (psIter->pszName)
     714             :                 {
     715          63 :                     if (strncmp(pszRef + 4, psIter->pszName,
     716          63 :                                 strlen(psIter->pszName)) == 0)
     717             :                     {
     718          18 :                         if (poClass->GetGeometryPropertyCount() > 0)
     719             :                         {
     720           9 :                             OGRwkbGeometryType eNewType = psIter->eType;
     721             :                             OGRwkbGeometryType eOldType =
     722             :                                 static_cast<OGRwkbGeometryType>(
     723           9 :                                     poClass->GetGeometryProperty(0)->GetType());
     724             : 
     725           9 :                             if ((eNewType == wkbMultiPoint &&
     726           6 :                                  eOldType == wkbPoint) ||
     727           3 :                                 (eNewType == wkbMultiLineString &&
     728           3 :                                  eOldType == wkbLineString) ||
     729           3 :                                 (eNewType == wkbMultiPolygon &&
     730             :                                  eOldType == wkbPolygon))
     731             :                             {
     732           9 :                                 poClass->GetGeometryProperty(0)->SetType(
     733             :                                     eNewType);
     734             :                             }
     735             :                             else
     736             :                             {
     737           0 :                                 CPLDebug("GML",
     738             :                                          "Geometry field already found ! "
     739             :                                          "Ignoring the following ones");
     740             :                             }
     741             :                         }
     742             :                         else
     743             :                         {
     744             :                             GMLGeometryPropertyDefn *poDefn =
     745             :                                 new GMLGeometryPropertyDefn(
     746             :                                     pszElementName, pszElementName,
     747           9 :                                     psIter->eType, nAttributeIndex, true);
     748             : 
     749           9 :                             if (poClass->AddGeometryProperty(poDefn) < 0)
     750           0 :                                 delete poDefn;
     751             :                             else
     752           9 :                                 nAttributeIndex++;
     753             :                         }
     754             : 
     755          18 :                         break;
     756             :                     }
     757             : 
     758          45 :                     psIter++;
     759             :                 }
     760             : 
     761          18 :                 if (psIter->pszName == nullptr)
     762             :                 {
     763             :                     // Can be a non geometry gml type .
     764             :                     // Too complex schema for us. Aborts parsing.
     765           0 :                     delete poClass;
     766           0 :                     return nullptr;
     767             :                 }
     768             : 
     769          18 :                 if (poClass->GetGeometryPropertyCount() == 0)
     770           0 :                     bGotUnrecognizedType = true;
     771             : 
     772          18 :                 continue;
     773             :             }
     774             : 
     775             :             /* Parse stuff like the following found in
     776             :             http://199.29.1.81:8181/miwfs/GetFeature.ashx?REQUEST=GetFeature&MAXFEATURES=1&SERVICE=WFS&VERSION=1.0.0&TYPENAME=miwfs:World
     777             :             : <xs:element name="Obj" minOccurs="0" maxOccurs="1">
     778             :                 <xs:complexType>
     779             :                     <xs:sequence>
     780             :                         <xs:element ref="gml:_Geometry"/>
     781             :                     </xs:sequence>
     782             :                 </xs:complexType>
     783             :             </xs:element>
     784             :             */
     785             :             CPLXMLNode *l_psComplexType =
     786           1 :                 GetSingleChildElement(psAttrDef, "complexType");
     787             :             CPLXMLNode *psComplexTypeSequence =
     788           1 :                 GetSingleChildElement(l_psComplexType, "sequence");
     789             :             CPLXMLNode *psComplexTypeSequenceElement =
     790           1 :                 GetSingleChildElement(psComplexTypeSequence, "element");
     791             : 
     792           2 :             if (pszElementName != nullptr &&
     793           1 :                 CheckMinMaxOccursCardinality(psAttrDef) &&
     794           1 :                 psComplexTypeSequenceElement != nullptr &&
     795           3 :                 CheckMinMaxOccursCardinality(psComplexTypeSequence) &&
     796           1 :                 strcmp(CPLGetXMLValue(psComplexTypeSequenceElement, "ref", ""),
     797             :                        "gml:_Geometry") == 0)
     798             :             {
     799             :                 GMLGeometryPropertyDefn *poDefn = new GMLGeometryPropertyDefn(
     800             :                     pszElementName, pszElementName, wkbUnknown, nAttributeIndex,
     801           1 :                     bNullable);
     802             : 
     803           1 :                 if (poClass->AddGeometryProperty(poDefn) < 0)
     804           0 :                     delete poDefn;
     805             :                 else
     806           1 :                     nAttributeIndex++;
     807             : 
     808           1 :                 continue;
     809             :             }
     810             :             else
     811             :             {
     812             :                 // Too complex schema for us. Aborts parsing.
     813           0 :                 delete poClass;
     814           0 :                 return nullptr;
     815             :             }
     816             :         }
     817             : 
     818         226 :         if (pszElementName == nullptr)
     819           0 :             pszElementName = "unnamed";
     820             :         GMLPropertyDefn *poProp =
     821         226 :             new GMLPropertyDefn(pszElementName, pszElementName);
     822             : 
     823         226 :         GMLPropertyType eType = GMLPT_Untyped;
     824         226 :         int nWidth = 0;
     825         226 :         int nPrecision = 0;
     826         226 :         GetSimpleTypeProperties(psSimpleType, &eType, &nWidth, &nPrecision);
     827             : 
     828         226 :         if (pszMaxOccurs != nullptr && strcmp(pszMaxOccurs, "1") != 0)
     829           9 :             eType = GetListTypeFromSingleType(eType);
     830             : 
     831         226 :         poProp->SetType(eType);
     832         226 :         poProp->SetWidth(nWidth);
     833         226 :         poProp->SetPrecision(nPrecision);
     834         226 :         poProp->SetNullable(bNullable);
     835             : 
     836         226 :         const CPLXMLNode *psAnnotation = CPLGetXMLNode(psAttrDef, "annotation");
     837         226 :         if (psAnnotation)
     838             :         {
     839             :             const char *pszDocumentation =
     840           8 :                 CPLGetXMLValue(psAnnotation, "documentation", nullptr);
     841           8 :             if (pszDocumentation)
     842           8 :                 poProp->SetDocumentation(pszDocumentation);
     843             :         }
     844             : 
     845         226 :         if (poClass->AddProperty(poProp) < 0)
     846           0 :             delete poProp;
     847             :         else
     848         226 :             nAttributeIndex++;
     849             :     }
     850             : 
     851             :     // If we have found an unknown types, let's be on the side of caution and
     852             :     // create a geometry field.
     853         561 :     if (poClass->GetGeometryPropertyCount() == 0 && bGotUnrecognizedType)
     854             :     {
     855           0 :         poClass->AddGeometryProperty(
     856           0 :             new GMLGeometryPropertyDefn("", "", wkbUnknown, -1, true));
     857             :     }
     858             : 
     859             :     /* -------------------------------------------------------------------- */
     860             :     /*      Class complete, add to reader class list.                       */
     861             :     /* -------------------------------------------------------------------- */
     862         561 :     poClass->SetSchemaLocked(true);
     863             : 
     864         561 :     return poClass;
     865             : }
     866             : 
     867             : /************************************************************************/
     868             : /*                         ExcludeBaseGMLSchemas()                            */
     869             : /************************************************************************/
     870             : 
     871          12 : static bool ExcludeBaseGMLSchemas(const char *pszFilename)
     872             : {
     873             :     // List of substrings to exclude
     874             :     const std::vector<std::string> excludedBaseGMLReferencedSchemasList = {
     875          96 :         "/gml/3.2.1/", "gml/3.1.1/", "/gml/2.1.2/", "/gmlsfProfile/"};
     876          12 :     if (pszFilename != nullptr)
     877             :     {
     878          12 :         const std::string osFilename(pszFilename);
     879          28 :         for (const auto &pattern : excludedBaseGMLReferencedSchemasList)
     880             :         {
     881          24 :             if (osFilename.find(pattern) != std::string::npos)
     882             :             {
     883           8 :                 return false;  // Found one of the excluded base GML referenced schema
     884             :             }
     885             :         }
     886             :     }
     887           4 :     return true;  // None of the base GML referenced schemas were found
     888             : }
     889             : 
     890             : /************************************************************************/
     891             : /*                         GMLParseXMLFile()                            */
     892             : /************************************************************************/
     893             : 
     894         445 : static CPLXMLNode *GMLParseXMLFile(const char *pszFilename)
     895             : {
     896         445 :     if (STARTS_WITH(pszFilename, "http://") ||
     897         444 :         STARTS_WITH(pszFilename, "https://"))
     898             :     {
     899           1 :         CPLXMLNode *psRet = nullptr;
     900           1 :         CPLHTTPResult *psResult = CPLHTTPFetch(pszFilename, nullptr);
     901           1 :         if (psResult != nullptr)
     902             :         {
     903           1 :             if (psResult->pabyData != nullptr)
     904             :             {
     905           1 :                 psRet = CPLParseXMLString(
     906           1 :                     reinterpret_cast<const char *>(psResult->pabyData));
     907             :             }
     908           1 :             CPLHTTPDestroyResult(psResult);
     909             :         }
     910           1 :         return psRet;
     911             :     }
     912             :     else
     913             :     {
     914         444 :         return CPLParseXMLFile(pszFilename);
     915             :     }
     916             : }
     917             : 
     918             : /************************************************************************/
     919             : /*                       CPLGetFirstChildNode()                         */
     920             : /************************************************************************/
     921             : 
     922          12 : static CPLXMLNode *CPLGetFirstChildNode(CPLXMLNode *psNode)
     923             : {
     924          12 :     if (psNode == nullptr)
     925           0 :         return nullptr;
     926          12 :     CPLXMLNode *psIter = psNode->psChild;
     927          94 :     while (psIter != nullptr)
     928             :     {
     929          94 :         if (psIter->eType == CXT_Element)
     930          12 :             return psIter;
     931          82 :         psIter = psIter->psNext;
     932             :     }
     933           0 :     return nullptr;
     934             : }
     935             : 
     936             : /************************************************************************/
     937             : /*                          CPLGetLastNode()                            */
     938             : /************************************************************************/
     939             : 
     940          12 : static CPLXMLNode *CPLGetLastNode(CPLXMLNode *psNode)
     941             : {
     942          12 :     CPLXMLNode *psIter = psNode;
     943          23 :     while (psIter->psNext != nullptr)
     944          11 :         psIter = psIter->psNext;
     945          12 :     return psIter;
     946             : }
     947             : 
     948             : /************************************************************************/
     949             : /*                       CPLXMLSchemaResolveInclude()                   */
     950             : /************************************************************************/
     951             : 
     952         433 : static void CPLXMLSchemaResolveInclude(const char *pszMainSchemaLocation,
     953             :                                        CPLXMLNode *psSchemaNode,
     954             :                                        bool bUseSchemaImports)
     955             : {
     956         866 :     std::set<CPLString> osAlreadyIncluded;
     957             : 
     958             :     bool bTryAgain;
     959         441 :     do
     960             :     {
     961         441 :         CPLXMLNode *psLast = nullptr;
     962         441 :         bTryAgain = false;
     963             : 
     964         441 :         CPLXMLNode *psThis = psSchemaNode->psChild;
     965        4727 :         for (; psThis != nullptr; psThis = psThis->psNext)
     966             :         {
     967             :             const char *pszSchemaLocation =
     968        4286 :                 CPLGetXMLValue(psThis, "schemaLocation", nullptr);
     969             : 
     970        6398 :             if (psThis->eType == CXT_Element &&
     971        2112 :                 (EQUAL(psThis->pszValue, "include") ||
     972        2103 :                  (bUseSchemaImports == TRUE &&
     973          20 :                   EQUAL(psThis->pszValue, "import") &&
     974          12 :                   ExcludeBaseGMLSchemas(pszSchemaLocation))))
     975             :             {
     976             : 
     977          26 :                 if (pszSchemaLocation != nullptr &&
     978          26 :                     osAlreadyIncluded.count(pszSchemaLocation) == 0)
     979             :                 {
     980          12 :                     osAlreadyIncluded.insert(pszSchemaLocation);
     981             : 
     982          36 :                     if (!STARTS_WITH(pszSchemaLocation, "http://") &&
     983          24 :                         !STARTS_WITH(pszSchemaLocation, "https://") &&
     984          12 :                         CPLIsFilenameRelative(pszSchemaLocation))
     985             :                     {
     986             :                         pszSchemaLocation =
     987          12 :                             CPLFormFilename(CPLGetPath(pszMainSchemaLocation),
     988             :                                             pszSchemaLocation, nullptr);
     989             :                     }
     990             : 
     991             :                     CPLXMLNode *psIncludedXSDTree =
     992          12 :                         GMLParseXMLFile(pszSchemaLocation);
     993          12 :                     if (psIncludedXSDTree != nullptr)
     994             :                     {
     995          12 :                         CPLStripXMLNamespace(psIncludedXSDTree, nullptr, TRUE);
     996             :                         CPLXMLNode *psIncludedSchemaNode =
     997          12 :                             CPLGetXMLNode(psIncludedXSDTree, "=schema");
     998          12 :                         if (psIncludedSchemaNode != nullptr)
     999             :                         {
    1000             :                             // Substitute de <include> node by its content.
    1001             :                             CPLXMLNode *psFirstChildElement =
    1002          12 :                                 CPLGetFirstChildNode(psIncludedSchemaNode);
    1003          12 :                             if (psFirstChildElement != nullptr)
    1004             :                             {
    1005             :                                 CPLXMLNode *psCopy =
    1006          12 :                                     CPLCloneXMLTree(psFirstChildElement);
    1007          12 :                                 if (psLast != nullptr)
    1008          12 :                                     psLast->psNext = psCopy;
    1009             :                                 else
    1010           0 :                                     psSchemaNode->psChild = psCopy;
    1011          12 :                                 CPLXMLNode *psNext = psThis->psNext;
    1012          12 :                                 psThis->psNext = nullptr;
    1013          12 :                                 CPLDestroyXMLNode(psThis);
    1014          12 :                                 psThis = CPLGetLastNode(psCopy);
    1015          12 :                                 psThis->psNext = psNext;
    1016             : 
    1017             :                                 // In case the included schema also contains
    1018             :                                 // includes.
    1019          12 :                                 bTryAgain = true;
    1020             :                             }
    1021             :                         }
    1022          12 :                         CPLDestroyXMLNode(psIncludedXSDTree);
    1023             :                     }
    1024             :                 }
    1025             :             }
    1026             : 
    1027        4286 :             psLast = psThis;
    1028             :         }
    1029             :     } while (bTryAgain);
    1030             : 
    1031             :     const char *pszSchemaOutputName =
    1032         433 :         CPLGetConfigOption("GML_SCHEMA_OUTPUT_NAME", nullptr);
    1033         433 :     if (pszSchemaOutputName != nullptr)
    1034             :     {
    1035           0 :         CPLSerializeXMLTreeToFile(psSchemaNode, pszSchemaOutputName);
    1036             :     }
    1037         433 : }
    1038             : 
    1039             : /************************************************************************/
    1040             : /*                       GetUniqueConstraints()                         */
    1041             : /************************************************************************/
    1042             : 
    1043             : static std::set<std::pair<std::string, std::string>>
    1044         120 : GetUniqueConstraints(const CPLXMLNode *psNode)
    1045             : {
    1046             :     /* Parse
    1047             :         <xs:unique name="uniqueConstraintpolyeas_id">
    1048             :             <xs:selector xpath="ogr:featureMember/ogr:poly"/>
    1049             :             <xs:field xpath="ogr:eas_id"/>
    1050             :         </xs:unique>
    1051             :     */
    1052         120 :     std::set<std::pair<std::string, std::string>> oSet;
    1053         480 :     for (const auto *psIter = psNode->psChild; psIter != nullptr;
    1054         360 :          psIter = psIter->psNext)
    1055             :     {
    1056         360 :         if (psIter->eType == CXT_Element && EQUAL(psIter->pszValue, "unique"))
    1057             :         {
    1058             :             const char *pszSelector =
    1059           0 :                 CPLGetXMLValue(psIter, "selector.xpath", nullptr);
    1060             :             const char *pszField =
    1061           0 :                 CPLGetXMLValue(psIter, "field.xpath", nullptr);
    1062           0 :             if (pszSelector && pszField && pszField[0] != '@')
    1063             :             {
    1064           0 :                 const char *pszSlash = strchr(pszSelector, '/');
    1065           0 :                 if (pszSlash)
    1066             :                 {
    1067             :                     oSet.insert(
    1068           0 :                         std::pair(StripNS(pszSlash + 1), StripNS(pszField)));
    1069             :                 }
    1070             :             }
    1071             :         }
    1072             :     }
    1073         120 :     return oSet;
    1074             : }
    1075             : 
    1076             : /************************************************************************/
    1077             : /*                          GMLParseXSD()                               */
    1078             : /************************************************************************/
    1079             : 
    1080         433 : bool GMLParseXSD(const char *pszFile, bool bUseSchemaImports,
    1081             :                  std::vector<GMLFeatureClass *> &aosClasses,
    1082             :                  bool &bFullyUnderstood)
    1083             : 
    1084             : {
    1085         433 :     bFullyUnderstood = false;
    1086             : 
    1087         433 :     if (pszFile == nullptr)
    1088           0 :         return false;
    1089             : 
    1090             :     /* -------------------------------------------------------------------- */
    1091             :     /*      Load the raw XML file.                                          */
    1092             :     /* -------------------------------------------------------------------- */
    1093         433 :     CPLXMLNode *psXSDTree = GMLParseXMLFile(pszFile);
    1094             : 
    1095         433 :     if (psXSDTree == nullptr)
    1096           0 :         return false;
    1097             : 
    1098             :     /* -------------------------------------------------------------------- */
    1099             :     /*      Strip off any namespace qualifiers.                             */
    1100             :     /* -------------------------------------------------------------------- */
    1101         433 :     CPLStripXMLNamespace(psXSDTree, nullptr, TRUE);
    1102             : 
    1103             :     /* -------------------------------------------------------------------- */
    1104             :     /*      Find <schema> root element.                                     */
    1105             :     /* -------------------------------------------------------------------- */
    1106         433 :     CPLXMLNode *psSchemaNode = CPLGetXMLNode(psXSDTree, "=schema");
    1107         433 :     if (psSchemaNode == nullptr)
    1108             :     {
    1109           0 :         CPLDestroyXMLNode(psXSDTree);
    1110           0 :         return false;
    1111             :     }
    1112             : 
    1113             :     /* ==================================================================== */
    1114             :     /*      Process each include directive.                                 */
    1115             :     /* ==================================================================== */
    1116         433 :     CPLXMLSchemaResolveInclude(pszFile, psSchemaNode, bUseSchemaImports);
    1117             : 
    1118             :     // CPLSerializeXMLTreeToFile(psSchemaNode, "/vsistdout/");
    1119             : 
    1120         433 :     bFullyUnderstood = true;
    1121             : 
    1122             :     /* ==================================================================== */
    1123             :     /*      Process each feature class definition.                          */
    1124             :     /* ==================================================================== */
    1125         433 :     CPLXMLNode *psThis = psSchemaNode->psChild;
    1126             : 
    1127         433 :     std::set<std::pair<std::string, std::string>> oSetUniqueConstraints;
    1128             : 
    1129        4659 :     for (; psThis != nullptr; psThis = psThis->psNext)
    1130             :     {
    1131             :         /* --------------------------------------------------------------------
    1132             :          */
    1133             :         /*      Check for <xs:element> node. */
    1134             :         /* --------------------------------------------------------------------
    1135             :          */
    1136        4226 :         if (psThis->eType != CXT_Element || !EQUAL(psThis->pszValue, "element"))
    1137        3670 :             continue;
    1138             : 
    1139             :         /* --------------------------------------------------------------------
    1140             :          */
    1141             :         /*      Get name */
    1142             :         /* --------------------------------------------------------------------
    1143             :          */
    1144         696 :         const char *pszName = CPLGetXMLValue(psThis, "name", nullptr);
    1145         696 :         if (pszName == nullptr)
    1146             :         {
    1147           0 :             continue;
    1148             :         }
    1149             : 
    1150             :         /* --------------------------------------------------------------------
    1151             :          */
    1152             :         /*      Check the substitution group. */
    1153             :         /* --------------------------------------------------------------------
    1154             :          */
    1155             :         const char *pszSubGroup =
    1156         696 :             StripNS(CPLGetXMLValue(psThis, "substitutionGroup", ""));
    1157             : 
    1158         696 :         if (EQUAL(pszName, "FeatureCollection") &&
    1159         120 :             (EQUAL(pszSubGroup, "_FeatureCollection") ||
    1160         104 :              EQUAL(pszSubGroup, "_GML") ||
    1161          94 :              EQUAL(pszSubGroup, "AbstractFeature")))
    1162             :         {
    1163         120 :             oSetUniqueConstraints = GetUniqueConstraints(psThis);
    1164         120 :             continue;
    1165             :         }
    1166             : 
    1167             :         // AbstractFeature used by GML 3.2.
    1168         576 :         if (!EQUAL(pszSubGroup, "_Feature") &&
    1169         138 :             !EQUAL(pszSubGroup, "AbstractFeature"))
    1170             :         {
    1171           0 :             continue;
    1172             :         }
    1173             : 
    1174             :         /* --------------------------------------------------------------------
    1175             :          */
    1176             :         /*      Get type and verify relationship with name. */
    1177             :         /* --------------------------------------------------------------------
    1178             :          */
    1179         576 :         const char *pszType = CPLGetXMLValue(psThis, "type", nullptr);
    1180         576 :         if (pszType == nullptr)
    1181             :         {
    1182          14 :             CPLXMLNode *psComplexType = CPLGetXMLNode(psThis, "complexType");
    1183          14 :             if (psComplexType)
    1184             :             {
    1185             :                 GMLFeatureClass *poClass =
    1186          14 :                     GMLParseFeatureType(psSchemaNode, pszName, psComplexType);
    1187          14 :                 if (poClass)
    1188          13 :                     aosClasses.push_back(poClass);
    1189             :                 else
    1190           1 :                     bFullyUnderstood = false;
    1191             :             }
    1192          14 :             continue;
    1193             :         }
    1194         562 :         if (strstr(pszType, ":") != nullptr)
    1195         554 :             pszType = strstr(pszType, ":") + 1;
    1196         562 :         if (EQUAL(pszType, pszName))
    1197             :         {
    1198             :             // A few WFS servers return a type name which is the element name
    1199             :             // without any _Type or Type suffix
    1200             :             // e.g.:
    1201             :             // http://apollo.erdas.com/erdas-apollo/vector/Cherokee?SERVICE=WFS&VERSION=1.0.0&REQUEST=DescribeFeatureType&TYPENAME=iwfs:Air
    1202             :             // */
    1203             : 
    1204             :             // TODO(schwehr): What was supposed to go here?
    1205             :         }
    1206             : 
    1207             :         // <element name="RekisteriyksikonPalstanTietoja"
    1208             :         // type="ktjkiiwfs:PalstanTietojaType" substitutionGroup="gml:_Feature"
    1209             :         // />
    1210         562 :         else if (strlen(pszType) > 4 &&
    1211         562 :                  strcmp(pszType + strlen(pszType) - 4, "Type") == 0 &&
    1212         562 :                  strlen(pszName) > strlen(pszType) - 4 &&
    1213           0 :                  strncmp(pszName + strlen(pszName) - (strlen(pszType) - 4),
    1214           0 :                          pszType, strlen(pszType) - 4) == 0)
    1215             :         {
    1216             :         }
    1217             : 
    1218         562 :         else if (!EQUALN(pszType, pszName, strlen(pszName)) ||
    1219         562 :                  !(EQUAL(pszType + strlen(pszName), "_Type") ||
    1220         404 :                    EQUAL(pszType + strlen(pszName), "Type") ||
    1221           4 :                    EQUAL(pszType + strlen(pszName), "FeatureType")))
    1222             :         {
    1223           3 :             continue;
    1224             :         }
    1225             : 
    1226             :         // CanVec .xsd contains weird types that are not used in the related
    1227             :         // GML.
    1228         559 :         if (STARTS_WITH(pszName, "XyZz") || STARTS_WITH(pszName, "XyZ1") ||
    1229         556 :             STARTS_WITH(pszName, "XyZ2"))
    1230           3 :             continue;
    1231             : 
    1232             :         GMLFeatureClass *poClass =
    1233         556 :             GMLParseFeatureType(psSchemaNode, pszName, pszType);
    1234         556 :         if (poClass)
    1235         548 :             aosClasses.push_back(poClass);
    1236             :         else
    1237           8 :             bFullyUnderstood = false;
    1238             :     }
    1239             : 
    1240         433 :     CPLDestroyXMLNode(psXSDTree);
    1241             : 
    1242             :     // Attach unique constraints to fields
    1243         433 :     for (const auto &typeFieldPair : oSetUniqueConstraints)
    1244             :     {
    1245           0 :         for (const auto *poClass : aosClasses)
    1246             :         {
    1247           0 :             if (poClass->GetName() == typeFieldPair.first)
    1248             :             {
    1249             :                 auto poProperty =
    1250           0 :                     poClass->GetProperty(typeFieldPair.second.c_str());
    1251           0 :                 if (poProperty)
    1252             :                 {
    1253           0 :                     poProperty->SetUnique(true);
    1254             :                 }
    1255           0 :                 break;
    1256             :             }
    1257             :         }
    1258             :     }
    1259             : 
    1260         433 :     return !aosClasses.empty();
    1261             : }

Generated by: LCOV version 1.14