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