1 : /******************************************************************************
2 : * $Id: parsexsd.cpp 24886 2012-09-01 15:00:24Z rouault $
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 1468 : const char *StripNS( const char *pszFullValue )
44 :
45 : {
46 1468 : const char *pszColon = strstr( pszFullValue, ":" );
47 1468 : if( pszColon == NULL )
48 598 : return pszFullValue;
49 : else
50 870 : return pszColon + 1;
51 : }
52 :
53 : /************************************************************************/
54 : /* GetSimpleTypeProperties() */
55 : /************************************************************************/
56 :
57 : static
58 135 : int GetSimpleTypeProperties(CPLXMLNode *psTypeNode,
59 : GMLPropertyType *pGMLType,
60 : int *pnWidth,
61 : int *pnPrecision)
62 : {
63 : const char *pszBase =
64 : StripNS( CPLGetXMLValue( psTypeNode,
65 135 : "restriction.base", "" ));
66 :
67 135 : if( EQUAL(pszBase,"decimal") )
68 : {
69 46 : *pGMLType = GMLPT_Real;
70 : const char *pszWidth =
71 : CPLGetXMLValue( psTypeNode,
72 46 : "restriction.totalDigits.value", "0" );
73 : const char *pszPrecision =
74 : CPLGetXMLValue( psTypeNode,
75 46 : "restriction.fractionDigits.value", "0" );
76 46 : *pnWidth = atoi(pszWidth);
77 46 : *pnPrecision = atoi(pszPrecision);
78 46 : return TRUE;
79 : }
80 :
81 89 : else if( EQUAL(pszBase,"float")
82 : || EQUAL(pszBase,"double") )
83 : {
84 0 : *pGMLType = GMLPT_Real;
85 0 : return TRUE;
86 : }
87 :
88 89 : else if( EQUAL(pszBase,"integer") )
89 : {
90 41 : *pGMLType = GMLPT_Integer;
91 : const char *pszWidth =
92 : CPLGetXMLValue( psTypeNode,
93 41 : "restriction.totalDigits.value", "0" );
94 41 : *pnWidth = atoi(pszWidth);
95 41 : return TRUE;
96 : }
97 :
98 48 : else if( EQUAL(pszBase,"string") )
99 : {
100 48 : *pGMLType = GMLPT_String;
101 : const char *pszWidth =
102 : CPLGetXMLValue( psTypeNode,
103 48 : "restriction.maxLength.value", "0" );
104 48 : *pnWidth = atoi(pszWidth);
105 48 : 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 : {NULL, wkbUnknown},
219 : };
220 :
221 : /* Found in FME .xsd (e.g. <element ref="gml:curveProperty" minOccurs="0"/>) */
222 : static const AssocNameType apsRefTypes [] =
223 : {
224 : {"pointProperty", wkbPoint},
225 : {"curveProperty", wkbLineString},
226 : {"surfaceProperty", wkbPolygon},
227 : {"multiPointProperty", wkbMultiPoint},
228 : {"multiCurveProperty", wkbMultiLineString},
229 : {"multiSurfaceProperty", wkbMultiPolygon},
230 : {NULL, wkbUnknown},
231 : };
232 :
233 : static
234 : GMLFeatureClass* GMLParseFeatureType(CPLXMLNode *psSchemaNode,
235 : const char* pszName,
236 : CPLXMLNode *psThis);
237 :
238 : static
239 141 : GMLFeatureClass* GMLParseFeatureType(CPLXMLNode *psSchemaNode,
240 : const char* pszName,
241 : const char *pszType)
242 : {
243 : CPLXMLNode *psThis;
244 2045 : for( psThis = psSchemaNode->psChild;
245 : psThis != NULL; psThis = psThis->psNext )
246 : {
247 2045 : if( psThis->eType == CXT_Element
248 : && EQUAL(psThis->pszValue,"complexType")
249 : && EQUAL(CPLGetXMLValue(psThis,"name",""),pszType) )
250 : {
251 141 : break;
252 : }
253 : }
254 141 : if (psThis == NULL)
255 0 : return NULL;
256 :
257 141 : return GMLParseFeatureType(psSchemaNode, pszName, psThis);
258 : }
259 :
260 :
261 : static
262 235 : GMLFeatureClass* GMLParseFeatureType(CPLXMLNode *psSchemaNode,
263 : const char* pszName,
264 : CPLXMLNode *psComplexType)
265 : {
266 :
267 : /* -------------------------------------------------------------------- */
268 : /* Grab the sequence of extensions greatgrandchild. */
269 : /* -------------------------------------------------------------------- */
270 : CPLXMLNode *psAttrSeq =
271 : CPLGetXMLNode( psComplexType,
272 235 : "complexContent.extension.sequence" );
273 :
274 235 : if( psAttrSeq == NULL )
275 : {
276 0 : return NULL;
277 : }
278 :
279 : /* -------------------------------------------------------------------- */
280 : /* We are pretty sure this going to be a valid Feature class */
281 : /* now, so create it. */
282 : /* -------------------------------------------------------------------- */
283 235 : GMLFeatureClass *poClass = new GMLFeatureClass( pszName );
284 :
285 : /* -------------------------------------------------------------------- */
286 : /* Loop over each of the attribute elements being defined for */
287 : /* this feature class. */
288 : /* -------------------------------------------------------------------- */
289 : CPLXMLNode *psAttrDef;
290 235 : int nAttributeIndex = 0;
291 :
292 235 : int bGotUnrecognizedType = FALSE;
293 :
294 1433 : for( psAttrDef = psAttrSeq->psChild;
295 : psAttrDef != NULL;
296 : psAttrDef = psAttrDef->psNext )
297 : {
298 1198 : if( strcmp(psAttrDef->pszValue,"group") == 0 )
299 : {
300 : /* Too complex schema for us. Aborts parsing */
301 0 : delete poClass;
302 0 : return NULL;
303 : }
304 :
305 1198 : if( !EQUAL(psAttrDef->pszValue,"element") )
306 0 : continue;
307 :
308 : /* MapServer WFS writes element type as an attribute of element */
309 : /* not as a simpleType definition */
310 1198 : const char* pszType = CPLGetXMLValue( psAttrDef, "type", NULL );
311 1198 : const char* pszElementName = CPLGetXMLValue( psAttrDef, "name", NULL );
312 1198 : if (pszType != NULL)
313 : {
314 1045 : const char* pszStrippedNSType = StripNS(pszType);
315 1045 : int nWidth = 0, nPrecision = 0;
316 :
317 1045 : GMLPropertyType gmlType = GMLPT_Untyped;
318 1349 : if (EQUAL(pszStrippedNSType, "string") ||
319 : EQUAL(pszStrippedNSType, "Character"))
320 304 : gmlType = GMLPT_String;
321 : /* TODO: Would be nice to have a proper date type */
322 741 : else if (EQUAL(pszStrippedNSType, "date") ||
323 : EQUAL(pszStrippedNSType, "dateTime"))
324 0 : gmlType = GMLPT_String;
325 1134 : else if (EQUAL(pszStrippedNSType, "real") ||
326 : EQUAL(pszStrippedNSType, "double") ||
327 : EQUAL(pszStrippedNSType, "float") ||
328 : EQUAL(pszStrippedNSType, "decimal"))
329 393 : gmlType = GMLPT_Real;
330 475 : else if (EQUAL(pszStrippedNSType, "short") ||
331 : EQUAL(pszStrippedNSType, "int") ||
332 : EQUAL(pszStrippedNSType, "integer") ||
333 : EQUAL(pszStrippedNSType, "long"))
334 127 : gmlType = GMLPT_Integer;
335 221 : else if (strncmp(pszType, "gml:", 4) == 0)
336 : {
337 220 : const AssocNameType* psIter = apsPropertyTypes;
338 1506 : while(psIter->pszName)
339 : {
340 1286 : if (strncmp(pszType + 4, psIter->pszName, strlen(psIter->pszName)) == 0)
341 : {
342 220 : if (poClass->GetGeometryAttributeIndex() != -1)
343 : {
344 0 : CPLDebug("GML", "Geometry field already found ! Ignoring the following ones");
345 : }
346 : else
347 : {
348 220 : poClass->SetGeometryElement(pszElementName);
349 220 : poClass->SetGeometryType(psIter->eType);
350 220 : poClass->SetGeometryAttributeIndex( nAttributeIndex );
351 :
352 220 : nAttributeIndex ++;
353 : }
354 :
355 220 : break;
356 : }
357 :
358 1066 : psIter ++;
359 : }
360 :
361 220 : if (psIter->pszName == NULL)
362 : {
363 : /* Can be a non geometry gml type */
364 : /* Too complex schema for us. Aborts parsing */
365 0 : delete poClass;
366 0 : return NULL;
367 : }
368 :
369 220 : if (poClass->GetGeometryAttributeIndex() == -1)
370 0 : bGotUnrecognizedType = TRUE;
371 :
372 220 : continue;
373 : }
374 :
375 : /* Integraph stuff */
376 1 : else if (strcmp(pszType, "G:Point_MultiPointPropertyType") == 0 ||
377 : strcmp(pszType, "gmgml:Point_MultiPointPropertyType") == 0)
378 : {
379 0 : poClass->SetGeometryElement(pszElementName);
380 0 : poClass->SetGeometryType(wkbMultiPoint);
381 0 : poClass->SetGeometryAttributeIndex( nAttributeIndex );
382 :
383 0 : nAttributeIndex ++;
384 0 : continue;
385 : }
386 1 : else if (strcmp(pszType, "G:LineString_MultiLineStringPropertyType") == 0 ||
387 : strcmp(pszType, "gmgml:LineString_MultiLineStringPropertyType") == 0)
388 : {
389 0 : poClass->SetGeometryElement(pszElementName);
390 0 : poClass->SetGeometryType(wkbMultiLineString);
391 0 : poClass->SetGeometryAttributeIndex( nAttributeIndex );
392 :
393 0 : nAttributeIndex ++;
394 0 : continue;
395 : }
396 1 : else if (strcmp(pszType, "G:Polygon_MultiPolygonPropertyType") == 0 ||
397 : strcmp(pszType, "gmgml:Polygon_MultiPolygonPropertyType") == 0 ||
398 : strcmp(pszType, "gmgml:Polygon_Surface_MultiSurface_CompositeSurfacePropertyType") == 0)
399 : {
400 0 : poClass->SetGeometryElement(pszElementName);
401 0 : poClass->SetGeometryType(wkbMultiPolygon);
402 0 : poClass->SetGeometryAttributeIndex( nAttributeIndex );
403 :
404 0 : nAttributeIndex ++;
405 0 : continue;
406 : }
407 :
408 : /* ERDAS Apollo stuff (like in http://apollo.erdas.com/erdas-apollo/vector/WORLDWIDE?SERVICE=WFS&VERSION=1.0.0&REQUEST=DescribeFeatureType&TYPENAME=wfs:cntry98) */
409 1 : else if (strcmp(pszType, "wfs:MixedPolygonPropertyType") == 0)
410 : {
411 0 : poClass->SetGeometryElement(pszElementName);
412 0 : poClass->SetGeometryType(wkbMultiPolygon);
413 0 : poClass->SetGeometryAttributeIndex( nAttributeIndex );
414 :
415 0 : nAttributeIndex ++;
416 0 : continue;
417 : }
418 :
419 : else
420 : {
421 1 : gmlType = GMLPT_Untyped;
422 1 : if ( ! LookForSimpleType(psSchemaNode, pszStrippedNSType,
423 : &gmlType, &nWidth, &nPrecision) )
424 : {
425 : /* Too complex schema for us. Aborts parsing */
426 0 : delete poClass;
427 0 : return NULL;
428 : }
429 : }
430 :
431 825 : if (pszElementName == NULL)
432 0 : pszElementName = "unnamed";
433 : GMLPropertyDefn *poProp = new GMLPropertyDefn(
434 825 : pszElementName, pszElementName );
435 :
436 825 : poProp->SetType( gmlType );
437 825 : poProp->SetAttributeIndex( nAttributeIndex );
438 825 : poProp->SetWidth( nWidth );
439 825 : poProp->SetPrecision( nPrecision );
440 :
441 825 : if (poClass->AddProperty( poProp ) < 0)
442 0 : delete poProp;
443 : else
444 825 : nAttributeIndex ++;
445 :
446 825 : continue;
447 : }
448 :
449 : // For now we skip geometries .. fixup later.
450 153 : CPLXMLNode* psSimpleType = CPLGetXMLNode( psAttrDef, "simpleType" );
451 153 : if( psSimpleType == NULL )
452 : {
453 19 : const char* pszRef = CPLGetXMLValue( psAttrDef, "ref", NULL );
454 :
455 : /* FME .xsd */
456 19 : if (pszRef != NULL && strncmp(pszRef, "gml:", 4) == 0)
457 : {
458 18 : const AssocNameType* psIter = apsRefTypes;
459 81 : while(psIter->pszName)
460 : {
461 63 : if (strncmp(pszRef + 4, psIter->pszName, strlen(psIter->pszName)) == 0)
462 : {
463 18 : if (poClass->GetGeometryAttributeIndex() != -1)
464 : {
465 9 : OGRwkbGeometryType eNewType = psIter->eType;
466 9 : OGRwkbGeometryType eOldType = (OGRwkbGeometryType)poClass->GetGeometryType();
467 18 : if ((eNewType == wkbMultiPoint && eOldType == wkbPoint) ||
468 : (eNewType == wkbMultiLineString && eOldType == wkbLineString) ||
469 : (eNewType == wkbMultiPolygon && eOldType == wkbPolygon))
470 : {
471 9 : poClass->SetGeometryType(eNewType);
472 : }
473 : else
474 : {
475 0 : CPLDebug("GML", "Geometry field already found ! Ignoring the following ones");
476 : }
477 : }
478 : else
479 : {
480 9 : poClass->SetGeometryElement(pszElementName);
481 9 : poClass->SetGeometryType(psIter->eType);
482 9 : poClass->SetGeometryAttributeIndex( nAttributeIndex );
483 :
484 9 : nAttributeIndex ++;
485 : }
486 :
487 18 : break;
488 : }
489 :
490 45 : psIter ++;
491 : }
492 :
493 18 : if (psIter->pszName == NULL)
494 : {
495 : /* Can be a non geometry gml type */
496 : /* Too complex schema for us. Aborts parsing */
497 0 : delete poClass;
498 0 : return NULL;
499 : }
500 :
501 18 : if (poClass->GetGeometryAttributeIndex() == -1)
502 0 : bGotUnrecognizedType = TRUE;
503 :
504 18 : continue;
505 : }
506 :
507 : /* 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 :
508 : <xs:element name="Obj" minOccurs="0" maxOccurs="1">
509 : <xs:complexType>
510 : <xs:sequence>
511 : <xs:element ref="gml:_Geometry"/>
512 : </xs:sequence>
513 : </xs:complexType>
514 : </xs:element>
515 : */
516 1 : CPLXMLNode* psComplexType = GetSingleChildElement( psAttrDef, "complexType" );
517 1 : CPLXMLNode* psComplexTypeSequence = GetSingleChildElement( psComplexType, "sequence" );
518 1 : CPLXMLNode* psComplexTypeSequenceElement = GetSingleChildElement( psComplexTypeSequence, "element" );
519 :
520 1 : if( pszElementName != NULL &&
521 : CheckMinMaxOccursCardinality(psAttrDef) &&
522 : psComplexTypeSequenceElement != NULL &&
523 : CheckMinMaxOccursCardinality(psComplexTypeSequence) &&
524 : strcmp(CPLGetXMLValue( psComplexTypeSequenceElement, "ref", "" ), "gml:_Geometry") == 0 )
525 : {
526 1 : poClass->SetGeometryElement(pszElementName);
527 1 : poClass->SetGeometryType(wkbUnknown);
528 1 : poClass->SetGeometryAttributeIndex( nAttributeIndex );
529 :
530 1 : nAttributeIndex ++;
531 :
532 1 : continue;
533 : }
534 : else
535 : {
536 : /* Too complex schema for us. Aborts parsing */
537 0 : delete poClass;
538 0 : return NULL;
539 : }
540 : }
541 :
542 134 : if (pszElementName == NULL)
543 0 : pszElementName = "unnamed";
544 : GMLPropertyDefn *poProp = new GMLPropertyDefn(
545 134 : pszElementName, pszElementName );
546 :
547 134 : GMLPropertyType eType = GMLPT_Untyped;
548 134 : int nWidth = 0, nPrecision = 0;
549 134 : GetSimpleTypeProperties(psSimpleType, &eType, &nWidth, &nPrecision);
550 134 : poProp->SetType( eType );
551 134 : poProp->SetWidth( nWidth );
552 134 : poProp->SetPrecision( nPrecision );
553 134 : poProp->SetAttributeIndex( nAttributeIndex );
554 :
555 134 : if (poClass->AddProperty( poProp ) < 0)
556 0 : delete poProp;
557 : else
558 134 : nAttributeIndex ++;
559 : }
560 :
561 : /* Only report wkbNone if we didn't find a known geometry type */
562 : /* and there were not any unknown types (in case this unknown type */
563 : /* would be a geometry type) */
564 235 : if (poClass->GetGeometryAttributeIndex() == -1 &&
565 : !bGotUnrecognizedType)
566 : {
567 5 : poClass->SetGeometryType(wkbNone);
568 : }
569 :
570 : /* -------------------------------------------------------------------- */
571 : /* Class complete, add to reader class list. */
572 : /* -------------------------------------------------------------------- */
573 235 : poClass->SetSchemaLocked( TRUE );
574 :
575 235 : return poClass;
576 : }
577 :
578 : /************************************************************************/
579 : /* GMLParseXSD() */
580 : /************************************************************************/
581 :
582 177 : int GMLParseXSD( const char *pszFile,
583 : std::vector<GMLFeatureClass*> & aosClasses)
584 :
585 : {
586 177 : if( pszFile == NULL )
587 0 : return FALSE;
588 :
589 : /* -------------------------------------------------------------------- */
590 : /* Load the raw XML file. */
591 : /* -------------------------------------------------------------------- */
592 177 : CPLXMLNode *psXSDTree = CPLParseXMLFile( pszFile );
593 :
594 177 : if( psXSDTree == NULL )
595 0 : return FALSE;
596 :
597 : /* -------------------------------------------------------------------- */
598 : /* Strip off any namespace qualifiers. */
599 : /* -------------------------------------------------------------------- */
600 177 : CPLStripXMLNamespace( psXSDTree, NULL, TRUE );
601 :
602 : /* -------------------------------------------------------------------- */
603 : /* Find <schema> root element. */
604 : /* -------------------------------------------------------------------- */
605 177 : CPLXMLNode *psSchemaNode = CPLGetXMLNode( psXSDTree, "=schema" );
606 177 : if( psSchemaNode == NULL )
607 : {
608 0 : CPLDestroyXMLNode( psXSDTree );
609 0 : return FALSE;
610 : }
611 :
612 : /* ==================================================================== */
613 : /* Process each feature class definition. */
614 : /* ==================================================================== */
615 : CPLXMLNode *psThis;
616 :
617 2566 : for( psThis = psSchemaNode->psChild;
618 : psThis != NULL; psThis = psThis->psNext )
619 : {
620 : /* -------------------------------------------------------------------- */
621 : /* Check for <xs:element> node. */
622 : /* -------------------------------------------------------------------- */
623 2389 : if( psThis->eType != CXT_Element
624 : || !EQUAL(psThis->pszValue,"element") )
625 2101 : continue;
626 :
627 : /* -------------------------------------------------------------------- */
628 : /* Check the substitution group. */
629 : /* -------------------------------------------------------------------- */
630 : const char *pszSubGroup =
631 288 : StripNS(CPLGetXMLValue(psThis,"substitutionGroup",""));
632 :
633 : // Old OGR produced elements for the feature collection.
634 288 : if( EQUAL(pszSubGroup, "_FeatureCollection") )
635 29 : continue;
636 :
637 259 : if( !EQUAL(pszSubGroup, "_Feature") &&
638 : !EQUAL(pszSubGroup, "AbstractFeature") /* AbstractFeature used by GML 3.2 */ )
639 : {
640 21 : continue;
641 : }
642 :
643 : /* -------------------------------------------------------------------- */
644 : /* Get name */
645 : /* -------------------------------------------------------------------- */
646 : const char *pszName;
647 :
648 238 : pszName = CPLGetXMLValue( psThis, "name", NULL );
649 238 : if( pszName == NULL )
650 : {
651 0 : continue;
652 : }
653 :
654 : /* -------------------------------------------------------------------- */
655 : /* Get type and verify relationship with name. */
656 : /* -------------------------------------------------------------------- */
657 : const char *pszType;
658 :
659 238 : pszType = CPLGetXMLValue( psThis, "type", NULL );
660 238 : if (pszType == NULL)
661 : {
662 94 : CPLXMLNode *psComplexType = CPLGetXMLNode( psThis, "complexType" );
663 94 : if (psComplexType)
664 : {
665 : GMLFeatureClass* poClass =
666 94 : GMLParseFeatureType(psSchemaNode, pszName, psComplexType);
667 94 : if (poClass)
668 94 : aosClasses.push_back(poClass);
669 : }
670 94 : continue;
671 : }
672 144 : if( strstr( pszType, ":" ) != NULL )
673 144 : pszType = strstr( pszType, ":" ) + 1;
674 144 : if( EQUAL(pszType, pszName) )
675 : {
676 : /* A few WFS servers return a type name which is the element name */
677 : /* without any _Type or Type suffix */
678 : /* e.g. : http://apollo.erdas.com/erdas-apollo/vector/Cherokee?SERVICE=WFS&VERSION=1.0.0&REQUEST=DescribeFeatureType&TYPENAME=iwfs:Air */
679 : }
680 144 : else if( !EQUALN(pszType,pszName,strlen(pszName))
681 : || !(EQUAL(pszType+strlen(pszName),"_Type") ||
682 : EQUAL(pszType+strlen(pszName),"Type")) )
683 : {
684 0 : continue;
685 : }
686 :
687 : /* CanVec .xsd contains weird types that are not used in the related GML */
688 144 : if (strncmp(pszName, "XyZz", 4) == 0 ||
689 : strncmp(pszName, "XyZ1", 4) == 0 ||
690 : strncmp(pszName, "XyZ2", 4) == 0)
691 3 : continue;
692 :
693 : GMLFeatureClass* poClass =
694 141 : GMLParseFeatureType(psSchemaNode, pszName, pszType);
695 141 : if (poClass)
696 141 : aosClasses.push_back(poClass);
697 : }
698 :
699 177 : CPLDestroyXMLNode( psXSDTree );
700 :
701 177 : if( aosClasses.size() > 0 )
702 : {
703 171 : return TRUE;
704 : }
705 : else
706 6 : return FALSE;
707 : }
|