1 : /******************************************************************************
2 : * $Id: gmlreader.cpp 20029 2010-07-11 18:38:23Z rouault $
3 : *
4 : * Project: GML Reader
5 : * Purpose: Implementation of GMLReader class.
6 : * Author: Frank Warmerdam, warmerdam@pobox.com
7 : *
8 : ******************************************************************************
9 : * Copyright (c) 2002, 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 "gmlreader.h"
31 : #include "cpl_error.h"
32 : #include "cpl_string.h"
33 :
34 : #define SUPPORT_GEOMETRY
35 :
36 : #ifdef SUPPORT_GEOMETRY
37 : # include "ogr_geometry.h"
38 : #endif
39 :
40 : /************************************************************************/
41 : /* ~IGMLReader() */
42 : /************************************************************************/
43 :
44 21 : IGMLReader::~IGMLReader()
45 :
46 : {
47 21 : }
48 :
49 : /************************************************************************/
50 : /* ==================================================================== */
51 : /* No XERCES or EXPAT Library */
52 : /* ==================================================================== */
53 : /************************************************************************/
54 : #if HAVE_XERCES == 0 && !defined(HAVE_EXPAT)
55 :
56 : /************************************************************************/
57 : /* CreateGMLReader() */
58 : /************************************************************************/
59 :
60 : IGMLReader *CreateGMLReader()
61 :
62 : {
63 : CPLError( CE_Failure, CPLE_AppDefined,
64 : "Unable to create Xerces C++ or Expat based GML reader, Xerces or Expat support\n"
65 : "not configured into GDAL/OGR." );
66 : return NULL;
67 : }
68 :
69 : /************************************************************************/
70 : /* ==================================================================== */
71 : /* With XERCES or EXPAT Library */
72 : /* ==================================================================== */
73 : /************************************************************************/
74 : #else /* HAVE_XERCES == 1 or HAVE_EXPAT */
75 :
76 : #include "gmlreaderp.h"
77 : #include "cpl_conv.h"
78 :
79 : /************************************************************************/
80 : /* CreateGMLReader() */
81 : /************************************************************************/
82 :
83 20 : IGMLReader *CreateGMLReader()
84 :
85 : {
86 20 : return new GMLReader();
87 : }
88 :
89 : int GMLReader::m_bXercesInitialized = FALSE;
90 : int GMLReader::m_nInstanceCount = 0;
91 :
92 : /************************************************************************/
93 : /* GMLReader() */
94 : /************************************************************************/
95 :
96 20 : GMLReader::GMLReader()
97 :
98 : {
99 20 : m_nInstanceCount++;
100 20 : m_nClassCount = 0;
101 20 : m_papoClass = NULL;
102 :
103 20 : m_bClassListLocked = FALSE;
104 :
105 20 : m_poGMLHandler = NULL;
106 : #if HAVE_XERCES == 1
107 20 : m_poSAXReader = NULL;
108 20 : m_poCompleteFeature = NULL;
109 20 : m_GMLInputSource = NULL;
110 : #else
111 : oParser = NULL;
112 : ppoFeatureTab = NULL;
113 : nFeatureTabIndex = 0;
114 : nFeatureTabLength = 0;
115 : #endif
116 20 : fpGML = NULL;
117 20 : m_bReadStarted = FALSE;
118 :
119 20 : m_poState = NULL;
120 :
121 20 : m_pszFilename = NULL;
122 :
123 20 : m_bStopParsing = FALSE;
124 20 : }
125 :
126 : /************************************************************************/
127 : /* ~GMLReader() */
128 : /************************************************************************/
129 :
130 20 : GMLReader::~GMLReader()
131 :
132 : {
133 20 : ClearClasses();
134 :
135 20 : CPLFree( m_pszFilename );
136 :
137 20 : CleanupParser();
138 :
139 20 : --m_nInstanceCount;
140 : #if HAVE_XERCES == 1
141 20 : if( m_nInstanceCount == 0 && m_bXercesInitialized )
142 : {
143 16 : XMLPlatformUtils::Terminate();
144 16 : m_bXercesInitialized = FALSE;
145 : }
146 : #endif
147 :
148 20 : if (fpGML)
149 19 : VSIFCloseL(fpGML);
150 20 : fpGML = NULL;
151 20 : }
152 :
153 : /************************************************************************/
154 : /* SetSourceFile() */
155 : /************************************************************************/
156 :
157 20 : void GMLReader::SetSourceFile( const char *pszFilename )
158 :
159 : {
160 20 : CPLFree( m_pszFilename );
161 20 : m_pszFilename = CPLStrdup( pszFilename );
162 20 : }
163 :
164 : /************************************************************************/
165 : /* GetSourceFileName() */
166 : /************************************************************************/
167 :
168 1 : const char* GMLReader::GetSourceFileName()
169 :
170 : {
171 1 : return m_pszFilename;
172 : }
173 :
174 : #ifdef HAVE_EXPAT
175 :
176 : static void XMLCALL startElementCbk(void *pUserData, const char *pszName,
177 : const char **ppszAttr)
178 : {
179 : ((GMLHandler*)pUserData)->startElement(pszName, ppszAttr);
180 : }
181 :
182 : static void XMLCALL endElementCbk(void *pUserData, const char *pszName)
183 : {
184 : ((GMLHandler*)pUserData)->endElement(pszName);
185 : }
186 :
187 : static void XMLCALL dataHandlerCbk(void *pUserData, const char *data, int nLen)
188 : {
189 : ((GMLHandler*)pUserData)->dataHandler(data, nLen);
190 : }
191 :
192 : #endif
193 :
194 : /************************************************************************/
195 : /* SetupParser() */
196 : /************************************************************************/
197 :
198 27 : int GMLReader::SetupParser()
199 :
200 : {
201 27 : if (fpGML == NULL)
202 19 : fpGML = VSIFOpenL(m_pszFilename, "rt");
203 27 : if (fpGML != NULL)
204 27 : VSIFSeekL( fpGML, 0, SEEK_SET );
205 :
206 : #if HAVE_XERCES == 1
207 :
208 27 : if( !m_bXercesInitialized )
209 : {
210 : try
211 : {
212 16 : XMLPlatformUtils::Initialize();
213 : }
214 :
215 0 : catch (const XMLException& toCatch)
216 : {
217 : CPLError( CE_Warning, CPLE_AppDefined,
218 : "Exception initializing Xerces based GML reader.\n%s",
219 0 : tr_strdup(toCatch.getMessage()) );
220 0 : return FALSE;
221 : }
222 16 : m_bXercesInitialized = TRUE;
223 : }
224 :
225 : // Cleanup any old parser.
226 27 : if( m_poSAXReader != NULL )
227 0 : CleanupParser();
228 :
229 : // Create and initialize parser.
230 27 : XMLCh* xmlUriValid = NULL;
231 27 : XMLCh* xmlUriNS = NULL;
232 :
233 : try{
234 27 : m_poSAXReader = XMLReaderFactory::createXMLReader();
235 :
236 27 : m_poGMLHandler = new GMLXercesHandler( this );
237 :
238 54 : m_poSAXReader->setContentHandler( m_poGMLHandler );
239 27 : m_poSAXReader->setErrorHandler( m_poGMLHandler );
240 27 : m_poSAXReader->setLexicalHandler( m_poGMLHandler );
241 27 : m_poSAXReader->setEntityResolver( m_poGMLHandler );
242 27 : m_poSAXReader->setDTDHandler( m_poGMLHandler );
243 :
244 27 : xmlUriValid = XMLString::transcode("http://xml.org/sax/features/validation");
245 27 : xmlUriNS = XMLString::transcode("http://xml.org/sax/features/namespaces");
246 :
247 : #if (OGR_GML_VALIDATION)
248 : m_poSAXReader->setFeature( xmlUriValid, true);
249 : m_poSAXReader->setFeature( xmlUriNS, true);
250 :
251 : m_poSAXReader->setFeature( XMLUni::fgSAX2CoreNameSpaces, true );
252 : m_poSAXReader->setFeature( XMLUni::fgXercesSchema, true );
253 :
254 : // m_poSAXReader->setDoSchema(true);
255 : // m_poSAXReader->setValidationSchemaFullChecking(true);
256 : #else
257 27 : m_poSAXReader->setFeature( XMLUni::fgSAX2CoreValidation, false);
258 :
259 : #if XERCES_VERSION_MAJOR >= 3
260 : m_poSAXReader->setFeature( XMLUni::fgXercesSchema, false);
261 : #else
262 27 : m_poSAXReader->setFeature( XMLUni::fgSAX2CoreNameSpaces, false);
263 : #endif
264 :
265 : #endif
266 27 : XMLString::release( &xmlUriValid );
267 27 : XMLString::release( &xmlUriNS );
268 : }
269 0 : catch (...)
270 : {
271 0 : XMLString::release( &xmlUriValid );
272 0 : XMLString::release( &xmlUriNS );
273 :
274 : CPLError( CE_Warning, CPLE_AppDefined,
275 0 : "Exception initializing Xerces based GML reader.\n" );
276 0 : return FALSE;
277 : }
278 :
279 54 : if (m_GMLInputSource == NULL && fpGML != NULL)
280 27 : m_GMLInputSource = new GMLInputSource(fpGML);
281 : #else
282 : // Cleanup any old parser.
283 : if( oParser != NULL )
284 : CleanupParser();
285 :
286 : oParser = OGRCreateExpatXMLParser();
287 : m_poGMLHandler = new GMLExpatHandler( this, oParser );
288 :
289 : XML_SetElementHandler(oParser, ::startElementCbk, ::endElementCbk);
290 : XML_SetCharacterDataHandler(oParser, ::dataHandlerCbk);
291 : XML_SetUserData(oParser, m_poGMLHandler);
292 : #endif
293 :
294 27 : m_bReadStarted = FALSE;
295 :
296 : // Push an empty state.
297 27 : PushState( new GMLReadState() );
298 :
299 27 : return TRUE;
300 : }
301 :
302 : /************************************************************************/
303 : /* CleanupParser() */
304 : /************************************************************************/
305 :
306 50 : void GMLReader::CleanupParser()
307 :
308 : {
309 : #if HAVE_XERCES == 1
310 50 : if( m_poSAXReader == NULL )
311 23 : return;
312 : #else
313 : if (oParser == NULL )
314 : return;
315 : #endif
316 :
317 81 : while( m_poState )
318 27 : PopState();
319 :
320 : #if HAVE_XERCES == 1
321 27 : delete m_poSAXReader;
322 27 : m_poSAXReader = NULL;
323 27 : delete m_GMLInputSource;
324 27 : m_GMLInputSource = NULL;
325 : #else
326 : if (oParser)
327 : XML_ParserFree(oParser);
328 : oParser = NULL;
329 :
330 : int i;
331 : for(i=nFeatureTabIndex;i<nFeatureTabLength;i++)
332 : delete ppoFeatureTab[i];
333 : CPLFree(ppoFeatureTab);
334 : nFeatureTabIndex = 0;
335 : nFeatureTabLength = 0;
336 : ppoFeatureTab = NULL;
337 :
338 : #endif
339 :
340 27 : delete m_poGMLHandler;
341 27 : m_poGMLHandler = NULL;
342 :
343 27 : m_bReadStarted = FALSE;
344 : }
345 :
346 : #if HAVE_XERCES == 1
347 :
348 27 : GMLBinInputStream::GMLBinInputStream(FILE* fp)
349 : {
350 27 : this->fp = fp;
351 27 : emptyString = 0;
352 27 : }
353 :
354 27 : GMLBinInputStream::~ GMLBinInputStream()
355 : {
356 27 : }
357 :
358 : #if XERCES_VERSION_MAJOR >= 3
359 : XMLFilePos GMLBinInputStream::curPos() const
360 : {
361 : return (XMLFilePos)VSIFTellL(fp);
362 : }
363 :
364 : XMLSize_t GMLBinInputStream::readBytes(XMLByte* const toFill, const XMLSize_t maxToRead)
365 : {
366 : return (XMLSize_t)VSIFReadL(toFill, 1, maxToRead, fp);
367 : }
368 :
369 : const XMLCh* GMLBinInputStream::getContentType() const
370 : {
371 : return &emptyString;
372 : }
373 : #else
374 0 : unsigned int GMLBinInputStream::curPos() const
375 : {
376 0 : return (unsigned int)VSIFTellL(fp);
377 : }
378 :
379 48 : unsigned int GMLBinInputStream::readBytes(XMLByte* const toFill, const unsigned int maxToRead)
380 : {
381 48 : return (unsigned int)VSIFReadL(toFill, 1, maxToRead, fp);
382 : }
383 : #endif
384 :
385 27 : GMLInputSource::GMLInputSource(FILE* fp, MemoryManager* const manager) : InputSource(manager)
386 : {
387 27 : binInputStream = new GMLBinInputStream(fp);
388 27 : }
389 :
390 27 : GMLInputSource::~GMLInputSource()
391 : {
392 27 : }
393 :
394 27 : BinInputStream* GMLInputSource::makeStream() const
395 : {
396 27 : return binInputStream;
397 : }
398 :
399 : #endif
400 :
401 : /************************************************************************/
402 : /* NextFeature() */
403 : /************************************************************************/
404 :
405 : #if HAVE_XERCES == 1
406 81 : GMLFeature *GMLReader::NextFeature()
407 :
408 : {
409 81 : GMLFeature *poReturn = NULL;
410 :
411 : try
412 : {
413 81 : if( !m_bReadStarted )
414 : {
415 27 : if( m_poSAXReader == NULL )
416 15 : SetupParser();
417 :
418 27 : m_bReadStarted = TRUE;
419 :
420 27 : if (m_GMLInputSource == NULL)
421 0 : return NULL;
422 :
423 27 : if( !m_poSAXReader->parseFirst( *m_GMLInputSource, m_oToFill ) )
424 0 : return NULL;
425 : }
426 :
427 3154 : while( m_poCompleteFeature == NULL
428 : && !m_bStopParsing
429 : && m_poSAXReader->parseNext( m_oToFill ) ) {}
430 :
431 81 : poReturn = m_poCompleteFeature;
432 81 : m_poCompleteFeature = NULL;
433 :
434 : }
435 0 : catch (const XMLException& toCatch)
436 : {
437 0 : char *pszErrorMessage = tr_strdup( toCatch.getMessage() );
438 : CPLDebug( "GML",
439 : "Error during NextFeature()! Message:\n%s",
440 0 : pszErrorMessage );
441 0 : CPLFree(pszErrorMessage);
442 0 : m_bStopParsing = TRUE;
443 : }
444 0 : catch (const SAXException& toCatch)
445 : {
446 0 : char *pszErrorMessage = tr_strdup( toCatch.getMessage() );
447 0 : CPLError(CE_Failure, CPLE_AppDefined, "%s", pszErrorMessage);
448 0 : CPLFree(pszErrorMessage);
449 0 : m_bStopParsing = TRUE;
450 : }
451 :
452 81 : return poReturn;
453 : }
454 : #else
455 : GMLFeature *GMLReader::NextFeature()
456 :
457 : {
458 : if (!m_bReadStarted)
459 : {
460 : if (oParser == NULL)
461 : SetupParser();
462 :
463 : m_bReadStarted = TRUE;
464 : }
465 :
466 : if (fpGML == NULL || m_bStopParsing)
467 : return NULL;
468 :
469 : if (nFeatureTabIndex < nFeatureTabLength)
470 : {
471 : return ppoFeatureTab[nFeatureTabIndex++];
472 : }
473 :
474 : if (VSIFEofL(fpGML))
475 : return NULL;
476 :
477 : char aBuf[BUFSIZ];
478 :
479 : CPLFree(ppoFeatureTab);
480 : ppoFeatureTab = NULL;
481 : nFeatureTabLength = 0;
482 : nFeatureTabIndex = 0;
483 :
484 : int nDone;
485 : do
486 : {
487 : m_poGMLHandler->ResetDataHandlerCounter();
488 :
489 : unsigned int nLen =
490 : (unsigned int)VSIFReadL( aBuf, 1, sizeof(aBuf), fpGML );
491 : nDone = VSIFEofL(fpGML);
492 : if (XML_Parse(oParser, aBuf, nLen, nDone) == XML_STATUS_ERROR)
493 : {
494 : CPLError(CE_Failure, CPLE_AppDefined,
495 : "XML parsing of GML file failed : %s "
496 : "at line %d, column %d",
497 : XML_ErrorString(XML_GetErrorCode(oParser)),
498 : (int)XML_GetCurrentLineNumber(oParser),
499 : (int)XML_GetCurrentColumnNumber(oParser));
500 : m_bStopParsing = TRUE;
501 : }
502 : if (!m_bStopParsing)
503 : m_bStopParsing = m_poGMLHandler->HasStoppedParsing();
504 :
505 : } while (!nDone && !m_bStopParsing && nFeatureTabLength == 0);
506 :
507 : return (nFeatureTabLength) ? ppoFeatureTab[nFeatureTabIndex++] : NULL;
508 : }
509 : #endif
510 :
511 : /************************************************************************/
512 : /* PushFeature() */
513 : /* */
514 : /* Create a feature based on the named element. If the */
515 : /* corresponding feature class doesn't exist yet, then create */
516 : /* it now. A new GMLReadState will be created for the feature, */
517 : /* and it will be placed within that state. The state is */
518 : /* pushed onto the readstate stack. */
519 : /************************************************************************/
520 :
521 : void GMLReader::PushFeature( const char *pszElement,
522 60 : const char *pszFID )
523 :
524 : {
525 : int iClass;
526 :
527 : /* -------------------------------------------------------------------- */
528 : /* Find the class of this element. */
529 : /* -------------------------------------------------------------------- */
530 62 : for( iClass = 0; iClass < GetClassCount(); iClass++ )
531 : {
532 53 : if( EQUAL(pszElement,GetClass(iClass)->GetElementName()) )
533 51 : break;
534 : }
535 :
536 : /* -------------------------------------------------------------------- */
537 : /* Create a new feature class for this element, if there is no */
538 : /* existing class for it. */
539 : /* -------------------------------------------------------------------- */
540 60 : if( iClass == GetClassCount() )
541 : {
542 9 : CPLAssert( !IsClassListLocked() );
543 :
544 9 : GMLFeatureClass *poNewClass = new GMLFeatureClass( pszElement );
545 :
546 9 : AddClass( poNewClass );
547 : }
548 :
549 : /* -------------------------------------------------------------------- */
550 : /* Create a feature of this feature class. Try to set the fid */
551 : /* if available. */
552 : /* -------------------------------------------------------------------- */
553 60 : GMLFeature *poFeature = new GMLFeature( GetClass( iClass ) );
554 60 : if( pszFID != NULL )
555 : {
556 54 : poFeature->SetFID( pszFID );
557 : }
558 :
559 : /* -------------------------------------------------------------------- */
560 : /* Create and push a new read state. */
561 : /* -------------------------------------------------------------------- */
562 : GMLReadState *poState;
563 :
564 60 : poState = new GMLReadState();
565 60 : poState->m_poFeature = poFeature;
566 60 : PushState( poState );
567 60 : }
568 :
569 : /************************************************************************/
570 : /* IsFeatureElement() */
571 : /* */
572 : /* Based on context and the element name, is this element a new */
573 : /* GML feature element? */
574 : /************************************************************************/
575 :
576 343 : int GMLReader::IsFeatureElement( const char *pszElement )
577 :
578 : {
579 343 : CPLAssert( m_poState != NULL );
580 :
581 343 : const char *pszLast = m_poState->GetLastComponent();
582 343 : int nLen = strlen(pszLast);
583 :
584 343 : if (strcmp(pszLast, "dane") == 0)
585 : {
586 : /* Polish TBD GML */
587 : }
588 343 : else if( nLen < 6 || !(EQUAL(pszLast+nLen-6,"member") ||
589 : EQUAL(pszLast+nLen-7,"members")) )
590 283 : return FALSE;
591 :
592 : // If the class list isn't locked, any element that is a featureMember
593 : // will do.
594 60 : if( !IsClassListLocked() )
595 49 : return TRUE;
596 :
597 : // otherwise, find a class with the desired element name.
598 11 : for( int i = 0; i < GetClassCount(); i++ )
599 : {
600 11 : if( EQUAL(pszElement,GetClass(i)->GetElementName()) )
601 11 : return TRUE;
602 : }
603 :
604 0 : return FALSE;
605 : }
606 :
607 : /************************************************************************/
608 : /* IsAttributeElement() */
609 : /************************************************************************/
610 :
611 574 : int GMLReader::IsAttributeElement( const char *pszElement )
612 :
613 : {
614 574 : if( m_poState->m_poFeature == NULL )
615 283 : return FALSE;
616 :
617 291 : GMLFeatureClass *poClass = m_poState->m_poFeature->GetClass();
618 :
619 : // If the schema is not yet locked, then any simple element
620 : // is potentially an attribute.
621 291 : if( !poClass->IsSchemaLocked() )
622 240 : return TRUE;
623 :
624 : // Otherwise build the path to this element into a single string
625 : // and compare against known attributes.
626 51 : CPLString osElemPath;
627 :
628 51 : if( m_poState->m_nPathLength == 0 )
629 45 : osElemPath = pszElement;
630 : else
631 : {
632 6 : osElemPath = m_poState->m_pszPath;
633 6 : osElemPath += "|";
634 6 : osElemPath += pszElement;
635 : }
636 :
637 150 : for( int i = 0; i < poClass->GetPropertyCount(); i++ )
638 133 : if( EQUAL(poClass->GetProperty(i)->GetSrcElement(),osElemPath) )
639 85 : return TRUE;
640 :
641 17 : return FALSE;
642 : }
643 :
644 : /************************************************************************/
645 : /* PopState() */
646 : /************************************************************************/
647 :
648 87 : void GMLReader::PopState()
649 :
650 : {
651 87 : if( m_poState != NULL )
652 : {
653 : #if HAVE_XERCES == 1
654 87 : if( m_poState->m_poFeature != NULL && m_poCompleteFeature == NULL )
655 : {
656 60 : m_poCompleteFeature = m_poState->m_poFeature;
657 60 : m_poState->m_poFeature = NULL;
658 : }
659 : #else
660 : if ( m_poState->m_poFeature != NULL )
661 : {
662 : ppoFeatureTab = (GMLFeature**)
663 : CPLRealloc(ppoFeatureTab,
664 : sizeof(GMLFeature*) * (nFeatureTabLength + 1));
665 : ppoFeatureTab[nFeatureTabLength] = m_poState->m_poFeature;
666 : nFeatureTabLength++;
667 :
668 : m_poState->m_poFeature = NULL;
669 : }
670 : #endif
671 :
672 : GMLReadState *poParent;
673 :
674 87 : poParent = m_poState->m_poParentState;
675 :
676 87 : delete m_poState;
677 87 : m_poState = poParent;
678 : }
679 87 : }
680 :
681 : /************************************************************************/
682 : /* PushState() */
683 : /************************************************************************/
684 :
685 87 : void GMLReader::PushState( GMLReadState *poState )
686 :
687 : {
688 87 : poState->m_poParentState = m_poState;
689 87 : m_poState = poState;
690 87 : }
691 :
692 : /************************************************************************/
693 : /* GetClass() */
694 : /************************************************************************/
695 :
696 186 : GMLFeatureClass *GMLReader::GetClass( int iClass ) const
697 :
698 : {
699 186 : if( iClass < 0 || iClass >= m_nClassCount )
700 0 : return NULL;
701 : else
702 186 : return m_papoClass[iClass];
703 : }
704 :
705 : /************************************************************************/
706 : /* GetClass() */
707 : /************************************************************************/
708 :
709 34 : GMLFeatureClass *GMLReader::GetClass( const char *pszName ) const
710 :
711 : {
712 36 : for( int iClass = 0; iClass < m_nClassCount; iClass++ )
713 : {
714 19 : if( EQUAL(GetClass(iClass)->GetName(),pszName) )
715 17 : return GetClass(iClass);
716 : }
717 :
718 17 : return NULL;
719 : }
720 :
721 : /************************************************************************/
722 : /* AddClass() */
723 : /************************************************************************/
724 :
725 17 : int GMLReader::AddClass( GMLFeatureClass *poNewClass )
726 :
727 : {
728 17 : CPLAssert( GetClass( poNewClass->GetName() ) == NULL );
729 :
730 17 : m_nClassCount++;
731 : m_papoClass = (GMLFeatureClass **)
732 17 : CPLRealloc( m_papoClass, sizeof(void*) * m_nClassCount );
733 17 : m_papoClass[m_nClassCount-1] = poNewClass;
734 :
735 17 : return m_nClassCount-1;
736 : }
737 :
738 : /************************************************************************/
739 : /* ClearClasses() */
740 : /************************************************************************/
741 :
742 32 : void GMLReader::ClearClasses()
743 :
744 : {
745 49 : for( int i = 0; i < m_nClassCount; i++ )
746 17 : delete m_papoClass[i];
747 32 : CPLFree( m_papoClass );
748 :
749 32 : m_nClassCount = 0;
750 32 : m_papoClass = NULL;
751 32 : }
752 :
753 : /************************************************************************/
754 : /* SetFeatureProperty() */
755 : /* */
756 : /* Set the property value on the current feature, adding the */
757 : /* property name to the GMLFeatureClass if required. */
758 : /* Eventually this function may also "refine" the property */
759 : /* type based on what is encountered. */
760 : /************************************************************************/
761 :
762 : void GMLReader::SetFeatureProperty( const char *pszElement,
763 218 : const char *pszValue )
764 :
765 : {
766 218 : GMLFeature *poFeature = GetState()->m_poFeature;
767 :
768 218 : CPLAssert( poFeature != NULL );
769 :
770 : /* -------------------------------------------------------------------- */
771 : /* Does this property exist in the feature class? If not, add */
772 : /* it. */
773 : /* -------------------------------------------------------------------- */
774 218 : GMLFeatureClass *poClass = poFeature->GetClass();
775 : int iProperty;
776 :
777 706 : for( iProperty=0; iProperty < poClass->GetPropertyCount(); iProperty++ )
778 : {
779 672 : if( EQUAL(poClass->GetProperty( iProperty )->GetSrcElement(),
780 : pszElement ) )
781 184 : break;
782 : }
783 :
784 218 : if( iProperty == poClass->GetPropertyCount() )
785 : {
786 34 : if( poClass->IsSchemaLocked() )
787 : {
788 0 : CPLDebug("GML","Encountered property missing from class schema.");
789 0 : return;
790 : }
791 :
792 34 : CPLString osFieldName;
793 :
794 34 : if( strchr(pszElement,'|') == NULL )
795 30 : osFieldName = pszElement;
796 : else
797 : {
798 4 : osFieldName = strrchr(pszElement,'|') + 1;
799 4 : if( poClass->GetPropertyIndex(osFieldName) != -1 )
800 0 : osFieldName = pszElement;
801 : }
802 :
803 : // Does this conflict with an existing property name?
804 68 : while( poClass->GetProperty(osFieldName) != NULL )
805 : {
806 0 : osFieldName += "_";
807 : }
808 :
809 34 : GMLPropertyDefn *poPDefn = new GMLPropertyDefn(osFieldName,pszElement);
810 :
811 68 : if( EQUAL(CPLGetConfigOption( "GML_FIELDTYPES", ""), "ALWAYS_STRING") )
812 0 : poPDefn->SetType( GMLPT_String );
813 :
814 34 : poClass->AddProperty( poPDefn );
815 : }
816 :
817 : /* -------------------------------------------------------------------- */
818 : /* Do we need to update the property type? */
819 : /* -------------------------------------------------------------------- */
820 218 : if( !poClass->IsSchemaLocked() )
821 : {
822 : poClass->GetProperty(iProperty)->AnalysePropertyValue(
823 : pszValue,
824 184 : poFeature->GetProperty(iProperty));
825 : }
826 :
827 : /* -------------------------------------------------------------------- */
828 : /* Set the property */
829 : /* -------------------------------------------------------------------- */
830 218 : switch( poClass->GetProperty( iProperty )->GetType() )
831 : {
832 : case GMLPT_StringList:
833 : case GMLPT_IntegerList:
834 : case GMLPT_RealList:
835 : {
836 0 : if( poFeature->GetProperty( iProperty ) != NULL )
837 : {
838 0 : CPLString osJoinedValue = poFeature->GetProperty( iProperty );
839 0 : osJoinedValue += ",";
840 0 : osJoinedValue += pszValue;
841 :
842 0 : poFeature->SetProperty( iProperty, osJoinedValue );
843 : }
844 : else
845 0 : poFeature->SetProperty( iProperty, pszValue );
846 : }
847 0 : break;
848 :
849 : default:
850 218 : poFeature->SetProperty( iProperty, pszValue );
851 : break;
852 : }
853 :
854 : }
855 :
856 : /************************************************************************/
857 : /* LoadClasses() */
858 : /************************************************************************/
859 :
860 5 : int GMLReader::LoadClasses( const char *pszFile )
861 :
862 : {
863 : // Add logic later to determine reasonable default schema file.
864 5 : if( pszFile == NULL )
865 0 : return FALSE;
866 :
867 : /* -------------------------------------------------------------------- */
868 : /* Load the raw XML file. */
869 : /* -------------------------------------------------------------------- */
870 : FILE *fp;
871 : int nLength;
872 : char *pszWholeText;
873 :
874 5 : fp = VSIFOpenL( pszFile, "rb" );
875 :
876 5 : if( fp == NULL )
877 : {
878 : CPLError( CE_Failure, CPLE_OpenFailed,
879 0 : "Failed to open file %s.", pszFile );
880 0 : return FALSE;
881 : }
882 :
883 5 : VSIFSeekL( fp, 0, SEEK_END );
884 5 : nLength = VSIFTellL( fp );
885 5 : VSIFSeekL( fp, 0, SEEK_SET );
886 :
887 5 : pszWholeText = (char *) VSIMalloc(nLength+1);
888 5 : if( pszWholeText == NULL )
889 : {
890 : CPLError( CE_Failure, CPLE_AppDefined,
891 : "Failed to allocate %d byte buffer for %s,\n"
892 : "is this really a GMLFeatureClassList file?",
893 0 : nLength, pszFile );
894 0 : VSIFCloseL( fp );
895 0 : return FALSE;
896 : }
897 :
898 5 : if( VSIFReadL( pszWholeText, nLength, 1, fp ) != 1 )
899 : {
900 0 : VSIFree( pszWholeText );
901 0 : VSIFCloseL( fp );
902 : CPLError( CE_Failure, CPLE_AppDefined,
903 0 : "Read failed on %s.", pszFile );
904 0 : return FALSE;
905 : }
906 5 : pszWholeText[nLength] = '\0';
907 :
908 5 : VSIFCloseL( fp );
909 :
910 5 : if( strstr( pszWholeText, "<GMLFeatureClassList>" ) == NULL )
911 : {
912 0 : VSIFree( pszWholeText );
913 : CPLError( CE_Failure, CPLE_AppDefined,
914 : "File %s does not contain a GMLFeatureClassList tree.",
915 0 : pszFile );
916 0 : return FALSE;
917 : }
918 :
919 : /* -------------------------------------------------------------------- */
920 : /* Convert to XML parse tree. */
921 : /* -------------------------------------------------------------------- */
922 : CPLXMLNode *psRoot;
923 :
924 5 : psRoot = CPLParseXMLString( pszWholeText );
925 5 : VSIFree( pszWholeText );
926 :
927 : // We assume parser will report errors via CPL.
928 5 : if( psRoot == NULL )
929 0 : return FALSE;
930 :
931 5 : if( psRoot->eType != CXT_Element
932 : || !EQUAL(psRoot->pszValue,"GMLFeatureClassList") )
933 : {
934 0 : CPLDestroyXMLNode(psRoot);
935 : CPLError( CE_Failure, CPLE_AppDefined,
936 : "File %s is not a GMLFeatureClassList document.",
937 0 : pszFile );
938 0 : return FALSE;
939 : }
940 :
941 : /* -------------------------------------------------------------------- */
942 : /* Extract feature classes for all definitions found. */
943 : /* -------------------------------------------------------------------- */
944 : CPLXMLNode *psThis;
945 :
946 10 : for( psThis = psRoot->psChild; psThis != NULL; psThis = psThis->psNext )
947 : {
948 5 : if( psThis->eType == CXT_Element
949 : && EQUAL(psThis->pszValue,"GMLFeatureClass") )
950 : {
951 : GMLFeatureClass *poClass;
952 :
953 5 : poClass = new GMLFeatureClass();
954 :
955 5 : if( !poClass->InitializeFromXML( psThis ) )
956 : {
957 0 : delete poClass;
958 0 : CPLDestroyXMLNode( psRoot );
959 0 : return FALSE;
960 : }
961 :
962 5 : poClass->SetSchemaLocked( TRUE );
963 :
964 5 : AddClass( poClass );
965 : }
966 : }
967 :
968 5 : CPLDestroyXMLNode( psRoot );
969 :
970 5 : SetClassListLocked( TRUE );
971 :
972 5 : return TRUE;
973 : }
974 :
975 : /************************************************************************/
976 : /* SaveClasses() */
977 : /************************************************************************/
978 :
979 8 : int GMLReader::SaveClasses( const char *pszFile )
980 :
981 : {
982 : // Add logic later to determine reasonable default schema file.
983 8 : if( pszFile == NULL )
984 0 : return FALSE;
985 :
986 : /* -------------------------------------------------------------------- */
987 : /* Create in memory schema tree. */
988 : /* -------------------------------------------------------------------- */
989 : CPLXMLNode *psRoot;
990 :
991 8 : psRoot = CPLCreateXMLNode( NULL, CXT_Element, "GMLFeatureClassList" );
992 :
993 17 : for( int iClass = 0; iClass < GetClassCount(); iClass++ )
994 : {
995 9 : GMLFeatureClass *poClass = GetClass( iClass );
996 :
997 9 : CPLAddXMLChild( psRoot, poClass->SerializeToXML() );
998 : }
999 :
1000 : /* -------------------------------------------------------------------- */
1001 : /* Serialize to disk. */
1002 : /* -------------------------------------------------------------------- */
1003 : FILE *fp;
1004 8 : int bSuccess = TRUE;
1005 8 : char *pszWholeText = CPLSerializeXMLTree( psRoot );
1006 :
1007 8 : CPLDestroyXMLNode( psRoot );
1008 :
1009 8 : fp = VSIFOpenL( pszFile, "wb" );
1010 :
1011 8 : if( fp == NULL )
1012 0 : bSuccess = FALSE;
1013 8 : else if( VSIFWriteL( pszWholeText, strlen(pszWholeText), 1, fp ) != 1 )
1014 0 : bSuccess = FALSE;
1015 : else
1016 8 : VSIFCloseL( fp );
1017 :
1018 8 : CPLFree( pszWholeText );
1019 :
1020 8 : return bSuccess;
1021 : }
1022 :
1023 : /************************************************************************/
1024 : /* PrescanForSchema() */
1025 : /* */
1026 : /* For now we use a pretty dumb approach of just doing a normal */
1027 : /* scan of the whole file, building up the schema information. */
1028 : /* Eventually we hope to do a more efficient scan when just */
1029 : /* looking for schema information. */
1030 : /************************************************************************/
1031 :
1032 12 : int GMLReader::PrescanForSchema( int bGetExtents )
1033 :
1034 : {
1035 : GMLFeature *poFeature;
1036 :
1037 12 : if( m_pszFilename == NULL )
1038 0 : return FALSE;
1039 :
1040 12 : SetClassListLocked( FALSE );
1041 :
1042 12 : ClearClasses();
1043 12 : if( !SetupParser() )
1044 0 : return FALSE;
1045 :
1046 49 : while( (poFeature = NextFeature()) != NULL )
1047 : {
1048 25 : GMLFeatureClass *poClass = poFeature->GetClass();
1049 :
1050 25 : if( poClass->GetFeatureCount() == -1 )
1051 9 : poClass->SetFeatureCount( 1 );
1052 : else
1053 16 : poClass->SetFeatureCount( poClass->GetFeatureCount() + 1 );
1054 :
1055 : #ifdef SUPPORT_GEOMETRY
1056 25 : if( bGetExtents )
1057 : {
1058 25 : OGRGeometry *poGeometry = NULL;
1059 :
1060 25 : if( poFeature->GetGeometry() != NULL
1061 : && strlen(poFeature->GetGeometry()) != 0 )
1062 : {
1063 : poGeometry = OGRGeometryFactory::createFromGML(
1064 25 : poFeature->GetGeometry() );
1065 : }
1066 :
1067 25 : if( poGeometry != NULL )
1068 : {
1069 : double dfXMin, dfXMax, dfYMin, dfYMax;
1070 25 : OGREnvelope sEnvelope;
1071 : OGRwkbGeometryType eGType = (OGRwkbGeometryType)
1072 25 : poClass->GetGeometryType();
1073 :
1074 : // Merge geometry type into layer.
1075 25 : if( poClass->GetFeatureCount() == 1 && eGType == wkbUnknown )
1076 9 : eGType = wkbNone;
1077 :
1078 : poClass->SetGeometryType(
1079 : (int) OGRMergeGeometryTypes(
1080 25 : eGType, poGeometry->getGeometryType() ) );
1081 :
1082 : // merge extents.
1083 25 : if (!poGeometry->IsEmpty())
1084 : {
1085 25 : poGeometry->getEnvelope( &sEnvelope );
1086 25 : if( poClass->GetExtents(&dfXMin, &dfXMax, &dfYMin, &dfYMax) )
1087 : {
1088 16 : dfXMin = MIN(dfXMin,sEnvelope.MinX);
1089 16 : dfXMax = MAX(dfXMax,sEnvelope.MaxX);
1090 16 : dfYMin = MIN(dfYMin,sEnvelope.MinY);
1091 16 : dfYMax = MAX(dfYMax,sEnvelope.MaxY);
1092 : }
1093 : else
1094 : {
1095 9 : dfXMin = sEnvelope.MinX;
1096 9 : dfXMax = sEnvelope.MaxX;
1097 9 : dfYMin = sEnvelope.MinY;
1098 9 : dfYMax = sEnvelope.MaxY;
1099 : }
1100 :
1101 25 : poClass->SetExtents( dfXMin, dfXMax, dfYMin, dfYMax );
1102 : }
1103 25 : delete poGeometry;
1104 :
1105 : }
1106 : #endif /* def SUPPORT_GEOMETRY */
1107 : }
1108 :
1109 25 : delete poFeature;
1110 : }
1111 :
1112 12 : CleanupParser();
1113 :
1114 12 : return GetClassCount() > 0;
1115 : }
1116 :
1117 : /************************************************************************/
1118 : /* ResetReading() */
1119 : /************************************************************************/
1120 :
1121 18 : void GMLReader::ResetReading()
1122 :
1123 : {
1124 18 : CleanupParser();
1125 18 : }
1126 :
1127 : #endif /* HAVE_XERCES == 1 or HAVE_EXPAT */
1128 :
|