1 /******************************************************************************
2 * $Id: ogrfmedatasource.cpp 27959 2014-11-14 18:29:21Z rouault $
3 *
4 * Project: FMEObjects Translator
5 * Purpose: Implementations of the OGRFMEDataSource class.
6 * Author: Frank Warmerdam, warmerdam@pobox.com
7 *
8 ******************************************************************************
9 * Copyright (c) 1999, 2001, 2002 Safe Software Inc.
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 "fme2ogr.h"
31 //#include "ogr2fme.h"
32 #include "cpl_conv.h"
33 #include "cpl_string.h"
34 //#include "ogrfme_cdsys.h"
35 #include "cpl_multiproc.h"
36 #include <idialog.h>
37 #include <ilogfile.h>
38 #include <icrdsysmgr.h>
39
40 const char* kPROVIDERNAME = "FME_OLEDB";
41
42 CPL_CVSID("$Id: ogrfmedatasource.cpp 27959 2014-11-14 18:29:21Z rouault $");
43
44 #ifdef WIN32
45 #define FMEDLL_NAME "fme.dll"
46 #define PATH_CHAR '\\'
47 #else
48 #define FMEDLL_NAME "libfmeobj.so"
49 #define PATH_CHAR '/'
50 #endif
51
52 static IFMESession *poSharedSession = NULL;
53 static int nSharedSessionRefCount = 0;
54 static void *hSessionMutex = NULL;
55
56 typedef struct {
57 IFMEUniversalReader *poReader;
58 char *pszReaderType;
59 char *pszDefinition;
60 } CachedConnection;
61
62 static int nCachedConnectionCount = 0;
63 static CachedConnection *pasCachedConnections = NULL;
64
65 typedef struct {
66 OGREnvelope sExtent;
67 char *pszIndFile;
68 char *pszCoordSys;
69 IFMESpatialIndex *poIndex;
70 OGRwkbGeometryType eBestGeomType;
71 } CacheLayerInfo;
72
73 /************************************************************************/
74 /* FME_Logger() */
75 /* */
76 /* Output that would normally go to the FME log file will */
77 /* instead be redirected through this function. */
78 /************************************************************************/
79
FME_Logger(FME_MsgLevel severity,const char * message)80 void FME_Logger( FME_MsgLevel severity, const char *message )
81
82 {
83 char *pszMessageCopy = CPLStrdup(message);
84
85 if( pszMessageCopy[strlen(pszMessageCopy)-1] == '\n' )
86 pszMessageCopy[strlen(pszMessageCopy)-1] = '\0';
87
88 CPLDebug( "FME_LOG", "%d:%s", severity, pszMessageCopy );
89
90 CPLFree( pszMessageCopy );
91 }
92
93 /************************************************************************/
94 /* GetTmpDir() */
95 /************************************************************************/
96
GetTmpDir()97 static const char *GetTmpDir()
98
99 {
100 const char *pszTmpDir;
101
102 pszTmpDir = getenv("OGRFME_TMPDIR");
103 if( pszTmpDir == NULL )
104 pszTmpDir = getenv("TMPDIR");
105 if( pszTmpDir == NULL )
106 pszTmpDir = getenv("TEMPDIR");
107 if( pszTmpDir == NULL ) //20020419 - ryan
108 pszTmpDir = getenv("TMP");
109 if( pszTmpDir == NULL )
110 pszTmpDir = getenv("TEMP");
111 if( pszTmpDir == NULL )
112 {
113 #ifdef WIN32
114 pszTmpDir = "C:\\";
115 #else
116 pszTmpDir = "/tmp";
117 #endif
118 }
119
120 return pszTmpDir;
121 }
122
123 /************************************************************************/
124 /* BuildTmpNam() */
125 /* */
126 /* Create a basename for the temporary file for a given layer */
127 /* on this dataset. */
128 /************************************************************************/
129
BuildTmpNam(const char * pszLayerName)130 static char *BuildTmpNam( const char *pszLayerName )
131
132 {
133 int i;
134 char szFilename[2048];
135 VSIStatBuf sStat;
136 const char *pszTmpDir = GetTmpDir();
137
138 /* -------------------------------------------------------------------- */
139 /* Look for an unused name. */
140 /* -------------------------------------------------------------------- */
141 for( i = -1; TRUE; i++ )
142 {
143 if( i == -1 )
144 sprintf( szFilename, "%s%c%s_%s",
145 pszTmpDir, PATH_CHAR, kPROVIDERNAME, pszLayerName );
146 else
147 sprintf( szFilename, "%s%c%s_%s_%d",
148 pszTmpDir, PATH_CHAR, kPROVIDERNAME, pszLayerName, i );
149
150 if( VSIStat( szFilename, &sStat ) != 0 )
151 break;
152 }
153
154 return CPLStrdup( szFilename );
155 }
156
157 /************************************************************************/
158 /* OGRFMEDataSource() */
159 /************************************************************************/
160
OGRFMEDataSource()161 OGRFMEDataSource::OGRFMEDataSource()
162
163 {
164 pszName = NULL;
165 pszDataset = NULL;
166 pszReaderName = NULL;
167 poSession = NULL;
168 poReader = NULL;
169 poFMEFeature = NULL;
170
171 nLayers = 0;
172 papoLayers = NULL;
173
174 poUserDirectives = NULL;
175
176 bUseCaching = FALSE;
177 }
178
179 /************************************************************************/
180 /* ~OGRFMEDataSource() */
181 /************************************************************************/
182
~OGRFMEDataSource()183 OGRFMEDataSource::~OGRFMEDataSource()
184
185 {
186 CPLDebug( kPROVIDERNAME, "~OGRFMEDataSource(): %p", this );
187
188 if( poSharedSession == NULL )
189 return;
190
191 AcquireSession();
192
193 /* -------------------------------------------------------------------- */
194 /* Destroy the layers, so we know we don't still have the */
195 /* caches open when we dereference them. */
196 /* -------------------------------------------------------------------- */
197 for( int i = 0; i < nLayers; i++ )
198 delete papoLayers[i];
199
200 CPLFree( papoLayers );
201
202 /* -------------------------------------------------------------------- */
203 /* If we have a cached instances, decrement the reference count. */
204 /* -------------------------------------------------------------------- */
205 #ifdef SUPPORT_PERSISTENT_CACHE
206 {
207 OGRFMECacheIndex oCacheIndex(
208 CPLFormFilename(GetTmpDir(), "ogrfmeds", "ind" ) );
209
210 if( pszReaderName != NULL && nLayers > 0
211 && bUseCaching && oCacheIndex.Lock() && oCacheIndex.Load() )
212 {
213 CPLXMLNode *psMatchDS = NULL;
214
215 psMatchDS = oCacheIndex.FindMatch( pszReaderName, pszDataset,
216 *poUserDirectives );
217
218 if( psMatchDS != NULL )
219 oCacheIndex.Dereference( psMatchDS );
220
221 if( oCacheIndex.ExpireOldCaches( poSession ) || psMatchDS != NULL )
222 oCacheIndex.Save();
223
224 oCacheIndex.Unlock();
225 }
226 }
227 #endif /* def SUPPORT_PERSISTENT_CACHE */
228
229 /* -------------------------------------------------------------------- */
230 /* Cleanup up various resources. */
231 /* -------------------------------------------------------------------- */
232 if( poFMEFeature != NULL )
233 poSession->destroyFeature( poFMEFeature );
234
235 if( poUserDirectives != NULL )
236 poSession->destroyStringArray( poUserDirectives );
237
238 if( poReader != NULL )
239 {
240 if( !IsPartOfConnectionCache( poReader ) )
241 poSession->destroyReader( poReader );
242 else
243 CPLDebug( kPROVIDERNAME, "Preserving cached reader on destructor");
244 }
245
246 if( poSession != NULL )
247 {
248 if( --nSharedSessionRefCount == 0 )
249 {
250 #ifdef SUPPORT_CLEANUP_SESSION
251 #ifdef SUPPORT_INDIRECT_FMEDLL
252 int (*pfnFME_destroySession)(void *);
253
254 pfnFME_destroySession = (int (*)(void*))
255 CPLGetSymbol(FMEDLL_NAME, "FME_DestroySession" );
256 if( pfnFME_destroySession == NULL )
257 CPLError( CE_Warning, CPLE_AppDefined,
258 "Failed to fetch FME_DestroySession entry point." );
259 else
260 pfnFME_destroySession( (void *) (&poSession) );
261 #else
262 FME_destroySession( poSession );
263 #endif // def SUPPORT_INDIRECT_FMEDLL
264 poSharedSession = NULL;
265 #else // ndef SUPPORT_CLEANUP_SESSION
266 CPLDebug( kPROVIDERNAME, "no active datasources left, but preserving session." );
267 #endif
268 }
269 }
270
271 CPLFree( pszName );
272 CPLFree( pszDataset );
273 CPLFree( pszReaderName );
274
275 ReleaseSession();
276 }
277
278 /************************************************************************/
279 /* PromptForSource() */
280 /************************************************************************/
281
PromptForSource()282 char *OGRFMEDataSource::PromptForSource()
283
284 {
285 IFMEDialog *poDialog = NULL;
286 IFMEString *poSourceFormat, *poSourceDSName;
287 char *pszResult = NULL;
288
289 poSourceFormat = poSession->createString();
290 poSourceDSName = poSession->createString();
291
292 if( poSession->createDialog( poDialog ) != 0 )
293 return NULL;
294
295 poUserDirectives->append( "SPATIAL_SETTINGS" );
296 poUserDirectives->append( "no" );
297 if( poDialog->sourcePrompt( NULL, NULL, *poSourceFormat, *poSourceDSName,
298 *poUserDirectives ) )
299 {
300 pszResult = CPLStrdup(CPLSPrintf("%s:%s",
301 poSourceFormat->data(),
302 poSourceDSName->data()));
303 }
304
305 poSession->destroyString( poSourceFormat );
306 poSession->destroyString( poSourceDSName );
307
308 return pszResult;
309 }
310
311 /************************************************************************/
312 /* ReadFileSource() */
313 /************************************************************************/
314
ReadFileSource(const char * pszFilename)315 char *OGRFMEDataSource::ReadFileSource( const char *pszFilename )
316
317 {
318 FILE *fp;
319 char **papszLines = NULL;
320 const char *pszLine;
321
322 /* -------------------------------------------------------------------- */
323 /* Read the definition file. */
324 /* -------------------------------------------------------------------- */
325 fp = VSIFOpen( pszFilename, "rt" );
326 if( fp == NULL )
327 {
328 CPLError( CE_Failure, CPLE_AppDefined,
329 "Failed to open file %s.",
330 pszFilename );
331 return NULL;
332 }
333
334 while( (pszLine = CPLReadLine(fp)) != NULL )
335 {
336 if( *pszLine != '#' )
337 papszLines = CSLAddString( papszLines, pszLine );
338 }
339
340 VSIFClose( fp );
341
342 /* -------------------------------------------------------------------- */
343 /* verify minimal requirements. */
344 /* -------------------------------------------------------------------- */
345 if( CSLCount(papszLines) < 2 )
346 {
347 CPLError( CE_Failure, CPLE_AppDefined,
348 "Insufficient lines in FME Data Definition file."
349 "At least a readername and data source name is required." );
350 return NULL;
351 }
352
353 /* -------------------------------------------------------------------- */
354 /* Apply extra values to user directives. */
355 /* -------------------------------------------------------------------- */
356 int i;
357
358 for( i = 2; papszLines[i] != NULL; i++ )
359 poUserDirectives->append( papszLines[i] );
360
361 /* -------------------------------------------------------------------- */
362 /* Prepare reader:dataset response string. */
363 /* -------------------------------------------------------------------- */
364 char *pszReturn;
365
366 pszReturn = CPLStrdup(CPLSPrintf("%s:%s", papszLines[0], papszLines[1] ));
367
368 CSLDestroy( papszLines );
369
370 return pszReturn;
371 }
372
373 /************************************************************************/
374 /* SaveDefinitionFile() */
375 /************************************************************************/
376
SaveDefinitionFile(const char * pszFilename,const char * pszReader,const char * pszDatasource,IFMEStringArray & oUserDirectives)377 static void SaveDefinitionFile( const char *pszFilename,
378 const char *pszReader,
379 const char *pszDatasource,
380 IFMEStringArray &oUserDirectives )
381
382 {
383 FILE *fp;
384 int i;
385
386 fp = VSIFOpen( CPLResetExtension( pszFilename, "fdd" ), "wt" );
387 if( fp == NULL )
388 return;
389
390 fprintf( fp, "%s\n", pszReader );
391 fprintf( fp, "%s\n", pszDatasource );
392
393 for( i = 0; i < (int) oUserDirectives.entries(); i++ )
394 {
395 fprintf( fp, "%s\n", oUserDirectives(i) );
396 }
397
398 VSIFClose( fp );
399 }
400
401 /************************************************************************/
402 /* ExtractSRS() */
403 /************************************************************************/
404
405 OGRSpatialReference *
ExtractSRS()406 OGRFMEDataSource::ExtractSRS()
407
408 {
409 /* -------------------------------------------------------------------- */
410 /* Try to find the COORDSYS in the user directives. */
411 /* -------------------------------------------------------------------- */
412 const char *pszCoordSys = NULL;
413 for( int i = 0; i < (int) poUserDirectives->entries(); i += 2 )
414 {
415 if( EQUAL((*poUserDirectives)(i),"COORDSYS") )
416 pszCoordSys = (*poUserDirectives)(i+1);
417 }
418
419 if( pszCoordSys == NULL || strlen(pszCoordSys) == 0 )
420 return NULL;
421
422 /* -------------------------------------------------------------------- */
423 /* Translate FME name to an OGRSpatialReference. */
424 /* -------------------------------------------------------------------- */
425 return FME2OGRSpatialRef( pszCoordSys );
426 }
427
428 /************************************************************************/
429 /* Open() */
430 /************************************************************************/
431
Open(const char * pszCompositeName)432 int OGRFMEDataSource::Open( const char * pszCompositeName )
433
434 {
435 FME_MsgNum err;
436
437 CPLAssert( poSession == NULL ); // only open once
438
439 /* -------------------------------------------------------------------- */
440 /* Do some initial validation. Does this even look like it */
441 /* could plausibly be an FME suitable name? We accept PROMPT:, */
442 /* <reader>: or anything ending in .fdd as a reasonable candidate. */
443 /* -------------------------------------------------------------------- */
444 int i;
445
446 for( i = 0; pszCompositeName[i] != ':' && pszCompositeName[i] != '\0'; i++)
447 {
448 if( pszCompositeName[i] == '/' || pszCompositeName[i] == '\\'
449 || pszCompositeName[i] == '.' )
450 break;
451 }
452
453 if( (i < 2 || pszCompositeName[i] != ':'
454 || EQUALN(pszCompositeName,"OCI:",4)
455 || EQUALN(pszCompositeName,"gltp:",5)
456 || EQUALN(pszCompositeName,"http",4)
457 || EQUALN(pszCompositeName,"DODS:",5)
458 || EQUALN(pszCompositeName,"ODBC:",5)
459 || EQUALN(pszCompositeName,"MYSQL:",5))
460 && !EQUAL(CPLGetExtension( pszCompositeName ), "fdd")
461 && !EQUALN(pszCompositeName,"PROMPT",6) )
462 {
463 CPLDebug( kPROVIDERNAME,
464 "OGRFMEDataSource::Open(%s) don't try to open via FME.",
465 pszCompositeName );
466 return FALSE;
467 }
468
469 CPLDebug( kPROVIDERNAME, "OGRFMEDataSource::Open(%s):%p/%ld",
470 pszCompositeName, this, (long) CPLGetPID() );
471
472 /* -------------------------------------------------------------------- */
473 /* Create an FME Session. */
474 /* -------------------------------------------------------------------- */
475 poSession = AcquireSession();
476 if( poSession == NULL )
477 return FALSE;
478
479 nSharedSessionRefCount++;
480
481 CPLDebug( kPROVIDERNAME, "%p:acquired session", this );
482
483 poUserDirectives = poSession->createStringArray();
484
485 /* -------------------------------------------------------------------- */
486 /* Redirect FME log messages through CPLDebug(). */
487 /* -------------------------------------------------------------------- */
488 IFMELogFile *poLogFile = poSession->logFile();
489
490 poLogFile->setFileName( NULL, FME_FALSE );
491 poLogFile->setCallBack( FME_Logger );
492
493 CPLDebug( kPROVIDERNAME, "%p:reset logfile", this );
494
495 /* -------------------------------------------------------------------- */
496 /* Prompt for a source, if none is provided. */
497 /* -------------------------------------------------------------------- */
498 if( EQUAL(pszCompositeName,"") || EQUALN(pszCompositeName,"PROMPT",6) )
499 {
500 pszName = PromptForSource();
501 if( pszName == NULL )
502 {
503 ReleaseSession();
504 return FALSE;
505 }
506 }
507 else if( CPLGetExtension( pszCompositeName ) != NULL
508 && EQUAL(CPLGetExtension( pszCompositeName ),"fdd") )
509 {
510 pszName = ReadFileSource(pszCompositeName);
511 if( pszName == NULL )
512 {
513 ReleaseSession();
514 return FALSE;
515 }
516 }
517 else
518 {
519 pszName = CPLStrdup( pszCompositeName );
520 }
521
522 /* -------------------------------------------------------------------- */
523 /* Extract the reader name and password compontents. The */
524 /* reader name will be followed by a single colon and then the */
525 /* FME DATASET name. */
526 /* -------------------------------------------------------------------- */
527 for( i = 0; pszName[i] != '\0' && pszName[i] != ':'; i++ ) {}
528
529 if( pszName[i] == '\0' || i < 2 )
530 {
531 CPLError( CE_Failure, CPLE_AppDefined,
532 "Failed to parse reader and data source from:\n%s",
533 pszName );
534 ReleaseSession();
535 return FALSE;
536 }
537
538 pszReaderName = CPLStrdup( pszName );
539 pszReaderName[i] = '\0';
540
541 pszDataset = CPLStrdup(pszName + i + 1);
542
543 CPLDebug( kPROVIDERNAME, "%s:parsed out dataset", pszDataset );
544
545 /* -------------------------------------------------------------------- */
546 /* If we prompted for a defintion that includes a file to save */
547 /* it to, do the save now. */
548 /* -------------------------------------------------------------------- */
549 if( EQUALN(pszCompositeName,"PROMPT:",7)
550 && strlen(pszCompositeName) > 7 )
551 {
552 SaveDefinitionFile( pszCompositeName+7,
553 pszReaderName, pszDataset,
554 *poUserDirectives );
555 }
556
557 /* -------------------------------------------------------------------- */
558 /* Is there a Coordsys statement in the user directives? */
559 /* -------------------------------------------------------------------- */
560 OGRSpatialReference *poSRS = ExtractSRS();
561
562 CPLDebug( kPROVIDERNAME, "got the SRS parsed");
563
564 bCoordSysOverride = poSRS != NULL;
565
566 /* -------------------------------------------------------------------- */
567 /* Allocate an FME string, and feature for use here and */
568 /* elsewhere. */
569 /* -------------------------------------------------------------------- */
570 poFMEFeature = poSession->createFeature();
571 poFMEString = poSession->createString();
572
573 /* -------------------------------------------------------------------- */
574 /* Are we going to use the direct access DB mechanism, or the */
575 /* spatiallly cached (dumb reader) mechanism. */
576 /* -------------------------------------------------------------------- */
577 bUseCaching = !EQUALN(pszReaderName,"SDE",3)
578 && !EQUALN(pszReaderName,"ORACLE",6);
579
580 /* -------------------------------------------------------------------- */
581 /* Is there already a cache for this dataset? If so, we will */
582 /* use it. */
583 /* -------------------------------------------------------------------- */
584 #ifdef SUPPORT_PERSISTENT_CACHE
585 OGRFMECacheIndex oCacheIndex(
586 CPLFormFilename(GetTmpDir(), "ogrfmeds", "ind" ) );
587 CPLXMLNode *psMatchDS = NULL;
588
589 if( bUseCaching && oCacheIndex.Lock() && oCacheIndex.Load() )
590 {
591 int bNeedSave = oCacheIndex.ExpireOldCaches( poSession );
592
593 psMatchDS = oCacheIndex.FindMatch( pszReaderName, pszDataset,
594 *poUserDirectives );
595
596 if( psMatchDS != NULL )
597 {
598 oCacheIndex.Reference( psMatchDS );
599 oCacheIndex.Save();
600
601 psMatchDS = CPLCloneXMLTree( psMatchDS );
602 oCacheIndex.Unlock();
603
604 }
605 else
606 {
607 if( bNeedSave )
608 oCacheIndex.Save();
609
610 oCacheIndex.Unlock();
611 }
612
613 if( psMatchDS != NULL )
614 {
615 if( InitializeFromXML( psMatchDS ) )
616 {
617 CPLDestroyXMLNode( psMatchDS );
618 ReleaseSession();
619
620 return TRUE;
621 }
622
623 CPLDestroyXMLNode( psMatchDS );
624 }
625 }
626 #endif /* def SUPPORT_PERSISTENT_CACHE */
627
628 /* -------------------------------------------------------------------- */
629 /* Create a reader. */
630 /* -------------------------------------------------------------------- */
631 IFMEStringArray *poParms = poSession->createStringArray();
632
633 for( i = 0; i < (int) poUserDirectives->entries(); i++ )
634 CPLDebug( kPROVIDERNAME, "oUserDirectives(%d) = '%s'",
635 i, (*poUserDirectives)(i) );
636
637 poReader = poSession->createReader(pszReaderName, FME_FALSE,
638 poUserDirectives);
639 if( poReader == NULL )
640 {
641 CPLFMEError( poSession,
642 "Failed to create reader of type `%s'.\n",
643 pszReaderName );
644 ReleaseSession();
645 return FALSE;
646 }
647
648 CPLDebug( kPROVIDERNAME, "%p:reader created.", this );
649
650 /* -------------------------------------------------------------------- */
651 /* Now try to open the dataset. */
652 /* -------------------------------------------------------------------- */
653 err = poReader->open( pszDataset, *poParms );
654 if( err )
655 {
656 CPLFMEError( poSession,
657 "Failed to open dataset `%s' with reader of type `%s'.\n",
658 pszDataset, pszReaderName );
659 ReleaseSession();
660 return FALSE;
661 }
662
663 CPLDebug( kPROVIDERNAME, "%p:reader opened.", this );
664
665 /* -------------------------------------------------------------------- */
666 /* There are some circumstances where we want to keep a */
667 /* "connection" open for a data source. Offer this reader for */
668 /* connection caching. */
669 /* -------------------------------------------------------------------- */
670 OfferForConnectionCaching( poReader, pszReaderName,
671 pszDataset );
672
673 /* -------------------------------------------------------------------- */
674 /* Create a layer for each schema feature. */
675 /* -------------------------------------------------------------------- */
676 FME_Boolean eEndOfSchema;
677
678 while( TRUE )
679 {
680 err = poReader->readSchema( *poFMEFeature, eEndOfSchema );
681 if( err )
682 {
683 CPLFMEError( poSession, "IFMEReader::readSchema() failed." );
684 ReleaseSession();
685 return FALSE;
686 }
687
688 if( eEndOfSchema == FME_TRUE )
689 break;
690
691 CPLDebug( kPROVIDERNAME, "%p:readSchema() got %s.",
692 this, poFMEFeature->getFeatureType() );
693
694 OGRFMELayer *poNewLayer = NULL;
695
696 if( bUseCaching )
697 poNewLayer = new OGRFMELayerCached( this );
698 else
699 poNewLayer = new OGRFMELayerDB( this, pszReaderName, pszDataset,
700 poUserDirectives );
701
702 if( !poNewLayer->Initialize( poFMEFeature, poSRS ) )
703 {
704 CPLDebug( kPROVIDERNAME, "%p:Initialize() failed.", this );
705 delete poNewLayer;
706 ReleaseSession();
707 return FALSE;
708 }
709
710 papoLayers = (OGRFMELayer **)
711 CPLRealloc(papoLayers, sizeof(void*) * ++nLayers );
712 papoLayers[nLayers-1] = poNewLayer;
713 }
714
715 poSession->destroyStringArray( poParms );
716
717 if( poSRS != NULL )
718 poSRS->Release();
719
720 CPLDebug( kPROVIDERNAME, "%p:schema read.", this );
721
722 /* -------------------------------------------------------------------- */
723 /* Do we want to build our own index/caches for each layer? */
724 /* -------------------------------------------------------------------- */
725 if( bUseCaching )
726 BuildSpatialIndexes();
727
728 CPLDebug( kPROVIDERNAME, "%p:Open() successful.", this );
729
730 ReleaseSession();
731
732 /* -------------------------------------------------------------------- */
733 /* If we are caching, add this cache to the cache index. */
734 /* -------------------------------------------------------------------- */
735 #ifdef SUPPORT_PERSISTENT_CACHE
736 if( bUseCaching && oCacheIndex.Lock() && oCacheIndex.Load() )
737 {
738 CPLXMLNode *psXML = SerializeToXML();
739
740 oCacheIndex.Add( psXML ); // cache index takes ownership of tree
741 oCacheIndex.Reference( psXML );
742 oCacheIndex.Save();
743 oCacheIndex.Unlock();
744 }
745 #endif
746
747 return TRUE;
748 }
749
750 /************************************************************************/
751 /* GetLayer() */
752 /************************************************************************/
753
GetLayer(int iLayer)754 OGRLayer *OGRFMEDataSource::GetLayer( int iLayer )
755
756 {
757 if( iLayer < 0 || iLayer >= nLayers )
758 return NULL;
759 else
760 return papoLayers[iLayer];
761 }
762
763 /************************************************************************/
764 /* BuildSpatialIndexes() */
765 /* */
766 /* Import all the features, building per-layer spatial */
767 /* caches with indexing. */
768 /************************************************************************/
769
BuildSpatialIndexes()770 void OGRFMEDataSource::BuildSpatialIndexes()
771
772 {
773 CacheLayerInfo *pasCLI;
774 int iLayer;
775
776 pasCLI = (CacheLayerInfo *) CPLCalloc(sizeof(CacheLayerInfo),nLayers);
777
778 /* -------------------------------------------------------------------- */
779 /* Create index files with "temp file" names. */
780 /* -------------------------------------------------------------------- */
781 for( iLayer = 0; iLayer < nLayers; iLayer++ )
782 {
783 CacheLayerInfo *psCLI = pasCLI + iLayer;
784
785 psCLI->pszCoordSys = NULL;
786
787 psCLI->pszIndFile =
788 BuildTmpNam( papoLayers[iLayer]->GetLayerDefn()->GetName() );
789
790 psCLI->poIndex =
791 poSession->createSpatialIndex( psCLI->pszIndFile, "WRITE", NULL );
792
793 if( psCLI->poIndex == NULL || psCLI->poIndex->open() != 0 )
794 {
795 CPLDebug( kPROVIDERNAME,
796 "Serious error creating or opening spatial index ... bailing." );
797 return;
798 }
799
800 // our special marker meaning unset.
801 psCLI->eBestGeomType = (OGRwkbGeometryType) 500;
802 }
803
804 /* -------------------------------------------------------------------- */
805 /* Read all features, and store them into appropriate spatial */
806 /* indexes. */
807 /* -------------------------------------------------------------------- */
808 while( ReadFMEFeature() )
809 {
810 CacheLayerInfo *psCLI = NULL;
811
812 poFMEFeature->getFeatureType( *poFMEString );
813
814 for( iLayer = 0; iLayer < nLayers; iLayer++ )
815 {
816 if( EQUAL(papoLayers[iLayer]->GetLayerDefn()->GetName(),
817 poFMEString->data()) )
818 {
819 psCLI = pasCLI + iLayer;
820 break;
821 }
822 }
823
824 if( psCLI == NULL )
825 {
826 CPLDebug( "FME_LOG",
827 "Skipping %s feature, doesn't match a layer.",
828 poFMEString->data() );
829 continue;
830 }
831
832 psCLI->poIndex->store( *poFMEFeature );
833
834 // Aggregate to extents.
835 FME_Real64 dfMinX, dfMaxX, dfMinY, dfMaxY;
836
837 poFMEFeature->boundingBox( dfMinX, dfMaxX, dfMinY, dfMaxY );
838
839 if( psCLI->poIndex->entries() == 1 )
840 {
841 psCLI->sExtent.MinX = dfMinX;
842 psCLI->sExtent.MaxX = dfMaxX;
843 psCLI->sExtent.MinY = dfMinY;
844 psCLI->sExtent.MaxY = dfMaxY;
845 }
846 else
847 {
848 psCLI->sExtent.MinX = MIN(psCLI->sExtent.MinX,dfMinX);
849 psCLI->sExtent.MaxX = MAX(psCLI->sExtent.MaxX,dfMaxX);
850 psCLI->sExtent.MinY = MIN(psCLI->sExtent.MinY,dfMinY);
851 psCLI->sExtent.MaxY = MAX(psCLI->sExtent.MaxY,dfMaxY);
852 }
853
854 // Update best geometry type to use based on this geometry.
855 ClarifyGeometryClass( poFMEFeature, psCLI->eBestGeomType );
856
857 // Check on coordsys.
858 if( poFMEFeature->getCoordSys() != NULL
859 && strlen(poFMEFeature->getCoordSys()) > 0 )
860 {
861 if( psCLI->pszCoordSys == NULL )
862 psCLI->pszCoordSys = CPLStrdup(poFMEFeature->getCoordSys());
863 else
864 {
865 if( !EQUAL(psCLI->pszCoordSys,poFMEFeature->getCoordSys()) )
866 CPLDebug( "FME_OLEDB",
867 "Conflicting coordsys %s (vs. %s) on layer %s.",
868 poFMEFeature->getCoordSys(),
869 psCLI->pszCoordSys,
870 papoLayers[iLayer]->GetLayerDefn()->GetName() );
871 }
872 }
873 }
874
875 /* -------------------------------------------------------------------- */
876 /* Close indexes and assign to layers. */
877 /* -------------------------------------------------------------------- */
878 for( iLayer = 0; iLayer < nLayers; iLayer++ )
879 {
880 OGRFMELayerCached * poLayer = (OGRFMELayerCached *) papoLayers[iLayer];
881 CacheLayerInfo *psCLI = pasCLI + iLayer;
882
883 // If there are no features, we destroy the layer.
884 if( psCLI->poIndex->entries() == 0 )
885 {
886 CPLDebug( "FME_LOG", "Drop layer %s, there are not features.",
887 poLayer->GetLayerDefn()->GetName() );
888 psCLI->poIndex->close(FME_TRUE);
889
890 delete poLayer;
891 papoLayers[iLayer] = NULL;
892 }
893 else
894 {
895 OGRSpatialReference *poSpatialRef = NULL;
896
897 psCLI->poIndex->close(FME_FALSE);
898 poSession->destroySpatialIndex( psCLI->poIndex );
899
900 if( psCLI->pszCoordSys != NULL && !bCoordSysOverride )
901 {
902 CPLDebug("FME_OLEDB",
903 "Applying COORDSYS=%s to layer %s from feature scan.",
904 psCLI->pszCoordSys,
905 papoLayers[iLayer]->GetLayerDefn()->GetName() );
906
907 poSpatialRef = FME2OGRSpatialRef( psCLI->pszCoordSys );
908 }
909
910 poLayer->AssignIndex( psCLI->pszIndFile, &(psCLI->sExtent),
911 poSpatialRef );
912 if( psCLI->eBestGeomType != 500
913 && psCLI->eBestGeomType
914 != poLayer->GetLayerDefn()->GetGeomType() )
915 {
916 CPLDebug( "FME_LOG", "Setting geom type from %d to %d",
917 poLayer->GetLayerDefn()->GetGeomType(),
918 psCLI->eBestGeomType );
919
920 poLayer->GetLayerDefn()->SetGeomType( psCLI->eBestGeomType );
921 }
922 }
923
924 CPLFree( psCLI->pszIndFile );
925 CPLFree( psCLI->pszCoordSys );
926 }
927
928 CPLFree( pasCLI );
929
930 /* -------------------------------------------------------------------- */
931 /* Compress missing layers from the layer list. */
932 /* -------------------------------------------------------------------- */
933 for( iLayer = 0; iLayer < nLayers; iLayer++ )
934 {
935 if( papoLayers[iLayer] == NULL )
936 {
937 papoLayers[iLayer] = papoLayers[nLayers-1];
938 nLayers--;
939 iLayer--;
940 }
941 }
942 }
943
944 /************************************************************************/
945 /* ClarifyGeometryClass() */
946 /* */
947 /* Examine an FME features geometry, and ensure the wkb */
948 /* geometry type we are using will include it. That is, we */
949 /* generally make the geometry type sufficiently general to */
950 /* include the type of geometry of this feature. */
951 /* */
952 /* Exceptions are when the existing type is unknown, in which */
953 /* case we assign it the type of the geometry we find, and if */
954 /* it is a mixture of polygon and multipolygon in which case we */
955 /* use multipolygon with the understanding that we will force */
956 /* the polygons to multipolygon. */
957 /************************************************************************/
958
ClarifyGeometryClass(IFMEFeature * poFeature,OGRwkbGeometryType & eBestGeomType)959 void OGRFMEDataSource::ClarifyGeometryClass(
960 IFMEFeature *poFeature,
961 OGRwkbGeometryType &eBestGeomType )
962
963 {
964 FME_GeometryType eFMEType = poFeature->getGeometryType();
965 OGRwkbGeometryType eThisType;
966
967 /* -------------------------------------------------------------------- */
968 /* Classify this FME geometry. The hard case is aggregate. */
969 /* -------------------------------------------------------------------- */
970 if( eFMEType == FME_GEOM_POINT )
971 eThisType = wkbPoint;
972 else if( eFMEType == FME_GEOM_LINE )
973 eThisType = wkbLineString;
974 else if( eFMEType == FME_GEOM_POLYGON || eFMEType == FME_GEOM_DONUT )
975 eThisType = wkbPolygon;
976 else if( eFMEType == FME_GEOM_AGGREGATE )
977 {
978 // This is the hard case! Split the aggregate to see if we can
979 // categorize it more specifically.
980 OGRwkbGeometryType eComponentType = (OGRwkbGeometryType) 500;
981 IFMEFeatureVector *poFeatVector;
982
983 poFeatVector = poSession->createFeatureVector();
984
985 poFeature->splitAggregate( *poFeatVector );
986
987 for( int iPart = 0; iPart < (int)poFeatVector->entries(); iPart++ )
988 {
989 IFMEFeature *poFMEPart = (*poFeatVector)(iPart);
990
991 if( poFMEPart != NULL )
992 ClarifyGeometryClass( poFMEPart, eComponentType );
993 }
994
995 poSession->destroyFeatureVector( poFeatVector );
996
997 if( wkbFlatten(eComponentType) == wkbPolygon )
998 eThisType = wkbMultiPolygon;
999 else if( wkbFlatten(eComponentType) == wkbPoint )
1000 eThisType = wkbMultiPoint;
1001 else if( wkbFlatten(eComponentType) == wkbLineString )
1002 eThisType = wkbMultiLineString;
1003 else
1004 eThisType = wkbGeometryCollection;
1005 }
1006 else
1007 eThisType = wkbUnknown;
1008
1009 // Is this 3D?
1010 if( poFeature->getDimension() == FME_THREE_D )
1011 eThisType = wkbSetZ(eThisType);
1012
1013 /* -------------------------------------------------------------------- */
1014 /* Now adjust the working type. */
1015 /* -------------------------------------------------------------------- */
1016 OGRwkbGeometryType eNewBestGeomType = eBestGeomType;
1017
1018 if( eBestGeomType == 500 )
1019 eNewBestGeomType = eThisType;
1020 else if( eThisType == wkbNone )
1021 /* do nothing */;
1022 else if( wkbFlatten(eThisType) == wkbFlatten(eBestGeomType) )
1023 /* no change */;
1024 else if( wkbFlatten(eThisType) == wkbPolygon
1025 && wkbFlatten(eBestGeomType) == wkbMultiPolygon )
1026 /* do nothing */;
1027 else if( wkbFlatten(eThisType) == wkbMultiPolygon
1028 && wkbFlatten(eBestGeomType) == wkbPolygon )
1029 eNewBestGeomType = wkbMultiPolygon;
1030 else if( wkbFlatten(eThisType) >= 4 && wkbFlatten(eThisType) <= 7
1031 && wkbFlatten(eBestGeomType) >= 4 && wkbFlatten(eBestGeomType) <= 7 )
1032 /* they are both collections, but not the same ... go to generic coll*/
1033 eNewBestGeomType = wkbGeometryCollection;
1034 else
1035 eNewBestGeomType = wkbUnknown;
1036
1037 if( (wkbHasZ(eBestGeomType) || wkbHasZ(eThisType))
1038 && (int) eNewBestGeomType != 500 )
1039 {
1040 eNewBestGeomType = wkbSetZ(eNewBestGeomType);
1041 }
1042
1043 eBestGeomType = eNewBestGeomType;
1044 }
1045
1046 /************************************************************************/
1047 /* ReadFMEFeature() */
1048 /* */
1049 /* Internal working function to read an FME feature into the */
1050 /* poFMEFeature object, and increment the nPreviousFeature */
1051 /* counter. Returns FALSE on end of input, or on error. */
1052 /************************************************************************/
1053
ReadFMEFeature()1054 int OGRFMEDataSource::ReadFMEFeature()
1055
1056 {
1057 FME_Boolean eEndOfSchema;
1058 FME_MsgNum err;
1059
1060 poFMEFeature->resetFeature();
1061 err = poReader->read( *poFMEFeature, eEndOfSchema );
1062
1063 if( err )
1064 {
1065 CPLFMEError( poSession, "Error while reading feature." );
1066 return FALSE;
1067 }
1068
1069 if( eEndOfSchema == FME_TRUE )
1070 {
1071 return FALSE;
1072 }
1073
1074 return TRUE;
1075 }
1076
1077 /************************************************************************/
1078 /* ProcessGeometry() */
1079 /* */
1080 /* Translate an FME geometry into an OGR geometry. */
1081 /************************************************************************/
1082
1083 OGRGeometry *
ProcessGeometry(OGRFMELayer * poLayer,IFMEFeature * poGeomFeat,OGRwkbGeometryType eDesiredType)1084 OGRFMEDataSource::ProcessGeometry( OGRFMELayer * poLayer,
1085 IFMEFeature * poGeomFeat,
1086 OGRwkbGeometryType eDesiredType )
1087 {
1088
1089 FME_GeometryType eGeomType = poGeomFeat->getGeometryType();
1090 int bForceToMulti = FALSE;
1091
1092 if( wkbFlatten(eDesiredType) == wkbGeometryCollection
1093 || wkbFlatten(eDesiredType) == wkbMultiPolygon )
1094 bForceToMulti = TRUE;
1095
1096 /* -------------------------------------------------------------------- */
1097 /* Point */
1098 /* -------------------------------------------------------------------- */
1099 if( eGeomType == FME_GEOM_POINT )
1100 {
1101 return
1102 new OGRPoint(poGeomFeat->getXCoordinate(0),
1103 poGeomFeat->getYCoordinate(0),
1104 poGeomFeat->getZCoordinate(0));
1105 }
1106
1107 /* -------------------------------------------------------------------- */
1108 /* Line */
1109 /* -------------------------------------------------------------------- */
1110 else if( eGeomType == FME_GEOM_LINE )
1111 {
1112 OGRLineString *poLine = new OGRLineString();
1113
1114 poLine->setNumPoints( poGeomFeat->numCoords() );
1115
1116 for( int iPoint = 0; iPoint < (int) poGeomFeat->numCoords(); iPoint++ )
1117 {
1118 poLine->setPoint( iPoint,
1119 poGeomFeat->getXCoordinate(iPoint),
1120 poGeomFeat->getYCoordinate(iPoint),
1121 poGeomFeat->getZCoordinate(iPoint) );
1122 }
1123
1124 return poLine;
1125 }
1126
1127 /* -------------------------------------------------------------------- */
1128 /* Polygon */
1129 /* -------------------------------------------------------------------- */
1130 else if( eGeomType == FME_GEOM_POLYGON )
1131 {
1132 OGRLinearRing *poLine = new OGRLinearRing();
1133 OGRPolygon *poPolygon = new OGRPolygon();
1134
1135 poLine->setNumPoints( poGeomFeat->numCoords() );
1136
1137 for( int iPoint = 0; iPoint < (int)poGeomFeat->numCoords(); iPoint++ )
1138 {
1139 poLine->setPoint( iPoint,
1140 poGeomFeat->getXCoordinate(iPoint),
1141 poGeomFeat->getYCoordinate(iPoint),
1142 poGeomFeat->getZCoordinate(iPoint) );
1143 }
1144 poPolygon->addRingDirectly( poLine );
1145
1146 if( !bForceToMulti )
1147 return poPolygon;
1148
1149 OGRMultiPolygon *poMP = new OGRMultiPolygon();
1150
1151 poMP->addGeometryDirectly( poPolygon );
1152
1153 return poMP;
1154 }
1155
1156 /* -------------------------------------------------------------------- */
1157 /* Donut */
1158 /* -------------------------------------------------------------------- */
1159 else if( eGeomType == FME_GEOM_DONUT )
1160 {
1161 OGRPolygon *poPolygon = new OGRPolygon();
1162 IFMEFeatureVector *poFeatVector;
1163 IFMEFeature *poFMERing = NULL;
1164
1165 poFeatVector = poSession->createFeatureVector();
1166
1167 poGeomFeat->getDonutParts( *poFeatVector );
1168
1169 for( int iPart = 0; iPart < (int)poFeatVector->entries(); iPart++ )
1170 {
1171 OGRLinearRing *poRing;
1172
1173 poFMERing = (*poFeatVector)(iPart);
1174 if( poFMERing == NULL )
1175 continue;
1176
1177 poRing = new OGRLinearRing();
1178
1179 poRing->setNumPoints( poFMERing->numCoords() );
1180
1181 for( int iPoint=0; iPoint < (int)poFMERing->numCoords(); iPoint++ )
1182 {
1183 poRing->setPoint( iPoint,
1184 poFMERing->getXCoordinate(iPoint),
1185 poFMERing->getYCoordinate(iPoint),
1186 poFMERing->getZCoordinate(iPoint) );
1187 }
1188
1189 poPolygon->addRingDirectly( poRing );
1190 }
1191
1192 poFeatVector->clearAndDestroy();
1193 poSession->destroyFeatureVector( poFeatVector );
1194
1195 if( !bForceToMulti )
1196 return poPolygon;
1197
1198 OGRMultiPolygon *poMP = new OGRMultiPolygon();
1199
1200 poMP->addGeometryDirectly( poPolygon );
1201
1202 return poMP;
1203 }
1204
1205 /* -------------------------------------------------------------------- */
1206 /* Aggregate */
1207 /* -------------------------------------------------------------------- */
1208 else if( eGeomType == FME_GEOM_AGGREGATE )
1209 {
1210 OGRGeometryCollection *poCollection;
1211 IFMEFeatureVector *poFeatVector;
1212 OGRwkbGeometryType eSubType = wkbUnknown;
1213
1214 if( bForceToMulti && eDesiredType == wkbMultiPolygon )
1215 {
1216 poCollection = new OGRMultiPolygon();
1217 eSubType = wkbPolygon;
1218 }
1219 else
1220 poCollection = new OGRGeometryCollection();
1221
1222 poFeatVector = poSession->createFeatureVector();
1223
1224 poGeomFeat->splitAggregate( *poFeatVector );
1225
1226 for( int iPart = 0; iPart < (int)poFeatVector->entries(); iPart++ )
1227 {
1228 OGRGeometry *poOGRPart;
1229 IFMEFeature *poFMEPart;
1230
1231 poFMEPart = (*poFeatVector)(iPart);
1232 if( poFMEPart == NULL )
1233 continue;
1234
1235 poOGRPart = ProcessGeometry( poLayer, poFMEPart, eSubType );
1236 if( poOGRPart == NULL )
1237 continue;
1238
1239 poCollection->addGeometryDirectly( poOGRPart );
1240 }
1241
1242 poSession->destroyFeatureVector( poFeatVector );
1243
1244 return poCollection;
1245 }
1246
1247 else if( eGeomType == FME_GEOM_UNDEFINED )
1248 {
1249 return NULL;
1250 }
1251 else
1252 {
1253 CPLDebug( kPROVIDERNAME,
1254 "unable to translate unsupported geometry type: %d\n",
1255 eGeomType );
1256
1257 return NULL;
1258 }
1259 }
1260
1261 /************************************************************************/
1262 /* ProcessFeature() */
1263 /* */
1264 /* Process the current fme feature into an OGR feature of the */
1265 /* passed layer types. */
1266 /************************************************************************/
1267
ProcessFeature(OGRFMELayer * poLayer,IFMEFeature * poSrcFeature)1268 OGRFeature *OGRFMEDataSource::ProcessFeature( OGRFMELayer *poLayer,
1269 IFMEFeature *poSrcFeature )
1270
1271 {
1272 OGRFeatureDefn *poDefn = poLayer->GetLayerDefn();
1273 OGRFeature *poFeature = new OGRFeature( poDefn );
1274 int iAttr;
1275
1276 /* -------------------------------------------------------------------- */
1277 /* Transfer attributes ... for numeric values assume the string */
1278 /* representation is appropriate, and automatically */
1279 /* translatable. Eventually we will need special handling for */
1280 /* array style fields. */
1281 /* -------------------------------------------------------------------- */
1282 for( iAttr = 0; iAttr < poDefn->GetFieldCount(); iAttr++ )
1283 {
1284 OGRFieldDefn *poField = poDefn->GetFieldDefn(iAttr);
1285
1286 if( poSrcFeature->getAttribute( poField->GetNameRef(),
1287 *poFMEString ) == FME_TRUE )
1288 {
1289 poFeature->SetField( iAttr, poFMEString->data() );
1290 }
1291 }
1292
1293 /* -------------------------------------------------------------------- */
1294 /* Translate the geometry. */
1295 /* -------------------------------------------------------------------- */
1296 OGRGeometry *poOGRGeom = NULL;
1297
1298 poOGRGeom = ProcessGeometry( poLayer, poSrcFeature,
1299 poLayer->GetLayerDefn()->GetGeomType() );
1300 if( poOGRGeom != NULL )
1301 poFeature->SetGeometryDirectly( poOGRGeom );
1302
1303 return poFeature;
1304 }
1305
1306 /************************************************************************/
1307 /* TestCapability() */
1308 /************************************************************************/
1309
TestCapability(const char *)1310 int OGRFMEDataSource::TestCapability( const char * )
1311
1312 {
1313 return FALSE;
1314 }
1315
1316 /************************************************************************/
1317 /* OfferForConnectionCaching() */
1318 /* */
1319 /* Sometimes we want to keep a prototype reader open to */
1320 /* maintain a connection, for instance to SDE where creating */
1321 /* the connection is pretty expensive. */
1322 /************************************************************************/
1323
OfferForConnectionCaching(IFMEUniversalReader * poReader,const char * pszReaderType,const char * pszDataset)1324 void OGRFMEDataSource::OfferForConnectionCaching(IFMEUniversalReader *poReader,
1325 const char *pszReaderType,
1326 const char *pszDataset)
1327
1328 {
1329 /* -------------------------------------------------------------------- */
1330 /* For now we only cache SDE readers. */
1331 /* -------------------------------------------------------------------- */
1332 if( !EQUALN(pszReaderType,"SDE",3)
1333 && !EQUALN(pszReaderType,"ORACLE",6) )
1334 return;
1335
1336 /* -------------------------------------------------------------------- */
1337 /* We want to build a definition of this connection that */
1338 /* indicates a unique connection. For now we base it on the */
1339 /* Server, UserName, Password, and Instance values. We will */
1340 /* pick these all out of the RUNTIME_MACROS if present. */
1341 /* */
1342 /* First find the runtime macros. */
1343 /* -------------------------------------------------------------------- */
1344 const char *pszRuntimeMacros = NULL;
1345 int i;
1346
1347 for( i = 0; i < (int) poUserDirectives->entries()-1; i += 2 )
1348 {
1349 if( EQUALN((const char *) (*poUserDirectives)(i),"RUNTIME_MACROS",14) )
1350 pszRuntimeMacros = (*poUserDirectives)(i+1);
1351 }
1352
1353 /* -------------------------------------------------------------------- */
1354 /* Break into name/value pairs. */
1355 /* -------------------------------------------------------------------- */
1356 char **papszTokens = NULL;
1357
1358 if( pszRuntimeMacros != NULL )
1359 papszTokens = CSLTokenizeStringComplex( pszRuntimeMacros, ",",
1360 TRUE, TRUE);
1361
1362 /* -------------------------------------------------------------------- */
1363 /* Look for Name values we want, and append them to the */
1364 /* definition string. */
1365 /* -------------------------------------------------------------------- */
1366 char szDefinition[5000];
1367
1368 sprintf( szDefinition, "%s::", pszDataset );
1369
1370 for( i = 0; i < CSLCount(papszTokens)-1; i += 2 )
1371 {
1372 if( strstr(papszTokens[i],"Server") != NULL
1373 || strstr(papszTokens[i],"Service") != NULL
1374 || strstr(papszTokens[i],"UserName") != NULL
1375 || strstr(papszTokens[i],"Password") != NULL
1376 || strstr(papszTokens[i],"Instance") != NULL )
1377 {
1378 if( strlen(papszTokens[i+1]) + strlen(papszTokens[i]) + 20
1379 < sizeof(szDefinition) - strlen(szDefinition) )
1380 {
1381 sprintf( szDefinition + strlen(szDefinition), "%s=%s;",
1382 papszTokens[i], papszTokens[i+1] );
1383 }
1384 }
1385 }
1386
1387 /* -------------------------------------------------------------------- */
1388 /* Do we already have a reader cached for this definition? */
1389 /* -------------------------------------------------------------------- */
1390 for( i = 0; i < nCachedConnectionCount; i++ )
1391 {
1392 if( strcmp(szDefinition, pasCachedConnections[i].pszDefinition) == 0 )
1393 return;
1394 }
1395
1396 /* -------------------------------------------------------------------- */
1397 /* Added this reader to the cache. */
1398 /* -------------------------------------------------------------------- */
1399 CPLDebug( kPROVIDERNAME,
1400 "Caching IFMEUniversalReader to maintain connection.\n"
1401 "ReaderType=%s, Definition=%s",
1402 pszReaderType, szDefinition );
1403
1404 nCachedConnectionCount++;
1405 pasCachedConnections = (CachedConnection *)
1406 CPLRealloc(pasCachedConnections,
1407 sizeof(CachedConnection) * nCachedConnectionCount);
1408
1409 pasCachedConnections[nCachedConnectionCount-1].poReader = poReader;
1410 pasCachedConnections[nCachedConnectionCount-1].pszReaderType =
1411 CPLStrdup(pszReaderType);
1412 pasCachedConnections[nCachedConnectionCount-1].pszDefinition =
1413 CPLStrdup(szDefinition);
1414 }
1415
1416 /************************************************************************/
1417 /* IsPartOfConnectionCache() */
1418 /* */
1419 /* I this reader being used to maintain a connection cache? */
1420 /************************************************************************/
IsPartOfConnectionCache(IFMEUniversalReader * poReader)1421 int OGRFMEDataSource::IsPartOfConnectionCache( IFMEUniversalReader *poReader )
1422
1423 {
1424 int i;
1425
1426 for( i = 0; i < nCachedConnectionCount; i++ )
1427 if( poReader == pasCachedConnections[i].poReader )
1428 return TRUE;
1429
1430 return FALSE;
1431 }
1432
1433 /************************************************************************/
1434 /* AcquireSession() */
1435 /* */
1436 /* Get unique ownership of the FME session for this thread. */
1437 /************************************************************************/
1438
AcquireSession()1439 IFMESession *OGRFMEDataSource::AcquireSession()
1440
1441 {
1442 FME_MsgNum err;
1443
1444 /* -------------------------------------------------------------------- */
1445 /* Create session mutex if we don't already have one. */
1446 /* -------------------------------------------------------------------- */
1447 if( hSessionMutex == NULL )
1448 {
1449 hSessionMutex = CPLCreateMutex();
1450
1451 CPLDebug( kPROVIDERNAME, "%p:Creating FME session, mutex=%d.",
1452 this, hSessionMutex );
1453 }
1454
1455 /* -------------------------------------------------------------------- */
1456 /* Try to acquire ownership of the session, even if the session */
1457 /* doesn't yet exist. */
1458 /* -------------------------------------------------------------------- */
1459 else
1460 {
1461 #ifdef DEBUG_MUTEX
1462 CPLDebug( kPROVIDERNAME, "%p:Wait for session mutex.", this );
1463 #endif
1464
1465 if( !CPLAcquireMutex( hSessionMutex, 5.0 ) )
1466 {
1467 CPLDebug( kPROVIDERNAME, "%p:Failed to acquire session mutex in 5s.",
1468 this );
1469 }
1470
1471 #ifdef DEBUG_MUTEX
1472 else
1473 CPLDebug( kPROVIDERNAME, "%p:Got session mutex.", this );
1474 #endif
1475 }
1476
1477 /* -------------------------------------------------------------------- */
1478 /* If the session doesn't exist, create it now. */
1479 /* -------------------------------------------------------------------- */
1480 if( poSharedSession == NULL )
1481 {
1482 #ifdef SUPPORT_INDIRECT_FMEDLL
1483 FME_MsgNum (*pfnFME_CreateSession)( void * );
1484 pfnFME_CreateSession = (FME_MsgNum (*)(void*))
1485 CPLGetSymbol( FMEDLL_NAME, "FME_CreateSession" );
1486 if( pfnFME_CreateSession == NULL )
1487 {
1488 CPLReleaseMutex( hSessionMutex );
1489 CPLDebug( kPROVIDERNAME, "Unable to load FME_CreateSession from %s, skipping FME Driver.", FMEDLL_NAME );
1490 return NULL;
1491 }
1492
1493 err = pfnFME_CreateSession( (void *) (&poSharedSession) );
1494 #else
1495 err = FME_createSession(poSharedSession);
1496 #endif
1497 if( err )
1498 {
1499 poSharedSession = NULL;
1500 CPLReleaseMutex( hSessionMutex );
1501 CPLError( CE_Failure, CPLE_AppDefined,
1502 "Failed to create FMESession." );
1503 return NULL;
1504 }
1505
1506 // Dale Nov 26 '01 -- Set up to log "badnews" from FME
1507 // to help track down problems
1508
1509 IFMEStringArray *poSessionDirectives =
1510 poSharedSession->createStringArray();
1511
1512 if( poSessionDirectives == NULL )
1513 {
1514 err = 1;
1515 CPLError( CE_Warning, CPLE_AppDefined,
1516 "Something has gone wonky with createStringArray() on the IFMESession.\n"
1517 "Is it possible you built with gcc 3.2 on Linux? This seems problematic." );
1518
1519 }
1520 else
1521 {
1522 poSessionDirectives->append("FME_DEBUG");
1523 poSessionDirectives->append("BADNEWS");
1524
1525 err = poSharedSession->init( poSessionDirectives );
1526
1527 poSharedSession->destroyStringArray( poSessionDirectives );
1528
1529 if( err )
1530 {
1531 CPLError( CE_Warning, CPLE_AppDefined,
1532 "Failed to initialize FMESession.\n%s",
1533 poSharedSession->getLastErrorMsg());
1534 }
1535 }
1536
1537 if( err )
1538 {
1539 #ifdef SUPPORT_INDIRECT_FMEDLL
1540 int (*pfnFME_destroySession)(void *);
1541
1542 pfnFME_destroySession = (int (*)(void*))
1543 CPLGetSymbol(FMEDLL_NAME, "FME_DestroySession" );
1544 if( pfnFME_destroySession != NULL )
1545 pfnFME_destroySession( (void *) (&poSharedSession) );
1546 #else
1547 FME_destroySession( poSharedSession );
1548 #endif // def SUPPORT_INDIRECT_FMEDLL
1549
1550 poSharedSession = NULL;
1551 CPLReleaseMutex( hSessionMutex );
1552 return NULL;
1553 }
1554 }
1555
1556 return poSharedSession;
1557 }
1558
1559 /************************************************************************/
1560 /* ReleaseSession() */
1561 /* */
1562 /* Release the lock on the FME session. */
1563 /************************************************************************/
1564
ReleaseSession()1565 void OGRFMEDataSource::ReleaseSession()
1566
1567 {
1568 #ifdef DEBUG_MUTEX
1569 CPLDebug( kPROVIDERNAME, "%p:Release session mutex.", this );
1570 #endif
1571
1572 CPLReleaseMutex( hSessionMutex );
1573 }
1574
1575 /************************************************************************/
1576 /* SerializeToXML() */
1577 /* */
1578 /* Convert the information about this datasource, and it's */
1579 /* layers into an XML format that can be stored in the */
1580 /* persistent feature cache index. */
1581 /************************************************************************/
1582
SerializeToXML()1583 CPLXMLNode *OGRFMEDataSource::SerializeToXML()
1584
1585 {
1586 CPLXMLNode *psDS;
1587
1588 CPLAssert( bUseCaching );
1589
1590 /* -------------------------------------------------------------------- */
1591 /* Setup data source information. */
1592 /* -------------------------------------------------------------------- */
1593 psDS = CPLCreateXMLNode( NULL, CXT_Element, "DataSource" );
1594
1595 CPLCreateXMLElementAndValue( psDS, "Driver", pszReaderName );
1596 CPLCreateXMLElementAndValue( psDS, "DSName", pszDataset );
1597 CPLCreateXMLElementAndValue( psDS, "RefCount", "0" );
1598 CPLCreateXMLElementAndValue( psDS, "CreationTime", "0" );
1599 CPLCreateXMLElementAndValue( psDS, "LastUseTime", "0" );
1600
1601 /* -------------------------------------------------------------------- */
1602 /* Append all the FME user directives in force. */
1603 /* -------------------------------------------------------------------- */
1604 CPLXMLNode *psUD;
1605
1606 psUD = CPLCreateXMLNode( psDS, CXT_Element, "UserDirectives" );
1607 for( int i = 0; i < (int) poUserDirectives->entries(); i++ )
1608 CPLCreateXMLElementAndValue( psUD, "Directive",
1609 (*poUserDirectives)(i) );
1610
1611 /* -------------------------------------------------------------------- */
1612 /* Now append all the layer information. */
1613 /* -------------------------------------------------------------------- */
1614 for( int iLayer = 0; iLayer < nLayers; iLayer++ )
1615 {
1616 OGRFMELayerCached * poLayer = (OGRFMELayerCached *) papoLayers[iLayer];
1617 CPLXMLNode *psLayer;
1618
1619 psLayer = poLayer->SerializeToXML();
1620 CPLAddXMLChild( psDS, psLayer );
1621 }
1622
1623 return psDS;
1624 }
1625
1626 /************************************************************************/
1627 /* InitializeFromXML() */
1628 /************************************************************************/
1629
InitializeFromXML(CPLXMLNode * psDS)1630 int OGRFMEDataSource::InitializeFromXML( CPLXMLNode *psDS )
1631
1632 {
1633 CPLAssert( bUseCaching );
1634
1635 /* -------------------------------------------------------------------- */
1636 /* Loop over layers, instantiating from the cached data. */
1637 /* -------------------------------------------------------------------- */
1638 CPLXMLNode *psLayerN;
1639
1640 for( psLayerN = psDS->psChild; psLayerN != NULL;
1641 psLayerN = psLayerN->psNext )
1642 {
1643 OGRFMELayerCached *poNewLayer;
1644
1645 if( !EQUAL(psLayerN->pszValue,"OGRLayer") )
1646 continue;
1647
1648 poNewLayer = new OGRFMELayerCached( this );
1649
1650 /* -------------------------------------------------------------------- */
1651 /* Initialize the layer from the XML. */
1652 /* -------------------------------------------------------------------- */
1653 if( !poNewLayer->InitializeFromXML( psLayerN ) )
1654 {
1655 // this is *not* proper cleanup
1656 CPLAssert( FALSE );
1657 nLayers = 0;
1658
1659 return FALSE;
1660 }
1661
1662 /* -------------------------------------------------------------------- */
1663 /* Assign the spatial index. We should really change this to */
1664 /* check if it succeeds! */
1665 /* -------------------------------------------------------------------- */
1666 poNewLayer->AssignIndex(
1667 CPLGetXMLValue( psLayerN, "SpatialCacheName",
1668 "<missing cachename>" ),
1669 NULL, NULL );
1670
1671 /* -------------------------------------------------------------------- */
1672 /* Add the layer to the layer list. */
1673 /* -------------------------------------------------------------------- */
1674 papoLayers = (OGRFMELayer **)
1675 CPLRealloc(papoLayers, sizeof(void*) * ++nLayers );
1676 papoLayers[nLayers-1] = poNewLayer;
1677 }
1678
1679 return TRUE;
1680 }
1681
1682 /************************************************************************/
1683 /* FME2OGRSpatialRef() */
1684 /* */
1685 /* Translate an FME coordinate system into an */
1686 /* OGRSpatialReference using the coordinate system manager */
1687 /* getCoordSysAsOGCDef() method. We assume the session has */
1688 /* already been acquired. */
1689 /************************************************************************/
1690
1691 OGRSpatialReference *
FME2OGRSpatialRef(const char * pszCoordsys)1692 OGRFMEDataSource::FME2OGRSpatialRef( const char *pszCoordsys )
1693
1694 {
1695 IFMEString *poOGCDef;
1696
1697 poOGCDef = poSession->createString();
1698
1699 poSession->coordSysManager()->getCoordSysAsOGCDef(
1700 pszCoordsys, *poOGCDef );
1701
1702 char *pszWKT = (char *) poOGCDef->data();
1703 OGRSpatialReference oSRS;
1704
1705 if( oSRS.importFromWkt( &pszWKT ) == OGRERR_NONE )
1706 {
1707 poSession->destroyString( poOGCDef );
1708 return oSRS.Clone();
1709 }
1710 else
1711 {
1712 poSession->destroyString( poOGCDef );
1713 return NULL;
1714 }
1715 }
1716
1717
1718
1719
1720
1721