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 2999 : static const char *StripNS(const char *pszFullValue)
39 :
40 : {
41 2999 : const char *pszColon = strstr(pszFullValue, ":");
42 2999 : if (pszColon == nullptr)
43 219 : return pszFullValue;
44 : else
45 2780 : return pszColon + 1;
46 : }
47 :
48 : /************************************************************************/
49 : /* GetSimpleTypeProperties() */
50 : /************************************************************************/
51 :
52 300 : static bool GetSimpleTypeProperties(CPLXMLNode *psTypeNode,
53 : GMLPropertyType *pGMLType, int *pnWidth,
54 : int *pnPrecision)
55 : {
56 : const char *pszBase =
57 300 : StripNS(CPLGetXMLValue(psTypeNode, "restriction.base", ""));
58 :
59 300 : if (EQUAL(pszBase, "decimal"))
60 : {
61 91 : *pGMLType = GMLPT_Real;
62 : const char *pszWidth =
63 91 : CPLGetXMLValue(psTypeNode, "restriction.totalDigits.value", "0");
64 : const char *pszPrecision =
65 91 : CPLGetXMLValue(psTypeNode, "restriction.fractionDigits.value", "0");
66 91 : *pnWidth = atoi(pszWidth);
67 91 : *pnPrecision = atoi(pszPrecision);
68 91 : return true;
69 : }
70 :
71 209 : else if (EQUAL(pszBase, "float"))
72 : {
73 1 : *pGMLType = GMLPT_Float;
74 1 : return true;
75 : }
76 :
77 208 : else if (EQUAL(pszBase, "double"))
78 : {
79 0 : *pGMLType = GMLPT_Real;
80 0 : return true;
81 : }
82 :
83 208 : else if (EQUAL(pszBase, "integer"))
84 : {
85 71 : *pGMLType = GMLPT_Integer;
86 : const char *pszWidth =
87 71 : CPLGetXMLValue(psTypeNode, "restriction.totalDigits.value", "0");
88 71 : *pnWidth = atoi(pszWidth);
89 71 : return true;
90 : }
91 :
92 137 : 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 125 : 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 125 : else if (EQUAL(pszBase, "string"))
112 : {
113 121 : *pGMLType = GMLPT_String;
114 : const char *pszWidth =
115 121 : CPLGetXMLValue(psTypeNode, "restriction.maxLength.value", "0");
116 121 : *pnWidth = atoi(pszWidth);
117 121 : 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") || EQUAL(pszBase, "TimeInstantType"))
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 584 : static GMLFeatureClass *GMLParseFeatureType(CPLXMLNode *psSchemaNode,
289 : const char *pszName,
290 : const char *pszType)
291 : {
292 584 : CPLXMLNode *psThis = psSchemaNode->psChild;
293 5226 : for (; psThis != nullptr; psThis = psThis->psNext)
294 : {
295 12969 : if (psThis->eType == CXT_Element &&
296 6201 : EQUAL(psThis->pszValue, "complexType") &&
297 975 : EQUAL(CPLGetXMLValue(psThis, "name", ""), pszType))
298 : {
299 584 : break;
300 : }
301 : }
302 584 : if (psThis == nullptr)
303 0 : return nullptr;
304 :
305 584 : return GMLParseFeatureType(psSchemaNode, pszName, psThis);
306 : }
307 :
308 598 : 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 598 : CPLGetXMLNode(psComplexType, "complexContent.extension.sequence");
318 :
319 598 : 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 598 : GMLFeatureClass *poClass = new GMLFeatureClass(pszName);
329 :
330 : /* -------------------------------------------------------------------- */
331 : /* Loop over each of the attribute elements being defined for */
332 : /* this feature class. */
333 : /* -------------------------------------------------------------------- */
334 598 : int nAttributeIndex = 0;
335 :
336 598 : bool bGotUnrecognizedType = false;
337 :
338 598 : CPLXMLNode *psAttrDef = psAttrSeq->psChild;
339 2945 : for (; psAttrDef != nullptr; psAttrDef = psAttrDef->psNext)
340 : {
341 2356 : 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 2356 : 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 2049 : continue;
401 : }
402 :
403 2355 : 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 2262 : const char *pszType = CPLGetXMLValue(psAttrDef, "type", nullptr);
409 2262 : const char *pszElementName = CPLGetXMLValue(psAttrDef, "name", nullptr);
410 : bool bNullable =
411 2262 : EQUAL(CPLGetXMLValue(psAttrDef, "minOccurs", "1"), "0");
412 : const char *pszMaxOccurs =
413 2262 : CPLGetXMLValue(psAttrDef, "maxOccurs", nullptr);
414 2262 : if (pszType != nullptr)
415 : {
416 1945 : const char *pszStrippedNSType = StripNS(pszType);
417 1945 : int nWidth = 0;
418 1945 : int nPrecision = 0;
419 :
420 1945 : GMLPropertyType gmlType = GMLPT_Untyped;
421 1945 : if (EQUAL(pszStrippedNSType, "string") ||
422 1436 : EQUAL(pszStrippedNSType, "Character"))
423 509 : gmlType = GMLPT_String;
424 1436 : else if (EQUAL(pszStrippedNSType, "date"))
425 64 : gmlType = GMLPT_Date;
426 1372 : else if (EQUAL(pszStrippedNSType, "time"))
427 3 : gmlType = GMLPT_Time;
428 1369 : else if (EQUAL(pszStrippedNSType, "dateTime") ||
429 1214 : EQUAL(pszStrippedNSType, "TimeInstantType"))
430 156 : gmlType = GMLPT_DateTime;
431 1213 : else if (EQUAL(pszStrippedNSType, "real") ||
432 1213 : EQUAL(pszStrippedNSType, "double") ||
433 1089 : EQUAL(pszStrippedNSType, "decimal"))
434 156 : gmlType = GMLPT_Real;
435 1057 : else if (EQUAL(pszStrippedNSType, "float"))
436 115 : gmlType = GMLPT_Float;
437 942 : else if (EQUAL(pszStrippedNSType, "int") ||
438 808 : EQUAL(pszStrippedNSType, "integer"))
439 135 : gmlType = GMLPT_Integer;
440 807 : else if (EQUAL(pszStrippedNSType, "long"))
441 9 : gmlType = GMLPT_Integer64;
442 798 : else if (EQUAL(pszStrippedNSType, "unsignedLong"))
443 : {
444 : // Optimistically map to signed integer
445 0 : gmlType = GMLPT_Integer64;
446 : }
447 798 : else if (EQUAL(pszStrippedNSType, "short"))
448 119 : gmlType = GMLPT_Short;
449 679 : else if (EQUAL(pszStrippedNSType, "boolean"))
450 115 : gmlType = GMLPT_Boolean;
451 : // TODO: Would be nice to have a binary type.
452 564 : else if (EQUAL(pszStrippedNSType, "hexBinary"))
453 0 : gmlType = GMLPT_String;
454 564 : else if (strcmp(pszType, "gml:FeaturePropertyType") == 0)
455 : {
456 3 : gmlType = GMLPT_FeatureProperty;
457 : }
458 561 : else if (STARTS_WITH(pszType, "gml:"))
459 : {
460 550 : const AssocNameType *psIter = apsPropertyTypes;
461 1652 : while (psIter->pszName)
462 : {
463 1652 : if (strncmp(pszType + 4, psIter->pszName,
464 1652 : strlen(psIter->pszName)) == 0)
465 : {
466 550 : OGRwkbGeometryType eType = psIter->eType;
467 1100 : std::string osSRSName;
468 :
469 : // Look if there's a comment restricting to subclasses.
470 550 : for (const CPLXMLNode *psIter2 = psAttrDef->psNext;
471 1026 : psIter2 != nullptr; psIter2 = psIter2->psNext)
472 : {
473 476 : if (psIter2->eType == CXT_Comment)
474 : {
475 102 : if (strstr(psIter2->pszValue,
476 : "restricted to Polygon"))
477 17 : eType = wkbPolygon;
478 85 : else if (strstr(psIter2->pszValue,
479 : "restricted to LineString"))
480 25 : eType = wkbLineString;
481 60 : else if (strstr(psIter2->pszValue,
482 : "restricted to MultiPolygon"))
483 13 : eType = wkbMultiPolygon;
484 47 : else if (strstr(
485 47 : psIter2->pszValue,
486 : "restricted to MultiLineString"))
487 11 : eType = wkbMultiLineString;
488 : else
489 : {
490 : const char *pszSRSName =
491 36 : strstr(psIter2->pszValue, "srsName=\"");
492 36 : if (pszSRSName)
493 : {
494 : osSRSName =
495 32 : pszSRSName + strlen("srsName=\"");
496 32 : const auto nPos = osSRSName.find('"');
497 32 : if (nPos != std::string::npos)
498 32 : osSRSName.resize(nPos);
499 : else
500 0 : osSRSName.clear();
501 : }
502 : }
503 : }
504 : }
505 :
506 : // Try to get coordinate precision from a construct like:
507 : /*
508 : <xs:element name="wkb_geometry" type="gml:SurfacePropertyType" nillable="true" minOccurs="0" maxOccurs="1">
509 : <xs:annotation>
510 : <xs:appinfo source="http://ogr.maptools.org/">
511 : <ogr:xy_coordinate_resolution>8.9e-9</ogr:xy_coordinate_resolution>
512 : <ogr:z_coordinate_resolution>1e-3</ogr:z_coordinate_resolution>
513 : <ogr:m_coordinate_resolution>1e-3</ogr:m_coordinate_resolution>
514 : </xs:appinfo>
515 : </xs:annotation>
516 : </xs:element>
517 : */
518 1100 : OGRGeomCoordinatePrecision oGeomCoordPrec;
519 : const auto psAnnotation =
520 550 : CPLGetXMLNode(psAttrDef, "annotation");
521 550 : if (psAnnotation)
522 : {
523 1 : for (const CPLXMLNode *psIterAppinfo =
524 : psAnnotation->psChild;
525 2 : psIterAppinfo;
526 1 : psIterAppinfo = psIterAppinfo->psNext)
527 : {
528 3 : if (psIterAppinfo->eType == CXT_Element &&
529 1 : strcmp(psIterAppinfo->pszValue,
530 2 : "appinfo") == 0 &&
531 1 : strcmp(CPLGetXMLValue(psIterAppinfo,
532 : "source", ""),
533 : "http://ogr.maptools.org/") == 0)
534 : {
535 1 : if (const char *pszXYRes = CPLGetXMLValue(
536 : psIterAppinfo,
537 : "xy_coordinate_resolution",
538 : nullptr))
539 : {
540 1 : const double dfVal = CPLAtof(pszXYRes);
541 1 : if (dfVal > 0 && std::isfinite(dfVal))
542 1 : oGeomCoordPrec.dfXYResolution =
543 : dfVal;
544 : }
545 1 : if (const char *pszZRes = CPLGetXMLValue(
546 : psIterAppinfo,
547 : "z_coordinate_resolution", nullptr))
548 : {
549 1 : const double dfVal = CPLAtof(pszZRes);
550 1 : if (dfVal > 0 && std::isfinite(dfVal))
551 1 : oGeomCoordPrec.dfZResolution =
552 : dfVal;
553 : }
554 : }
555 : }
556 : }
557 :
558 : GMLGeometryPropertyDefn *poDefn =
559 : new GMLGeometryPropertyDefn(
560 : pszElementName, pszElementName, eType,
561 550 : nAttributeIndex, bNullable, oGeomCoordPrec);
562 550 : poDefn->SetSRSName(osSRSName);
563 :
564 550 : if (poClass->AddGeometryProperty(poDefn) < 0)
565 0 : delete poDefn;
566 : else
567 550 : nAttributeIndex++;
568 :
569 550 : break;
570 : }
571 :
572 1102 : psIter++;
573 : }
574 :
575 550 : if (psIter->pszName == nullptr)
576 : {
577 : // Can be a non geometry gml type.
578 : // Too complex schema for us. Aborts parsing.
579 0 : delete poClass;
580 9 : return nullptr;
581 : }
582 :
583 550 : if (poClass->GetGeometryPropertyCount() == 0)
584 0 : bGotUnrecognizedType = true;
585 :
586 1936 : continue;
587 : }
588 :
589 : /* Integraph stuff */
590 11 : else if (strcmp(pszType, "G:Point_MultiPointPropertyType") == 0 ||
591 11 : strcmp(pszType, "gmgml:Point_MultiPointPropertyType") == 0)
592 : {
593 : GMLGeometryPropertyDefn *poDefn = new GMLGeometryPropertyDefn(
594 : pszElementName, pszElementName, wkbMultiPoint,
595 0 : nAttributeIndex, bNullable);
596 :
597 0 : if (poClass->AddGeometryProperty(poDefn) < 0)
598 0 : delete poDefn;
599 : else
600 0 : nAttributeIndex++;
601 :
602 0 : continue;
603 : }
604 11 : else if (strcmp(pszType,
605 11 : "G:LineString_MultiLineStringPropertyType") == 0 ||
606 11 : strcmp(pszType,
607 : "gmgml:LineString_MultiLineStringPropertyType") ==
608 : 0)
609 : {
610 : GMLGeometryPropertyDefn *poDefn = new GMLGeometryPropertyDefn(
611 : pszElementName, pszElementName, wkbMultiLineString,
612 0 : nAttributeIndex, bNullable);
613 :
614 0 : if (poClass->AddGeometryProperty(poDefn) < 0)
615 0 : delete poDefn;
616 : else
617 0 : nAttributeIndex++;
618 :
619 0 : continue;
620 : }
621 11 : else if (strcmp(pszType, "G:Polygon_MultiPolygonPropertyType") ==
622 11 : 0 ||
623 11 : strcmp(pszType,
624 11 : "gmgml:Polygon_MultiPolygonPropertyType") == 0 ||
625 11 : strcmp(pszType, "gmgml:Polygon_Surface_MultiSurface_"
626 : "CompositeSurfacePropertyType") == 0)
627 : {
628 : GMLGeometryPropertyDefn *poDefn = new GMLGeometryPropertyDefn(
629 : pszElementName, pszElementName, wkbMultiPolygon,
630 0 : nAttributeIndex, bNullable);
631 :
632 0 : if (poClass->AddGeometryProperty(poDefn) < 0)
633 0 : delete poDefn;
634 : else
635 0 : nAttributeIndex++;
636 :
637 0 : continue;
638 : }
639 :
640 : // ERDAS Apollo stufflike in
641 : // http://apollo.erdas.com/erdas-apollo/vector/WORLDWIDE?SERVICE=WFS&VERSION=1.0.0&REQUEST=DescribeFeatureType&TYPENAME=wfs:cntry98)
642 11 : else if (strcmp(pszType, "wfs:MixedPolygonPropertyType") == 0)
643 : {
644 : GMLGeometryPropertyDefn *poDefn = new GMLGeometryPropertyDefn(
645 : pszElementName, pszElementName, wkbMultiPolygon,
646 0 : nAttributeIndex, bNullable);
647 :
648 0 : if (poClass->AddGeometryProperty(poDefn) < 0)
649 0 : delete poDefn;
650 : else
651 0 : nAttributeIndex++;
652 :
653 0 : continue;
654 : }
655 :
656 : else
657 : {
658 11 : gmlType = GMLPT_Untyped;
659 11 : if (!LookForSimpleType(psSchemaNode, pszStrippedNSType,
660 : &gmlType, &nWidth, &nPrecision))
661 : {
662 : // Too complex schema for us. Aborts parsing.
663 9 : delete poClass;
664 9 : return nullptr;
665 : }
666 : }
667 :
668 1386 : if (pszElementName == nullptr)
669 0 : pszElementName = "unnamed";
670 1386 : const char *pszPropertyName = pszElementName;
671 1386 : if (gmlType == GMLPT_FeatureProperty)
672 : {
673 3 : pszPropertyName = CPLSPrintf("%s_href", pszElementName);
674 : }
675 : GMLPropertyDefn *poProp =
676 1386 : new GMLPropertyDefn(pszPropertyName, pszElementName);
677 :
678 1386 : if (pszMaxOccurs != nullptr && strcmp(pszMaxOccurs, "1") != 0)
679 2 : gmlType = GetListTypeFromSingleType(gmlType);
680 :
681 1386 : poProp->SetType(gmlType);
682 1386 : poProp->SetWidth(nWidth);
683 1386 : poProp->SetPrecision(nPrecision);
684 1386 : poProp->SetNullable(bNullable);
685 :
686 : const CPLXMLNode *psAnnotation =
687 1386 : CPLGetXMLNode(psAttrDef, "annotation");
688 1386 : if (psAnnotation)
689 : {
690 : const char *pszDocumentation =
691 3 : CPLGetXMLValue(psAnnotation, "documentation", nullptr);
692 3 : if (pszDocumentation)
693 3 : poProp->SetDocumentation(pszDocumentation);
694 : }
695 :
696 1386 : if (poClass->AddProperty(poProp) < 0)
697 0 : delete poProp;
698 : else
699 1386 : nAttributeIndex++;
700 :
701 1386 : continue;
702 : }
703 :
704 : // For now we skip geometries. Fixup later.
705 317 : CPLXMLNode *psSimpleType = CPLGetXMLNode(psAttrDef, "simpleType");
706 317 : if (psSimpleType == nullptr)
707 : {
708 19 : const char *pszRef = CPLGetXMLValue(psAttrDef, "ref", nullptr);
709 :
710 : // FME .xsd
711 19 : if (pszRef != nullptr && STARTS_WITH(pszRef, "gml:"))
712 : {
713 18 : const AssocNameType *psIter = apsRefTypes;
714 63 : while (psIter->pszName)
715 : {
716 63 : if (strncmp(pszRef + 4, psIter->pszName,
717 63 : strlen(psIter->pszName)) == 0)
718 : {
719 18 : if (poClass->GetGeometryPropertyCount() > 0)
720 : {
721 9 : OGRwkbGeometryType eNewType = psIter->eType;
722 : OGRwkbGeometryType eOldType =
723 : static_cast<OGRwkbGeometryType>(
724 9 : poClass->GetGeometryProperty(0)->GetType());
725 :
726 9 : if ((eNewType == wkbMultiPoint &&
727 6 : eOldType == wkbPoint) ||
728 3 : (eNewType == wkbMultiLineString &&
729 3 : eOldType == wkbLineString) ||
730 3 : (eNewType == wkbMultiPolygon &&
731 : eOldType == wkbPolygon))
732 : {
733 9 : poClass->GetGeometryProperty(0)->SetType(
734 : eNewType);
735 : }
736 : else
737 : {
738 0 : CPLDebug("GML",
739 : "Geometry field already found ! "
740 : "Ignoring the following ones");
741 : }
742 : }
743 : else
744 : {
745 : GMLGeometryPropertyDefn *poDefn =
746 : new GMLGeometryPropertyDefn(
747 : pszElementName, pszElementName,
748 9 : psIter->eType, nAttributeIndex, true);
749 :
750 9 : if (poClass->AddGeometryProperty(poDefn) < 0)
751 0 : delete poDefn;
752 : else
753 9 : nAttributeIndex++;
754 : }
755 :
756 18 : break;
757 : }
758 :
759 45 : psIter++;
760 : }
761 :
762 18 : if (psIter->pszName == nullptr)
763 : {
764 : // Can be a non geometry gml type .
765 : // Too complex schema for us. Aborts parsing.
766 0 : delete poClass;
767 0 : return nullptr;
768 : }
769 :
770 18 : if (poClass->GetGeometryPropertyCount() == 0)
771 0 : bGotUnrecognizedType = true;
772 :
773 18 : continue;
774 : }
775 :
776 : /* Parse stuff like the following found in
777 : http://199.29.1.81:8181/miwfs/GetFeature.ashx?REQUEST=GetFeature&MAXFEATURES=1&SERVICE=WFS&VERSION=1.0.0&TYPENAME=miwfs:World
778 : : <xs:element name="Obj" minOccurs="0" maxOccurs="1">
779 : <xs:complexType>
780 : <xs:sequence>
781 : <xs:element ref="gml:_Geometry"/>
782 : </xs:sequence>
783 : </xs:complexType>
784 : </xs:element>
785 : */
786 : CPLXMLNode *l_psComplexType =
787 1 : GetSingleChildElement(psAttrDef, "complexType");
788 : CPLXMLNode *psComplexTypeSequence =
789 1 : GetSingleChildElement(l_psComplexType, "sequence");
790 : CPLXMLNode *psComplexTypeSequenceElement =
791 1 : GetSingleChildElement(psComplexTypeSequence, "element");
792 :
793 2 : if (pszElementName != nullptr &&
794 1 : CheckMinMaxOccursCardinality(psAttrDef) &&
795 1 : psComplexTypeSequenceElement != nullptr &&
796 3 : CheckMinMaxOccursCardinality(psComplexTypeSequence) &&
797 1 : strcmp(CPLGetXMLValue(psComplexTypeSequenceElement, "ref", ""),
798 : "gml:_Geometry") == 0)
799 : {
800 : GMLGeometryPropertyDefn *poDefn = new GMLGeometryPropertyDefn(
801 : pszElementName, pszElementName, wkbUnknown, nAttributeIndex,
802 1 : bNullable);
803 :
804 1 : if (poClass->AddGeometryProperty(poDefn) < 0)
805 0 : delete poDefn;
806 : else
807 1 : nAttributeIndex++;
808 :
809 1 : continue;
810 : }
811 : else
812 : {
813 : // Too complex schema for us. Aborts parsing.
814 0 : delete poClass;
815 0 : return nullptr;
816 : }
817 : }
818 :
819 298 : if (pszElementName == nullptr)
820 0 : pszElementName = "unnamed";
821 : GMLPropertyDefn *poProp =
822 298 : new GMLPropertyDefn(pszElementName, pszElementName);
823 :
824 298 : GMLPropertyType eType = GMLPT_Untyped;
825 298 : int nWidth = 0;
826 298 : int nPrecision = 0;
827 298 : GetSimpleTypeProperties(psSimpleType, &eType, &nWidth, &nPrecision);
828 :
829 298 : if (pszMaxOccurs != nullptr && strcmp(pszMaxOccurs, "1") != 0)
830 9 : eType = GetListTypeFromSingleType(eType);
831 :
832 298 : poProp->SetType(eType);
833 298 : poProp->SetWidth(nWidth);
834 298 : poProp->SetPrecision(nPrecision);
835 298 : poProp->SetNullable(bNullable);
836 :
837 298 : const CPLXMLNode *psAnnotation = CPLGetXMLNode(psAttrDef, "annotation");
838 298 : if (psAnnotation)
839 : {
840 : const char *pszDocumentation =
841 8 : CPLGetXMLValue(psAnnotation, "documentation", nullptr);
842 8 : if (pszDocumentation)
843 8 : poProp->SetDocumentation(pszDocumentation);
844 : }
845 :
846 298 : if (poClass->AddProperty(poProp) < 0)
847 0 : delete poProp;
848 : else
849 298 : nAttributeIndex++;
850 : }
851 :
852 : // If we have found an unknown types, let's be on the side of caution and
853 : // create a geometry field.
854 589 : if (poClass->GetGeometryPropertyCount() == 0 && bGotUnrecognizedType)
855 : {
856 0 : poClass->AddGeometryProperty(
857 0 : new GMLGeometryPropertyDefn("", "", wkbUnknown, -1, true));
858 : }
859 :
860 : /* -------------------------------------------------------------------- */
861 : /* Class complete, add to reader class list. */
862 : /* -------------------------------------------------------------------- */
863 589 : poClass->SetSchemaLocked(true);
864 :
865 589 : return poClass;
866 : }
867 :
868 : /************************************************************************/
869 : /* ExcludeBaseGMLSchemas() */
870 : /************************************************************************/
871 :
872 12 : static bool ExcludeBaseGMLSchemas(const std::string &osFilename)
873 : {
874 : // List of substrings to exclude
875 : const std::vector<std::string> excludedBaseGMLReferencedSchemasList = {
876 96 : "/gml/3.2.1/", "gml/3.1.1/", "/gml/2.1.2/", "/gmlsfProfile/"};
877 28 : for (const auto &pattern : excludedBaseGMLReferencedSchemasList)
878 : {
879 24 : if (osFilename.find(pattern) != std::string::npos)
880 : {
881 8 : return false; // Found one of the excluded base GML referenced schema
882 : }
883 : }
884 4 : return true; // None of the base GML referenced schemas were found
885 : }
886 :
887 : /************************************************************************/
888 : /* GMLParseXMLFile() */
889 : /************************************************************************/
890 :
891 478 : static CPLXMLNode *GMLParseXMLFile(const char *pszFilename)
892 : {
893 478 : if (STARTS_WITH(pszFilename, "http://") ||
894 477 : STARTS_WITH(pszFilename, "https://"))
895 : {
896 1 : CPLXMLNode *psRet = nullptr;
897 1 : CPLHTTPResult *psResult = CPLHTTPFetch(pszFilename, nullptr);
898 1 : if (psResult != nullptr)
899 : {
900 1 : if (psResult->pabyData != nullptr)
901 : {
902 1 : psRet = CPLParseXMLString(
903 1 : reinterpret_cast<const char *>(psResult->pabyData));
904 : }
905 1 : CPLHTTPDestroyResult(psResult);
906 : }
907 1 : return psRet;
908 : }
909 : else
910 : {
911 477 : return CPLParseXMLFile(pszFilename);
912 : }
913 : }
914 :
915 : /************************************************************************/
916 : /* CPLGetFirstChildNode() */
917 : /************************************************************************/
918 :
919 12 : static CPLXMLNode *CPLGetFirstChildNode(CPLXMLNode *psNode)
920 : {
921 12 : if (psNode == nullptr)
922 0 : return nullptr;
923 12 : CPLXMLNode *psIter = psNode->psChild;
924 94 : while (psIter != nullptr)
925 : {
926 94 : if (psIter->eType == CXT_Element)
927 12 : return psIter;
928 82 : psIter = psIter->psNext;
929 : }
930 0 : return nullptr;
931 : }
932 :
933 : /************************************************************************/
934 : /* CPLGetLastNode() */
935 : /************************************************************************/
936 :
937 12 : static CPLXMLNode *CPLGetLastNode(CPLXMLNode *psNode)
938 : {
939 12 : CPLXMLNode *psIter = psNode;
940 23 : while (psIter->psNext != nullptr)
941 11 : psIter = psIter->psNext;
942 12 : return psIter;
943 : }
944 :
945 : /************************************************************************/
946 : /* CPLXMLSchemaResolveInclude() */
947 : /************************************************************************/
948 :
949 466 : static void CPLXMLSchemaResolveInclude(const char *pszMainSchemaLocation,
950 : CPLXMLNode *psSchemaNode,
951 : bool bUseSchemaImports)
952 : {
953 932 : std::set<CPLString> osAlreadyIncluded;
954 :
955 : bool bTryAgain;
956 474 : do
957 : {
958 474 : CPLXMLNode *psLast = nullptr;
959 474 : bTryAgain = false;
960 :
961 474 : CPLXMLNode *psThis = psSchemaNode->psChild;
962 5102 : for (; psThis != nullptr; psThis = psThis->psNext)
963 : {
964 : std::string osSchemaLocation =
965 4628 : CPLGetXMLValue(psThis, "schemaLocation", "");
966 :
967 6891 : if (psThis->eType == CXT_Element &&
968 2263 : (EQUAL(psThis->pszValue, "include") ||
969 2254 : (bUseSchemaImports == TRUE &&
970 20 : EQUAL(psThis->pszValue, "import") &&
971 12 : ExcludeBaseGMLSchemas(osSchemaLocation))))
972 : {
973 :
974 26 : if (!osSchemaLocation.empty() &&
975 26 : osAlreadyIncluded.count(osSchemaLocation) == 0)
976 : {
977 12 : osAlreadyIncluded.insert(osSchemaLocation);
978 :
979 12 : if (!STARTS_WITH(osSchemaLocation.c_str(), "http://") &&
980 24 : !STARTS_WITH(osSchemaLocation.c_str(), "https://") &&
981 12 : CPLIsFilenameRelative(osSchemaLocation.c_str()))
982 : {
983 36 : osSchemaLocation = CPLFormFilenameSafe(
984 24 : CPLGetPathSafe(pszMainSchemaLocation).c_str(),
985 12 : osSchemaLocation.c_str(), nullptr);
986 : }
987 :
988 : CPLXMLNode *psIncludedXSDTree =
989 12 : GMLParseXMLFile(osSchemaLocation.c_str());
990 12 : if (psIncludedXSDTree != nullptr)
991 : {
992 12 : CPLStripXMLNamespace(psIncludedXSDTree, nullptr, TRUE);
993 : CPLXMLNode *psIncludedSchemaNode =
994 12 : CPLGetXMLNode(psIncludedXSDTree, "=schema");
995 12 : if (psIncludedSchemaNode != nullptr)
996 : {
997 : // Substitute de <include> node by its content.
998 : CPLXMLNode *psFirstChildElement =
999 12 : CPLGetFirstChildNode(psIncludedSchemaNode);
1000 12 : if (psFirstChildElement != nullptr)
1001 : {
1002 : CPLXMLNode *psCopy =
1003 12 : CPLCloneXMLTree(psFirstChildElement);
1004 12 : if (psLast != nullptr)
1005 12 : psLast->psNext = psCopy;
1006 : else
1007 0 : psSchemaNode->psChild = psCopy;
1008 12 : CPLXMLNode *psNext = psThis->psNext;
1009 12 : psThis->psNext = nullptr;
1010 12 : CPLDestroyXMLNode(psThis);
1011 12 : psThis = CPLGetLastNode(psCopy);
1012 12 : psThis->psNext = psNext;
1013 :
1014 : // In case the included schema also contains
1015 : // includes.
1016 12 : bTryAgain = true;
1017 : }
1018 : }
1019 12 : CPLDestroyXMLNode(psIncludedXSDTree);
1020 : }
1021 : }
1022 : }
1023 :
1024 4628 : psLast = psThis;
1025 : }
1026 : } while (bTryAgain);
1027 :
1028 : const char *pszSchemaOutputName =
1029 466 : CPLGetConfigOption("GML_SCHEMA_OUTPUT_NAME", nullptr);
1030 466 : if (pszSchemaOutputName != nullptr)
1031 : {
1032 0 : CPLSerializeXMLTreeToFile(psSchemaNode, pszSchemaOutputName);
1033 : }
1034 466 : }
1035 :
1036 : /************************************************************************/
1037 : /* GetUniqueConstraints() */
1038 : /************************************************************************/
1039 :
1040 : static std::set<std::pair<std::string, std::string>>
1041 145 : GetUniqueConstraints(const CPLXMLNode *psNode)
1042 : {
1043 : /* Parse
1044 : <xs:unique name="uniqueConstraintpolyeas_id">
1045 : <xs:selector xpath="ogr:featureMember/ogr:poly"/>
1046 : <xs:field xpath="ogr:eas_id"/>
1047 : </xs:unique>
1048 : */
1049 145 : std::set<std::pair<std::string, std::string>> oSet;
1050 580 : for (const auto *psIter = psNode->psChild; psIter != nullptr;
1051 435 : psIter = psIter->psNext)
1052 : {
1053 435 : if (psIter->eType == CXT_Element && EQUAL(psIter->pszValue, "unique"))
1054 : {
1055 : const char *pszSelector =
1056 0 : CPLGetXMLValue(psIter, "selector.xpath", nullptr);
1057 : const char *pszField =
1058 0 : CPLGetXMLValue(psIter, "field.xpath", nullptr);
1059 0 : if (pszSelector && pszField && pszField[0] != '@')
1060 : {
1061 0 : const char *pszSlash = strchr(pszSelector, '/');
1062 0 : if (pszSlash)
1063 : {
1064 : oSet.insert(
1065 0 : std::pair(StripNS(pszSlash + 1), StripNS(pszField)));
1066 : }
1067 : }
1068 : }
1069 : }
1070 145 : return oSet;
1071 : }
1072 :
1073 : /************************************************************************/
1074 : /* GMLParseXSD() */
1075 : /************************************************************************/
1076 :
1077 466 : bool GMLParseXSD(const char *pszFile, bool bUseSchemaImports,
1078 : std::vector<GMLFeatureClass *> &aosClasses,
1079 : bool &bFullyUnderstood)
1080 :
1081 : {
1082 466 : bFullyUnderstood = false;
1083 :
1084 466 : if (pszFile == nullptr)
1085 0 : return false;
1086 :
1087 : /* -------------------------------------------------------------------- */
1088 : /* Load the raw XML file. */
1089 : /* -------------------------------------------------------------------- */
1090 466 : CPLXMLNode *psXSDTree = GMLParseXMLFile(pszFile);
1091 :
1092 466 : if (psXSDTree == nullptr)
1093 0 : return false;
1094 :
1095 : /* -------------------------------------------------------------------- */
1096 : /* Strip off any namespace qualifiers. */
1097 : /* -------------------------------------------------------------------- */
1098 466 : CPLStripXMLNamespace(psXSDTree, nullptr, TRUE);
1099 :
1100 : /* -------------------------------------------------------------------- */
1101 : /* Find <schema> root element. */
1102 : /* -------------------------------------------------------------------- */
1103 466 : CPLXMLNode *psSchemaNode = CPLGetXMLNode(psXSDTree, "=schema");
1104 466 : if (psSchemaNode == nullptr)
1105 : {
1106 0 : CPLDestroyXMLNode(psXSDTree);
1107 0 : return false;
1108 : }
1109 :
1110 : /* ==================================================================== */
1111 : /* Process each include directive. */
1112 : /* ==================================================================== */
1113 466 : CPLXMLSchemaResolveInclude(pszFile, psSchemaNode, bUseSchemaImports);
1114 :
1115 : // CPLSerializeXMLTreeToFile(psSchemaNode, "/vsistdout/");
1116 :
1117 466 : bFullyUnderstood = true;
1118 :
1119 : /* ==================================================================== */
1120 : /* Process each feature class definition. */
1121 : /* ==================================================================== */
1122 466 : CPLXMLNode *psThis = psSchemaNode->psChild;
1123 :
1124 466 : std::set<std::pair<std::string, std::string>> oSetUniqueConstraints;
1125 :
1126 5034 : for (; psThis != nullptr; psThis = psThis->psNext)
1127 : {
1128 : /* --------------------------------------------------------------------
1129 : */
1130 : /* Check for <xs:element> node. */
1131 : /* --------------------------------------------------------------------
1132 : */
1133 4568 : if (psThis->eType != CXT_Element || !EQUAL(psThis->pszValue, "element"))
1134 3984 : continue;
1135 :
1136 : /* --------------------------------------------------------------------
1137 : */
1138 : /* Get name */
1139 : /* --------------------------------------------------------------------
1140 : */
1141 754 : const char *pszName = CPLGetXMLValue(psThis, "name", nullptr);
1142 754 : if (pszName == nullptr)
1143 : {
1144 0 : continue;
1145 : }
1146 :
1147 : /* --------------------------------------------------------------------
1148 : */
1149 : /* Check the substitution group. */
1150 : /* --------------------------------------------------------------------
1151 : */
1152 : const char *pszSubGroup =
1153 754 : StripNS(CPLGetXMLValue(psThis, "substitutionGroup", ""));
1154 :
1155 754 : if (EQUAL(pszName, "FeatureCollection") &&
1156 145 : (EQUAL(pszSubGroup, "_FeatureCollection") ||
1157 105 : EQUAL(pszSubGroup, "_GML") ||
1158 95 : EQUAL(pszSubGroup, "AbstractFeature")))
1159 : {
1160 145 : oSetUniqueConstraints = GetUniqueConstraints(psThis);
1161 145 : continue;
1162 : }
1163 :
1164 : // AbstractFeature used by GML 3.2.
1165 609 : if (!EQUAL(pszSubGroup, "_Feature") &&
1166 139 : !EQUAL(pszSubGroup, "AbstractFeature"))
1167 : {
1168 0 : continue;
1169 : }
1170 :
1171 : /* --------------------------------------------------------------------
1172 : */
1173 : /* Get type and verify relationship with name. */
1174 : /* --------------------------------------------------------------------
1175 : */
1176 609 : const char *pszType = CPLGetXMLValue(psThis, "type", nullptr);
1177 609 : if (pszType == nullptr)
1178 : {
1179 14 : CPLXMLNode *psComplexType = CPLGetXMLNode(psThis, "complexType");
1180 14 : if (psComplexType)
1181 : {
1182 : GMLFeatureClass *poClass =
1183 14 : GMLParseFeatureType(psSchemaNode, pszName, psComplexType);
1184 14 : if (poClass)
1185 13 : aosClasses.push_back(poClass);
1186 : else
1187 1 : bFullyUnderstood = false;
1188 : }
1189 14 : continue;
1190 : }
1191 595 : if (strstr(pszType, ":") != nullptr)
1192 587 : pszType = strstr(pszType, ":") + 1;
1193 595 : if (EQUAL(pszType, pszName))
1194 : {
1195 : // A few WFS servers return a type name which is the element name
1196 : // without any _Type or Type suffix
1197 : // e.g.:
1198 : // http://apollo.erdas.com/erdas-apollo/vector/Cherokee?SERVICE=WFS&VERSION=1.0.0&REQUEST=DescribeFeatureType&TYPENAME=iwfs:Air
1199 : // */
1200 :
1201 : // TODO(schwehr): What was supposed to go here?
1202 : }
1203 :
1204 : // <element name="RekisteriyksikonPalstanTietoja"
1205 : // type="ktjkiiwfs:PalstanTietojaType" substitutionGroup="gml:_Feature"
1206 : // />
1207 595 : else if (strlen(pszType) > 4 &&
1208 595 : strcmp(pszType + strlen(pszType) - 4, "Type") == 0 &&
1209 595 : strlen(pszName) > strlen(pszType) - 4 &&
1210 0 : strncmp(pszName + strlen(pszName) - (strlen(pszType) - 4),
1211 0 : pszType, strlen(pszType) - 4) == 0)
1212 : {
1213 : }
1214 :
1215 595 : else if (!EQUALN(pszType, pszName, strlen(pszName)) ||
1216 595 : !(EQUAL(pszType + strlen(pszName), "_Type") ||
1217 412 : EQUAL(pszType + strlen(pszName), "Type") ||
1218 9 : EQUAL(pszType + strlen(pszName), "FeatureType")))
1219 : {
1220 8 : continue;
1221 : }
1222 :
1223 : // CanVec .xsd contains weird types that are not used in the related
1224 : // GML.
1225 587 : if (STARTS_WITH(pszName, "XyZz") || STARTS_WITH(pszName, "XyZ1") ||
1226 584 : STARTS_WITH(pszName, "XyZ2"))
1227 3 : continue;
1228 :
1229 : GMLFeatureClass *poClass =
1230 584 : GMLParseFeatureType(psSchemaNode, pszName, pszType);
1231 584 : if (poClass)
1232 576 : aosClasses.push_back(poClass);
1233 : else
1234 8 : bFullyUnderstood = false;
1235 : }
1236 :
1237 466 : CPLDestroyXMLNode(psXSDTree);
1238 :
1239 : // Attach unique constraints to fields
1240 466 : for (const auto &typeFieldPair : oSetUniqueConstraints)
1241 : {
1242 0 : for (const auto *poClass : aosClasses)
1243 : {
1244 0 : if (poClass->GetName() == typeFieldPair.first)
1245 : {
1246 : auto poProperty =
1247 0 : poClass->GetProperty(typeFieldPair.second.c_str());
1248 0 : if (poProperty)
1249 : {
1250 0 : poProperty->SetUnique(true);
1251 : }
1252 0 : break;
1253 : }
1254 : }
1255 : }
1256 :
1257 466 : return !aosClasses.empty();
1258 : }
|