1 : /******************************************************************************
2 : * $Id: parsexsd.cpp 25731 2013-03-11 13:08:34Z tamas $
3 : *
4 : * Project: GML Reader
5 : * Purpose: Implementation of GMLParseXSD()
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2005, Frank Warmerdam
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 "parsexsd.h"
31 : #include "cpl_error.h"
32 : #include "cpl_conv.h"
33 : #include "ogr_core.h"
34 :
35 : /************************************************************************/
36 : /* StripNS() */
37 : /* */
38 : /* Return potentially shortened form of string with namespace */
39 : /* stripped off if there is one. Returns pointer into */
40 : /* original string. */
41 : /************************************************************************/
42 :
43 1146 : const char *StripNS( const char *pszFullValue )
44 :
45 : {
46 1146 : const char *pszColon = strstr( pszFullValue, ":" );
47 1146 : if( pszColon == NULL )
48 471 : return pszFullValue;
49 : else
50 675 : return pszColon + 1;
51 : }
52 :
53 : /************************************************************************/
54 : /* GetSimpleTypeProperties() */
55 : /************************************************************************/
56 :
57 : static
58 35 : int GetSimpleTypeProperties(CPLXMLNode *psTypeNode,
59 : GMLPropertyType *pGMLType,
60 : int *pnWidth,
61 : int *pnPrecision)
62 : {
63 : const char *pszBase =
64 : StripNS( CPLGetXMLValue( psTypeNode,
65 35 : "restriction.base", "" ));
66 :
67 35 : if( EQUAL(pszBase,"decimal") )
68 : {
69 13 : *pGMLType = GMLPT_Real;
70 : const char *pszWidth =
71 : CPLGetXMLValue( psTypeNode,
72 13 : "restriction.totalDigits.value", "0" );
73 : const char *pszPrecision =
74 : CPLGetXMLValue( psTypeNode,
75 13 : "restriction.fractionDigits.value", "0" );
76 13 : *pnWidth = atoi(pszWidth);
77 13 : *pnPrecision = atoi(pszPrecision);
78 13 : return TRUE;
79 : }
80 :
81 22 : else if( EQUAL(pszBase,"float")
82 : || EQUAL(pszBase,"double") )
83 : {
84 0 : *pGMLType = GMLPT_Real;
85 0 : return TRUE;
86 : }
87 :
88 22 : else if( EQUAL(pszBase,"integer") )
89 : {
90 8 : *pGMLType = GMLPT_Integer;
91 : const char *pszWidth =
92 : CPLGetXMLValue( psTypeNode,
93 8 : "restriction.totalDigits.value", "0" );
94 8 : *pnWidth = atoi(pszWidth);
95 8 : return TRUE;
96 : }
97 :
98 14 : else if( EQUAL(pszBase,"string") )
99 : {
100 14 : *pGMLType = GMLPT_String;
101 : const char *pszWidth =
102 : CPLGetXMLValue( psTypeNode,
103 14 : "restriction.maxLength.value", "0" );
104 14 : *pnWidth = atoi(pszWidth);
105 14 : return TRUE;
106 : }
107 :
108 : /* TODO: Would be nice to have a proper date type */
109 0 : else if( EQUAL(pszBase,"date") ||
110 : EQUAL(pszBase,"dateTime") )
111 : {
112 0 : *pGMLType = GMLPT_String;
113 0 : return TRUE;
114 : }
115 0 : return FALSE;
116 : }
117 :
118 : /************************************************************************/
119 : /* LookForSimpleType() */
120 : /************************************************************************/
121 :
122 : static
123 1 : int LookForSimpleType(CPLXMLNode *psSchemaNode,
124 : const char* pszStrippedNSType,
125 : GMLPropertyType *pGMLType,
126 : int *pnWidth,
127 : int *pnPrecision)
128 : {
129 : CPLXMLNode *psThis;
130 10 : for( psThis = psSchemaNode->psChild;
131 : psThis != NULL; psThis = psThis->psNext )
132 : {
133 10 : if( psThis->eType == CXT_Element
134 : && EQUAL(psThis->pszValue,"simpleType")
135 : && EQUAL(CPLGetXMLValue(psThis,"name",""),pszStrippedNSType) )
136 : {
137 1 : break;
138 : }
139 : }
140 1 : if (psThis == NULL)
141 0 : return FALSE;
142 :
143 1 : return GetSimpleTypeProperties(psThis, pGMLType, pnWidth, pnPrecision);
144 : }
145 :
146 : /************************************************************************/
147 : /* GetSingleChildElement() */
148 : /************************************************************************/
149 :
150 : /* Returns the child element whose name is pszExpectedValue only if */
151 : /* there is only one child that is an element. */
152 : static
153 3 : CPLXMLNode* GetSingleChildElement(CPLXMLNode* psNode, const char* pszExpectedValue)
154 : {
155 3 : CPLXMLNode* psChild = NULL;
156 : CPLXMLNode* psIter;
157 :
158 3 : if( psNode == NULL )
159 0 : return NULL;
160 :
161 3 : psIter = psNode->psChild;
162 3 : if( psIter == NULL )
163 0 : return NULL;
164 12 : while( psIter != NULL )
165 : {
166 6 : if( psIter->eType == CXT_Element )
167 : {
168 3 : if( psChild != NULL )
169 0 : return NULL;
170 3 : if( pszExpectedValue != NULL &&
171 : strcmp(psIter->pszValue, pszExpectedValue) != 0 )
172 0 : return NULL;
173 3 : psChild = psIter;
174 : }
175 6 : psIter = psIter->psNext;
176 : }
177 3 : return psChild;
178 : }
179 :
180 : /************************************************************************/
181 : /* CheckMinMaxOccursCardinality() */
182 : /************************************************************************/
183 :
184 2 : static int CheckMinMaxOccursCardinality(CPLXMLNode* psNode)
185 : {
186 2 : const char* pszMinOccurs = CPLGetXMLValue( psNode, "minOccurs", NULL );
187 2 : const char* pszMaxOccurs = CPLGetXMLValue( psNode, "maxOccurs", NULL );
188 : return (pszMinOccurs == NULL || EQUAL(pszMinOccurs, "0") ||
189 : EQUAL(pszMinOccurs, "1")) &&
190 2 : (pszMaxOccurs == NULL || EQUAL(pszMaxOccurs, "1"));
191 : }
192 :
193 :
194 : /************************************************************************/
195 : /* ParseFeatureType() */
196 : /************************************************************************/
197 :
198 : typedef struct
199 : {
200 : const char* pszName;
201 : OGRwkbGeometryType eType;
202 : } AssocNameType;
203 :
204 : static const AssocNameType apsPropertyTypes [] =
205 : {
206 : {"GeometryPropertyType", wkbUnknown},
207 : {"PointPropertyType", wkbPoint},
208 : {"LineStringPropertyType", wkbLineString},
209 : {"CurvePropertyType", wkbLineString},
210 : {"PolygonPropertyType", wkbPolygon},
211 : {"SurfacePropertyType", wkbPolygon},
212 : {"MultiPointPropertyType", wkbMultiPoint},
213 : {"MultiLineStringPropertyType", wkbMultiLineString},
214 : {"MultiCurvePropertyType", wkbMultiLineString},
215 : {"MultiPolygonPropertyType", wkbMultiPolygon},
216 : {"MultiSurfacePropertyType", wkbMultiPolygon},
217 : {"MultiGeometryPropertyType", wkbGeometryCollection},
218 : {"GeometryAssociationType", wkbUnknown},
219 : {NULL, wkbUnknown},
220 : };
221 :
222 : /* Found in FME .xsd (e.g. <element ref="gml:curveProperty" minOccurs="0"/>) */
223 : static const AssocNameType apsRefTypes [] =
224 : {
225 : {"pointProperty", wkbPoint},
226 : {"curveProperty", wkbLineString},
227 : {"surfaceProperty", wkbPolygon},
228 : {"multiPointProperty", wkbMultiPoint},
229 : {"multiCurveProperty", wkbMultiLineString},
230 : {"multiSurfaceProperty", wkbMultiPolygon},
231 : {NULL, wkbUnknown},
232 : };
233 :
234 : static
235 : GMLFeatureClass* GMLParseFeatureType(CPLXMLNode *psSchemaNode,
236 : const char* pszName,
237 : CPLXMLNode *psThis);
238 :
239 : static
240 117 : GMLFeatureClass* GMLParseFeatureType(CPLXMLNode *psSchemaNode,
241 : const char* pszName,
242 : const char *pszType)
243 : {
244 : CPLXMLNode *psThis;
245 1802 : for( psThis = psSchemaNode->psChild;
246 : psThis != NULL; psThis = psThis->psNext )
247 : {
248 1802 : if( psThis->eType == CXT_Element
249 : && EQUAL(psThis->pszValue,"complexType")
250 : && EQUAL(CPLGetXMLValue(psThis,"name",""),pszType) )
251 : {
252 117 : break;
253 : }
254 : }
255 117 : if (psThis == NULL)
256 0 : return NULL;
257 :
258 117 : return GMLParseFeatureType(psSchemaNode, pszName, psThis);
259 : }
260 :
261 :
262 : static
263 192 : GMLFeatureClass* GMLParseFeatureType(CPLXMLNode *psSchemaNode,
264 : const char* pszName,
265 : CPLXMLNode *psComplexType)
266 : {
267 :
268 : /* -------------------------------------------------------------------- */
269 : /* Grab the sequence of extensions greatgrandchild. */
270 : /* -------------------------------------------------------------------- */
271 : CPLXMLNode *psAttrSeq =
272 : CPLGetXMLNode( psComplexType,
273 192 : "complexContent.extension.sequence" );
274 :
275 192 : if( psAttrSeq == NULL )
276 : {
277 0 : return NULL;
278 : }
279 :
280 : /* -------------------------------------------------------------------- */
281 : /* We are pretty sure this going to be a valid Feature class */
282 : /* now, so create it. */
283 : /* -------------------------------------------------------------------- */
284 192 : GMLFeatureClass *poClass = new GMLFeatureClass( pszName );
285 :
286 : /* -------------------------------------------------------------------- */
287 : /* Loop over each of the attribute elements being defined for */
288 : /* this feature class. */
289 : /* -------------------------------------------------------------------- */
290 : CPLXMLNode *psAttrDef;
291 192 : int nAttributeIndex = 0;
292 :
293 192 : int bGotUnrecognizedType = FALSE;
294 :
295 1145 : for( psAttrDef = psAttrSeq->psChild;
296 : psAttrDef != NULL;
297 : psAttrDef = psAttrDef->psNext )
298 : {
299 953 : if( strcmp(psAttrDef->pszValue,"group") == 0 )
300 : {
301 : /* Too complex schema for us. Aborts parsing */
302 0 : delete poClass;
303 0 : return NULL;
304 : }
305 :
306 953 : if( !EQUAL(psAttrDef->pszValue,"element") )
307 0 : continue;
308 :
309 : /* MapServer WFS writes element type as an attribute of element */
310 : /* not as a simpleType definition */
311 953 : const char* pszType = CPLGetXMLValue( psAttrDef, "type", NULL );
312 953 : const char* pszElementName = CPLGetXMLValue( psAttrDef, "name", NULL );
313 953 : if (pszType != NULL)
314 : {
315 900 : const char* pszStrippedNSType = StripNS(pszType);
316 900 : int nWidth = 0, nPrecision = 0;
317 :
318 900 : GMLPropertyType gmlType = GMLPT_Untyped;
319 1170 : if (EQUAL(pszStrippedNSType, "string") ||
320 : EQUAL(pszStrippedNSType, "Character"))
321 270 : gmlType = GMLPT_String;
322 : /* TODO: Would be nice to have a proper date type */
323 630 : else if (EQUAL(pszStrippedNSType, "date") ||
324 : EQUAL(pszStrippedNSType, "dateTime"))
325 0 : gmlType = GMLPT_String;
326 940 : else if (EQUAL(pszStrippedNSType, "real") ||
327 : EQUAL(pszStrippedNSType, "double") ||
328 : EQUAL(pszStrippedNSType, "float") ||
329 : EQUAL(pszStrippedNSType, "decimal"))
330 310 : gmlType = GMLPT_Real;
331 458 : else if (EQUAL(pszStrippedNSType, "short") ||
332 : EQUAL(pszStrippedNSType, "int") ||
333 : EQUAL(pszStrippedNSType, "integer") ||
334 : EQUAL(pszStrippedNSType, "long"))
335 138 : gmlType = GMLPT_Integer;
336 182 : else if (strncmp(pszType, "gml:", 4) == 0)
337 : {
338 181 : const AssocNameType* psIter = apsPropertyTypes;
339 1085 : while(psIter->pszName)
340 : {
341 904 : if (strncmp(pszType + 4, psIter->pszName, strlen(psIter->pszName)) == 0)
342 : {
343 181 : if (poClass->GetGeometryAttributeIndex() != -1)
344 : {
345 0 : CPLDebug("GML", "Geometry field already found ! Ignoring the following ones");
346 : }
347 : else
348 : {
349 181 : poClass->SetGeometryElement(pszElementName);
350 181 : poClass->SetGeometryType(psIter->eType);
351 181 : poClass->SetGeometryAttributeIndex( nAttributeIndex );
352 :
353 181 : nAttributeIndex ++;
354 : }
355 :
356 181 : break;
357 : }
358 :
359 723 : psIter ++;
360 : }
361 :
362 181 : if (psIter->pszName == NULL)
363 : {
364 : /* Can be a non geometry gml type */
365 : /* Too complex schema for us. Aborts parsing */
366 0 : delete poClass;
367 0 : return NULL;
368 : }
369 :
370 181 : if (poClass->GetGeometryAttributeIndex() == -1)
371 0 : bGotUnrecognizedType = TRUE;
372 :
373 181 : continue;
374 : }
375 :
376 : /* Integraph stuff */
377 1 : else if (strcmp(pszType, "G:Point_MultiPointPropertyType") == 0 ||
378 : strcmp(pszType, "gmgml:Point_MultiPointPropertyType") == 0)
379 : {
380 0 : poClass->SetGeometryElement(pszElementName);
381 0 : poClass->SetGeometryType(wkbMultiPoint);
382 0 : poClass->SetGeometryAttributeIndex( nAttributeIndex );
383 :
384 0 : nAttributeIndex ++;
385 0 : continue;
386 : }
387 1 : else if (strcmp(pszType, "G:LineString_MultiLineStringPropertyType") == 0 ||
388 : strcmp(pszType, "gmgml:LineString_MultiLineStringPropertyType") == 0)
389 : {
390 0 : poClass->SetGeometryElement(pszElementName);
391 0 : poClass->SetGeometryType(wkbMultiLineString);
392 0 : poClass->SetGeometryAttributeIndex( nAttributeIndex );
393 :
394 0 : nAttributeIndex ++;
395 0 : continue;
396 : }
397 1 : else if (strcmp(pszType, "G:Polygon_MultiPolygonPropertyType") == 0 ||
398 : strcmp(pszType, "gmgml:Polygon_MultiPolygonPropertyType") == 0 ||
399 : strcmp(pszType, "gmgml:Polygon_Surface_MultiSurface_CompositeSurfacePropertyType") == 0)
400 : {
401 0 : poClass->SetGeometryElement(pszElementName);
402 0 : poClass->SetGeometryType(wkbMultiPolygon);
403 0 : poClass->SetGeometryAttributeIndex( nAttributeIndex );
404 :
405 0 : nAttributeIndex ++;
406 0 : continue;
407 : }
408 :
409 : /* ERDAS Apollo stuff (like in http://apollo.erdas.com/erdas-apollo/vector/WORLDWIDE?SERVICE=WFS&VERSION=1.0.0&REQUEST=DescribeFeatureType&TYPENAME=wfs:cntry98) */
410 1 : else if (strcmp(pszType, "wfs:MixedPolygonPropertyType") == 0)
411 : {
412 0 : poClass->SetGeometryElement(pszElementName);
413 0 : poClass->SetGeometryType(wkbMultiPolygon);
414 0 : poClass->SetGeometryAttributeIndex( nAttributeIndex );
415 :
416 0 : nAttributeIndex ++;
417 0 : continue;
418 : }
419 :
420 : else
421 : {
422 1 : gmlType = GMLPT_Untyped;
423 1 : if ( ! LookForSimpleType(psSchemaNode, pszStrippedNSType,
424 : &gmlType, &nWidth, &nPrecision) )
425 : {
426 : /* Too complex schema for us. Aborts parsing */
427 0 : delete poClass;
428 0 : return NULL;
429 : }
430 : }
431 :
432 719 : if (pszElementName == NULL)
433 0 : pszElementName = "unnamed";
434 : GMLPropertyDefn *poProp = new GMLPropertyDefn(
435 719 : pszElementName, pszElementName );
436 :
437 719 : poProp->SetType( gmlType );
438 719 : poProp->SetAttributeIndex( nAttributeIndex );
439 719 : poProp->SetWidth( nWidth );
440 719 : poProp->SetPrecision( nPrecision );
441 :
442 719 : if (poClass->AddProperty( poProp ) < 0)
443 0 : delete poProp;
444 : else
445 719 : nAttributeIndex ++;
446 :
447 719 : continue;
448 : }
449 :
450 : // For now we skip geometries .. fixup later.
451 53 : CPLXMLNode* psSimpleType = CPLGetXMLNode( psAttrDef, "simpleType" );
452 53 : if( psSimpleType == NULL )
453 : {
454 19 : const char* pszRef = CPLGetXMLValue( psAttrDef, "ref", NULL );
455 :
456 : /* FME .xsd */
457 19 : if (pszRef != NULL && strncmp(pszRef, "gml:", 4) == 0)
458 : {
459 18 : const AssocNameType* psIter = apsRefTypes;
460 81 : while(psIter->pszName)
461 : {
462 63 : if (strncmp(pszRef + 4, psIter->pszName, strlen(psIter->pszName)) == 0)
463 : {
464 18 : if (poClass->GetGeometryAttributeIndex() != -1)
465 : {
466 9 : OGRwkbGeometryType eNewType = psIter->eType;
467 9 : OGRwkbGeometryType eOldType = (OGRwkbGeometryType)poClass->GetGeometryType();
468 18 : if ((eNewType == wkbMultiPoint && eOldType == wkbPoint) ||
469 : (eNewType == wkbMultiLineString && eOldType == wkbLineString) ||
470 : (eNewType == wkbMultiPolygon && eOldType == wkbPolygon))
471 : {
472 9 : poClass->SetGeometryType(eNewType);
473 : }
474 : else
475 : {
476 0 : CPLDebug("GML", "Geometry field already found ! Ignoring the following ones");
477 : }
478 : }
479 : else
480 : {
481 9 : poClass->SetGeometryElement(pszElementName);
482 9 : poClass->SetGeometryType(psIter->eType);
483 9 : poClass->SetGeometryAttributeIndex( nAttributeIndex );
484 :
485 9 : nAttributeIndex ++;
486 : }
487 :
488 18 : break;
489 : }
490 :
491 45 : psIter ++;
492 : }
493 :
494 18 : if (psIter->pszName == NULL)
495 : {
496 : /* Can be a non geometry gml type */
497 : /* Too complex schema for us. Aborts parsing */
498 0 : delete poClass;
499 0 : return NULL;
500 : }
501 :
502 18 : if (poClass->GetGeometryAttributeIndex() == -1)
503 0 : bGotUnrecognizedType = TRUE;
504 :
505 18 : continue;
506 : }
507 :
508 : /* Parse stuff like the following found in http://199.29.1.81:8181/miwfs/GetFeature.ashx?REQUEST=GetFeature&MAXFEATURES=1&SERVICE=WFS&VERSION=1.0.0&TYPENAME=miwfs:World :
509 : <xs:element name="Obj" minOccurs="0" maxOccurs="1">
510 : <xs:complexType>
511 : <xs:sequence>
512 : <xs:element ref="gml:_Geometry"/>
513 : </xs:sequence>
514 : </xs:complexType>
515 : </xs:element>
516 : */
517 1 : CPLXMLNode* psComplexType = GetSingleChildElement( psAttrDef, "complexType" );
518 1 : CPLXMLNode* psComplexTypeSequence = GetSingleChildElement( psComplexType, "sequence" );
519 1 : CPLXMLNode* psComplexTypeSequenceElement = GetSingleChildElement( psComplexTypeSequence, "element" );
520 :
521 1 : if( pszElementName != NULL &&
522 : CheckMinMaxOccursCardinality(psAttrDef) &&
523 : psComplexTypeSequenceElement != NULL &&
524 : CheckMinMaxOccursCardinality(psComplexTypeSequence) &&
525 : strcmp(CPLGetXMLValue( psComplexTypeSequenceElement, "ref", "" ), "gml:_Geometry") == 0 )
526 : {
527 1 : poClass->SetGeometryElement(pszElementName);
528 1 : poClass->SetGeometryType(wkbUnknown);
529 1 : poClass->SetGeometryAttributeIndex( nAttributeIndex );
530 :
531 1 : nAttributeIndex ++;
532 :
533 1 : continue;
534 : }
535 : else
536 : {
537 : /* Too complex schema for us. Aborts parsing */
538 0 : delete poClass;
539 0 : return NULL;
540 : }
541 : }
542 :
543 34 : if (pszElementName == NULL)
544 0 : pszElementName = "unnamed";
545 : GMLPropertyDefn *poProp = new GMLPropertyDefn(
546 34 : pszElementName, pszElementName );
547 :
548 34 : GMLPropertyType eType = GMLPT_Untyped;
549 34 : int nWidth = 0, nPrecision = 0;
550 34 : GetSimpleTypeProperties(psSimpleType, &eType, &nWidth, &nPrecision);
551 34 : poProp->SetType( eType );
552 34 : poProp->SetWidth( nWidth );
553 34 : poProp->SetPrecision( nPrecision );
554 34 : poProp->SetAttributeIndex( nAttributeIndex );
555 :
556 34 : if (poClass->AddProperty( poProp ) < 0)
557 0 : delete poProp;
558 : else
559 34 : nAttributeIndex ++;
560 : }
561 :
562 : /* Only report wkbNone if we didn't find a known geometry type */
563 : /* and there were not any unknown types (in case this unknown type */
564 : /* would be a geometry type) */
565 192 : if (poClass->GetGeometryAttributeIndex() == -1 &&
566 : !bGotUnrecognizedType)
567 : {
568 1 : poClass->SetGeometryType(wkbNone);
569 : }
570 :
571 : /* -------------------------------------------------------------------- */
572 : /* Class complete, add to reader class list. */
573 : /* -------------------------------------------------------------------- */
574 192 : poClass->SetSchemaLocked( TRUE );
575 :
576 192 : return poClass;
577 : }
578 :
579 : /************************************************************************/
580 : /* GMLParseXSD() */
581 : /************************************************************************/
582 :
583 151 : int GMLParseXSD( const char *pszFile,
584 : std::vector<GMLFeatureClass*> & aosClasses)
585 :
586 : {
587 151 : if( pszFile == NULL )
588 0 : return FALSE;
589 :
590 : /* -------------------------------------------------------------------- */
591 : /* Load the raw XML file. */
592 : /* -------------------------------------------------------------------- */
593 151 : CPLXMLNode *psXSDTree = CPLParseXMLFile( pszFile );
594 :
595 151 : if( psXSDTree == NULL )
596 0 : return FALSE;
597 :
598 : /* -------------------------------------------------------------------- */
599 : /* Strip off any namespace qualifiers. */
600 : /* -------------------------------------------------------------------- */
601 151 : CPLStripXMLNamespace( psXSDTree, NULL, TRUE );
602 :
603 : /* -------------------------------------------------------------------- */
604 : /* Find <schema> root element. */
605 : /* -------------------------------------------------------------------- */
606 151 : CPLXMLNode *psSchemaNode = CPLGetXMLNode( psXSDTree, "=schema" );
607 151 : if( psSchemaNode == NULL )
608 : {
609 0 : CPLDestroyXMLNode( psXSDTree );
610 0 : return FALSE;
611 : }
612 :
613 : /* ==================================================================== */
614 : /* Process each feature class definition. */
615 : /* ==================================================================== */
616 : CPLXMLNode *psThis;
617 :
618 2272 : for( psThis = psSchemaNode->psChild;
619 : psThis != NULL; psThis = psThis->psNext )
620 : {
621 : /* -------------------------------------------------------------------- */
622 : /* Check for <xs:element> node. */
623 : /* -------------------------------------------------------------------- */
624 2121 : if( psThis->eType != CXT_Element
625 : || !EQUAL(psThis->pszValue,"element") )
626 1910 : continue;
627 :
628 : /* -------------------------------------------------------------------- */
629 : /* Check the substitution group. */
630 : /* -------------------------------------------------------------------- */
631 : const char *pszSubGroup =
632 211 : StripNS(CPLGetXMLValue(psThis,"substitutionGroup",""));
633 :
634 : // Old OGR produced elements for the feature collection.
635 211 : if( EQUAL(pszSubGroup, "_FeatureCollection") )
636 12 : continue;
637 :
638 199 : if( !EQUAL(pszSubGroup, "_Feature") &&
639 : !EQUAL(pszSubGroup, "AbstractFeature") /* AbstractFeature used by GML 3.2 */ )
640 : {
641 4 : continue;
642 : }
643 :
644 : /* -------------------------------------------------------------------- */
645 : /* Get name */
646 : /* -------------------------------------------------------------------- */
647 : const char *pszName;
648 :
649 195 : pszName = CPLGetXMLValue( psThis, "name", NULL );
650 195 : if( pszName == NULL )
651 : {
652 0 : continue;
653 : }
654 :
655 : /* -------------------------------------------------------------------- */
656 : /* Get type and verify relationship with name. */
657 : /* -------------------------------------------------------------------- */
658 : const char *pszType;
659 :
660 195 : pszType = CPLGetXMLValue( psThis, "type", NULL );
661 195 : if (pszType == NULL)
662 : {
663 75 : CPLXMLNode *psComplexType = CPLGetXMLNode( psThis, "complexType" );
664 75 : if (psComplexType)
665 : {
666 : GMLFeatureClass* poClass =
667 75 : GMLParseFeatureType(psSchemaNode, pszName, psComplexType);
668 75 : if (poClass)
669 75 : aosClasses.push_back(poClass);
670 : }
671 75 : continue;
672 : }
673 120 : if( strstr( pszType, ":" ) != NULL )
674 120 : pszType = strstr( pszType, ":" ) + 1;
675 120 : if( EQUAL(pszType, pszName) )
676 : {
677 : /* A few WFS servers return a type name which is the element name */
678 : /* without any _Type or Type suffix */
679 : /* e.g. : http://apollo.erdas.com/erdas-apollo/vector/Cherokee?SERVICE=WFS&VERSION=1.0.0&REQUEST=DescribeFeatureType&TYPENAME=iwfs:Air */
680 : }
681 120 : else if( !EQUALN(pszType,pszName,strlen(pszName))
682 : || !(EQUAL(pszType+strlen(pszName),"_Type") ||
683 : EQUAL(pszType+strlen(pszName),"Type")) )
684 : {
685 0 : continue;
686 : }
687 :
688 : /* CanVec .xsd contains weird types that are not used in the related GML */
689 120 : if (strncmp(pszName, "XyZz", 4) == 0 ||
690 : strncmp(pszName, "XyZ1", 4) == 0 ||
691 : strncmp(pszName, "XyZ2", 4) == 0)
692 3 : continue;
693 :
694 : GMLFeatureClass* poClass =
695 117 : GMLParseFeatureType(psSchemaNode, pszName, pszType);
696 117 : if (poClass)
697 117 : aosClasses.push_back(poClass);
698 : }
699 :
700 151 : CPLDestroyXMLNode( psXSDTree );
701 :
702 151 : if( aosClasses.size() > 0 )
703 : {
704 145 : return TRUE;
705 : }
706 : else
707 6 : return FALSE;
708 : }
|