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 : }
|