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

Generated by: LCOV version 1.14