1 /******************************************************************************
2 *
3 * Project: FMEObjects Translator
4 * Purpose: Implementations of the OGRFMEDataSource class.
5 * Author: Frank Warmerdam, warmerdam@pobox.com
6 *
7 ******************************************************************************
8 * Copyright (c) 1999, 2001, 2002 Safe Software Inc.
9 *
10 * Permission is hereby granted, free of charge, to any person obtaining a
11 * copy of this software and associated documentation files (the "Software"),
12 * to deal in the Software without restriction, including without limitation
13 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14 * and/or sell copies of the Software, and to permit persons to whom the
15 * Software is furnished to do so, subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be included
18 * in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
23 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
25 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
26 * DEALINGS IN THE SOFTWARE.
27 ****************************************************************************/
28
29 #include "fme2ogr.h"
30 //#include "ogr2fme.h"
31 #include "cpl_conv.h"
32 #include "cpl_string.h"
33 //#include "ogrfme_cdsys.h"
34 #include "cpl_multiproc.h"
35 #include <idialog.h>
36 #include <ilogfile.h>
37 #include <icrdsysmgr.h>
38
39 const char* kPROVIDERNAME = "FME_OLEDB";
40
41 CPL_CVSID("$Id: ogrfmedatasource.cpp 327bfdc0f5dd563c3b1c4cbf26d34967c5c9c790 2020-02-28 13:51:40 +0100 Even Rouault $")
42
43 #ifdef WIN32
44 #define FMEDLL_NAME "fme.dll"
45 #define PATH_CHAR '\\'
46 #else
47 #define FMEDLL_NAME "libfmeobj.so"
48 #define PATH_CHAR '/'
49 #endif
50
51 static IFMESession *poSharedSession = NULL;
52 static int nSharedSessionRefCount = 0;
53 static void *hSessionMutex = NULL;
54
55 typedef struct {
56 IFMEUniversalReader *poReader;
57 char *pszReaderType;
58 char *pszDefinition;
59 } CachedConnection;
60
61 static int nCachedConnectionCount = 0;
62 static CachedConnection *pasCachedConnections = NULL;
63
64 typedef struct {
65 OGREnvelope sExtent;
66 char *pszIndFile;
67 char *pszCoordSys;
68 IFMESpatialIndex *poIndex;
69 OGRwkbGeometryType eBestGeomType;
70 } CacheLayerInfo;
71
72 /************************************************************************/
73 /* FME_Logger() */
74 /* */
75 /* Output that would normally go to the FME log file will */
76 /* instead be redirected through this function. */
77 /************************************************************************/
78
FME_Logger(FME_MsgLevel severity,const char * message)79 void FME_Logger( FME_MsgLevel severity, const char *message )
80
81 {
82 char *pszMessageCopy = CPLStrdup(message);
83
84 if( pszMessageCopy[strlen(pszMessageCopy)-1] == '\n' )
85 pszMessageCopy[strlen(pszMessageCopy)-1] = '\0';
86
87 CPLDebug( "FME_LOG", "%d:%s", severity, pszMessageCopy );
88
89 CPLFree( pszMessageCopy );
90 }
91
92 /************************************************************************/
93 /* GetTmpDir() */
94 /************************************************************************/
95
GetTmpDir()96 static const char *GetTmpDir()
97
98 {
99 const char *pszTmpDir;
100
101 pszTmpDir = getenv("OGRFME_TMPDIR");
102 if( pszTmpDir == NULL )
103 pszTmpDir = getenv("TMPDIR");
104 if( pszTmpDir == NULL )
105 pszTmpDir = getenv("TEMPDIR");
106 if( pszTmpDir == NULL ) //20020419 - ryan
107 pszTmpDir = getenv("TMP");
108 if( pszTmpDir == NULL )
109 pszTmpDir = getenv("TEMP");
110 if( pszTmpDir == NULL )
111 {
112 #ifdef WIN32
113 pszTmpDir = "C:\\";
114 #else
115 pszTmpDir = "/tmp";
116 #endif
117 }
118
119 return pszTmpDir;
120 }
121
122 /************************************************************************/
123 /* BuildTmpName() */
124 /* */
125 /* Create a basename for the temporary file for a given layer */
126 /* on this dataset. */
127 /************************************************************************/
128
BuildTmpName(const char * pszLayerName)129 static char *BuildTmpName( const char *pszLayerName )
130
131 {
132 int i;
133 char szFilename[2048];
134 VSIStatBuf sStat;
135 const char *pszTmpDir = GetTmpDir();
136
137 /* -------------------------------------------------------------------- */
138 /* Look for an unused name. */
139 /* -------------------------------------------------------------------- */
140 for( i = -1; true; i++ )
141 {
142 if( i == -1 )
143 sprintf( szFilename, "%s%c%s_%s",
144 pszTmpDir, PATH_CHAR, kPROVIDERNAME, pszLayerName );
145 else
146 sprintf( szFilename, "%s%c%s_%s_%d",
147 pszTmpDir, PATH_CHAR, kPROVIDERNAME, pszLayerName, i );
148
149 if( VSIStat( szFilename, &sStat ) != 0 )
150 break;
151 }
152
153 return CPLStrdup( szFilename );
154 }
155
156 /************************************************************************/
157 /* OGRFMEDataSource() */
158 /************************************************************************/
159
OGRFMEDataSource()160 OGRFMEDataSource::OGRFMEDataSource()
161
162 {
163 pszName = NULL;
164 pszDataset = NULL;
165 pszReaderName = NULL;
166 poSession = NULL;
167 poReader = NULL;
168 poFMEFeature = NULL;
169
170 nLayers = 0;
171 papoLayers = NULL;
172
173 poUserDirectives = NULL;
174
175 bUseCaching = FALSE;
176 poFMEString = NULL;
177 bCoordSysOverride = FALSE;
178 }
179
180 /************************************************************************/
181 /* ~OGRFMEDataSource() */
182 /************************************************************************/
183
~OGRFMEDataSource()184 OGRFMEDataSource::~OGRFMEDataSource()
185
186 {
187 CPLDebug( kPROVIDERNAME, "~OGRFMEDataSource(): %p", this );
188
189 if( poSharedSession == NULL )
190 return;
191
192 AcquireSession();
193
194 /* -------------------------------------------------------------------- */
195 /* Destroy the layers, so we know we don't still have the */
196 /* caches open when we dereference them. */
197 /* -------------------------------------------------------------------- */
198 for( int i = 0; i < nLayers; i++ )
199 delete papoLayers[i];
200
201 CPLFree( papoLayers );
202
203 /* -------------------------------------------------------------------- */
204 /* If we have a cached instances, decrement the reference count. */
205 /* -------------------------------------------------------------------- */
206 #ifdef SUPPORT_PERSISTENT_CACHE
207 {
208 OGRFMECacheIndex oCacheIndex(
209 CPLFormFilename(GetTmpDir(), "ogrfmeds", "ind" ) );
210
211 if( pszReaderName != NULL && nLayers > 0
212 && bUseCaching && oCacheIndex.Lock() && oCacheIndex.Load() )
213 {
214 CPLXMLNode *psMatchDS = NULL;
215
216 psMatchDS = oCacheIndex.FindMatch( pszReaderName, pszDataset,
217 *poUserDirectives );
218
219 if( psMatchDS != NULL )
220 oCacheIndex.Dereference( psMatchDS );
221
222 if( oCacheIndex.ExpireOldCaches( poSession ) || psMatchDS != NULL )
223 oCacheIndex.Save();
224
225 oCacheIndex.Unlock();
226 }
227 }
228 #endif /* def SUPPORT_PERSISTENT_CACHE */
229
230 /* -------------------------------------------------------------------- */
231 /* Cleanup up various resources. */
232 /* -------------------------------------------------------------------- */
233 if( poFMEFeature != NULL )
234 poSession->destroyFeature( poFMEFeature );
235
236 if( poUserDirectives != NULL )
237 poSession->destroyStringArray( poUserDirectives );
238
239 if( poReader != NULL )
240 {
241 if( !IsPartOfConnectionCache( poReader ) )
242 poSession->destroyReader( poReader );
243 else
244 CPLDebug( kPROVIDERNAME, "Preserving cached reader on destructor");
245 }
246
247 if( poSession != NULL )
248 {
249 if( --nSharedSessionRefCount == 0 )
250 {
251 #ifdef SUPPORT_CLEANUP_SESSION
252 #ifdef SUPPORT_INDIRECT_FMEDLL
253 int (*pfnFME_destroySession)(void *);
254
255 pfnFME_destroySession = (int (*)(void*))
256 CPLGetSymbol(FMEDLL_NAME, "FME_DestroySession" );
257 if( pfnFME_destroySession == NULL )
258 CPLError( CE_Warning, CPLE_AppDefined,
259 "Failed to fetch FME_DestroySession entry point." );
260 else
261 pfnFME_destroySession( (void *) (&poSession) );
262 #else
263 FME_destroySession( poSession );
264 #endif // def SUPPORT_INDIRECT_FMEDLL
265 poSharedSession = NULL;
266 #else // ndef SUPPORT_CLEANUP_SESSION
267 CPLDebug( kPROVIDERNAME, "no active datasources left, but preserving session." );
268 #endif
269 }
270 }
271
272 CPLFree( pszName );
273 CPLFree( pszDataset );
274 CPLFree( pszReaderName );
275
276 ReleaseSession();
277 }
278
279 /************************************************************************/
280 /* PromptForSource() */
281 /************************************************************************/
282
PromptForSource()283 char *OGRFMEDataSource::PromptForSource()
284
285 {
286 IFMEDialog *poDialog = NULL;
287 IFMEString *poSourceFormat, *poSourceDSName;
288 char *pszResult = NULL;
289
290 poSourceFormat = poSession->createString();
291 poSourceDSName = poSession->createString();
292
293 if( poSession->createDialog( poDialog ) != 0 || poDialog == nullptr )
294 return NULL;
295
296 poUserDirectives->append( "SPATIAL_SETTINGS" );
297 poUserDirectives->append( "no" );
298 if( poDialog->sourcePrompt( NULL, NULL, *poSourceFormat, *poSourceDSName,
299 *poUserDirectives ) )
300 {
301 pszResult = CPLStrdup(CPLSPrintf("%s:%s",
302 poSourceFormat->data(),
303 poSourceDSName->data()));
304 }
305
306 poSession->destroyString( poSourceFormat );
307 poSession->destroyString( poSourceDSName );
308
309 return pszResult;
310 }
311
312 /************************************************************************/
313 /* ReadFileSource() */
314 /************************************************************************/
315
ReadFileSource(const char * pszFilename)316 char *OGRFMEDataSource::ReadFileSource( const char *pszFilename )
317
318 {
319 FILE *fp;
320 char **papszLines = NULL;
321 const char *pszLine;
322
323 /* -------------------------------------------------------------------- */
324 /* Read the definition file. */
325 /* -------------------------------------------------------------------- */
326 fp = VSIFOpen( pszFilename, "rt" );
327 if( fp == NULL )
328 {
329 CPLError( CE_Failure, CPLE_AppDefined,
330 "Failed to open file %s.",
331 pszFilename );
332 return NULL;
333 }
334
335 while( (pszLine = CPLReadLine(fp)) != NULL )
336 {
337 if( *pszLine != '#' )
338 papszLines = CSLAddString( papszLines, pszLine );
339 }
340
341 VSIFClose( fp );
342
343 /* -------------------------------------------------------------------- */
344 /* verify minimal requirements. */
345 /* -------------------------------------------------------------------- */
346 if( CSLCount(papszLines) < 2 )
347 {
348 CPLError( CE_Failure, CPLE_AppDefined,
349 "Insufficient lines in FME Data Definition file."
350 "At least a readername and data source name is required." );
351 return NULL;
352 }
353
354 /* -------------------------------------------------------------------- */
355 /* Apply extra values to user directives. */
356 /* -------------------------------------------------------------------- */
357 int i;
358
359 for( i = 2; papszLines[i] != NULL; i++ )
360 poUserDirectives->append( papszLines[i] );
361
362 /* -------------------------------------------------------------------- */
363 /* Prepare reader:dataset response string. */
364 /* -------------------------------------------------------------------- */
365 char *pszReturn;
366
367 pszReturn = CPLStrdup(CPLSPrintf("%s:%s", papszLines[0], papszLines[1] ));
368
369 CSLDestroy( papszLines );
370
371 return pszReturn;
372 }
373
374 /************************************************************************/
375 /* SaveDefinitionFile() */
376 /************************************************************************/
377
SaveDefinitionFile(const char * pszFilename,const char * pszReader,const char * pszDatasource,IFMEStringArray & oUserDirectives)378 static void SaveDefinitionFile( const char *pszFilename,
379 const char *pszReader,
380 const char *pszDatasource,
381 IFMEStringArray &oUserDirectives )
382
383 {
384 FILE *fp;
385 int i;
386
387 fp = VSIFOpen( CPLResetExtension( pszFilename, "fdd" ), "wt" );
388 if( fp == NULL )
389 return;
390
391 fprintf( fp, "%s\n", pszReader );
392 fprintf( fp, "%s\n", pszDatasource );
393
394 for( i = 0; i < (int) oUserDirectives.entries(); i++ )
395 {
396 fprintf( fp, "%s\n", oUserDirectives(i) );
397 }
398
399 VSIFClose( fp );
400 }
401
402 /************************************************************************/
403 /* ExtractSRS() */
404 /************************************************************************/
405
406 OGRSpatialReference *
ExtractSRS()407 OGRFMEDataSource::ExtractSRS()
408
409 {
410 /* -------------------------------------------------------------------- */
411 /* Try to find the COORDSYS in the user directives. */
412 /* -------------------------------------------------------------------- */
413 const char *pszCoordSys = NULL;
414 for( int i = 0; i < (int) poUserDirectives->entries(); i += 2 )
415 {
416 if( EQUAL((*poUserDirectives)(i),"COORDSYS") )
417 pszCoordSys = (*poUserDirectives)(i+1);
418 }
419
420 if( pszCoordSys == NULL || strlen(pszCoordSys) == 0 )
421 return NULL;
422
423 /* -------------------------------------------------------------------- */
424 /* Translate FME name to an OGRSpatialReference. */
425 /* -------------------------------------------------------------------- */
426 return FME2OGRSpatialRef( pszCoordSys );
427 }
428
429 /************************************************************************/
430 /* Open() */
431 /************************************************************************/
432
Open(const char * pszCompositeName)433 int OGRFMEDataSource::Open( const char * pszCompositeName )
434
435 {
436 FME_MsgNum err;
437
438 CPLAssert( poSession == NULL ); // only open once
439
440 /* -------------------------------------------------------------------- */
441 /* Do some initial validation. Does this even look like it */
442 /* could plausibly be an FME suitable name? We accept PROMPT:, */
443 /* <reader>: or anything ending in .fdd as a reasonable candidate. */
444 /* -------------------------------------------------------------------- */
445 int i;
446
447 for( i = 0; pszCompositeName[i] != ':' && pszCompositeName[i] != '\0'; i++)
448 {
449 if( pszCompositeName[i] == '/' || pszCompositeName[i] == '\\'
450 || pszCompositeName[i] == '.' )
451 break;
452 }
453
454 if( (i < 2 || pszCompositeName[i] != ':'
455 || STARTS_WITH_CI(pszCompositeName, "OCI:")
456 || STARTS_WITH_CI(pszCompositeName, "gltp:")
457 || STARTS_WITH_CI(pszCompositeName, "http")
458 || STARTS_WITH_CI(pszCompositeName, "DODS:")
459 || STARTS_WITH_CI(pszCompositeName, "ODBC:")
460 || STARTS_WITH_CI(pszCompositeName, "MYSQL:"))
461 && !EQUAL(CPLGetExtension( pszCompositeName ), "fdd")
462 && !STARTS_WITH_CI(pszCompositeName, "PROMPT") )
463 {
464 CPLDebug( kPROVIDERNAME,
465 "OGRFMEDataSource::Open(%s) don't try to open via FME.",
466 pszCompositeName );
467 return FALSE;
468 }
469
470 CPLDebug( kPROVIDERNAME, "OGRFMEDataSource::Open(%s):%p/%ld",
471 pszCompositeName, this, (long) CPLGetPID() );
472
473 /* -------------------------------------------------------------------- */
474 /* Create an FME Session. */
475 /* -------------------------------------------------------------------- */
476 poSession = AcquireSession();
477 if( poSession == NULL )
478 return FALSE;
479
480 nSharedSessionRefCount++;
481
482 CPLDebug( kPROVIDERNAME, "%p:acquired session", this );
483
484 poUserDirectives = poSession->createStringArray();
485
486 /* -------------------------------------------------------------------- */
487 /* Redirect FME log messages through CPLDebug(). */
488 /* -------------------------------------------------------------------- */
489 IFMELogFile *poLogFile = poSession->logFile();
490
491 poLogFile->setFileName( NULL, FME_FALSE );
492 poLogFile->setCallBack( FME_Logger );
493
494 CPLDebug( kPROVIDERNAME, "%p:reset logfile", this );
495
496 /* -------------------------------------------------------------------- */
497 /* Prompt for a source, if none is provided. */
498 /* -------------------------------------------------------------------- */
499 if( EQUAL(pszCompositeName,"") || STARTS_WITH_CI(pszCompositeName, "PROMPT") )
500 {
501 pszName = PromptForSource();
502 if( pszName == NULL )
503 {
504 ReleaseSession();
505 return FALSE;
506 }
507 }
508 else if( CPLGetExtension( pszCompositeName ) != NULL
509 && EQUAL(CPLGetExtension( pszCompositeName ),"fdd") )
510 {
511 pszName = ReadFileSource(pszCompositeName);
512 if( pszName == NULL )
513 {
514 ReleaseSession();
515 return FALSE;
516 }
517 }
518 else
519 {
520 pszName = CPLStrdup( pszCompositeName );
521 }
522
523 /* -------------------------------------------------------------------- */
524 /* Extract the reader name and password components. The */
525 /* reader name will be followed by a single colon and then the */
526 /* FME DATASET name. */
527 /* -------------------------------------------------------------------- */
528 for( i = 0; pszName[i] != '\0' && pszName[i] != ':'; i++ ) {}
529
530 if( pszName[i] == '\0' || i < 2 )
531 {
532 CPLError( CE_Failure, CPLE_AppDefined,
533 "Failed to parse reader and data source from:\n%s",
534 pszName );
535 ReleaseSession();
536 return FALSE;
537 }
538
539 pszReaderName = CPLStrdup( pszName );
540 pszReaderName[i] = '\0';
541
542 pszDataset = CPLStrdup(pszName + i + 1);
543
544 CPLDebug( kPROVIDERNAME, "%s:parsed out dataset", pszDataset );
545
546 /* -------------------------------------------------------------------- */
547 /* If we prompted for a definition that includes a file to save */
548 /* it to, do the save now. */
549 /* -------------------------------------------------------------------- */
550 if( STARTS_WITH_CI(pszCompositeName, "PROMPT:")
551 && strlen(pszCompositeName) > 7 )
552 {
553 SaveDefinitionFile( pszCompositeName+7,
554 pszReaderName, pszDataset,
555 *poUserDirectives );
556 }
557
558 /* -------------------------------------------------------------------- */
559 /* Is there a Coordsys statement in the user directives? */
560 /* -------------------------------------------------------------------- */
561 OGRSpatialReference *poSRS = ExtractSRS();
562
563 CPLDebug( kPROVIDERNAME, "got the SRS parsed");
564
565 bCoordSysOverride = poSRS != NULL;
566
567 /* -------------------------------------------------------------------- */
568 /* Allocate an FME string, and feature for use here and */
569 /* elsewhere. */
570 /* -------------------------------------------------------------------- */
571 poFMEFeature = poSession->createFeature();
572 poFMEString = poSession->createString();
573
574 /* -------------------------------------------------------------------- */
575 /* Are we going to use the direct access DB mechanism, or the */
576 /* spatially cached (dumb reader) mechanism. */
577 /* -------------------------------------------------------------------- */
578 bUseCaching = !STARTS_WITH_CI(pszReaderName, "SDE")
579 && !STARTS_WITH_CI(pszReaderName, "ORACLE");
580
581 /* -------------------------------------------------------------------- */
582 /* Is there already a cache for this dataset? If so, we will */
583 /* use it. */
584 /* -------------------------------------------------------------------- */
585 #ifdef SUPPORT_PERSISTENT_CACHE
586 OGRFMECacheIndex oCacheIndex(
587 CPLFormFilename(GetTmpDir(), "ogrfmeds", "ind" ) );
588 CPLXMLNode *psMatchDS = NULL;
589
590 if( bUseCaching && oCacheIndex.Lock() && oCacheIndex.Load() )
591 {
592 int bNeedSave = oCacheIndex.ExpireOldCaches( poSession );
593
594 psMatchDS = oCacheIndex.FindMatch( pszReaderName, pszDataset,
595 *poUserDirectives );
596
597 if( psMatchDS != NULL )
598 {
599 oCacheIndex.Reference( psMatchDS );
600 oCacheIndex.Save();
601
602 psMatchDS = CPLCloneXMLTree( psMatchDS );
603 oCacheIndex.Unlock();
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 BuildTmpName( 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 = ProcessGeometry( poLayer, poSrcFeature,
1297 poLayer->GetLayerDefn()->GetGeomType() );
1298 if( poOGRGeom != NULL )
1299 poFeature->SetGeometryDirectly( poOGRGeom );
1300
1301 return poFeature;
1302 }
1303
1304 /************************************************************************/
1305 /* TestCapability() */
1306 /************************************************************************/
1307
TestCapability(const char *)1308 int OGRFMEDataSource::TestCapability( const char * )
1309
1310 {
1311 return FALSE;
1312 }
1313
1314 /************************************************************************/
1315 /* OfferForConnectionCaching() */
1316 /* */
1317 /* Sometimes we want to keep a prototype reader open to */
1318 /* maintain a connection, for instance to SDE where creating */
1319 /* the connection is pretty expensive. */
1320 /************************************************************************/
1321
OfferForConnectionCaching(IFMEUniversalReader * poReader,const char * pszReaderType,const char * pszDataset)1322 void OGRFMEDataSource::OfferForConnectionCaching(IFMEUniversalReader *poReader,
1323 const char *pszReaderType,
1324 const char *pszDataset)
1325
1326 {
1327 /* -------------------------------------------------------------------- */
1328 /* For now we only cache SDE readers. */
1329 /* -------------------------------------------------------------------- */
1330 if( !STARTS_WITH_CI(pszReaderType, "SDE")
1331 && !STARTS_WITH_CI(pszReaderType, "ORACLE") )
1332 return;
1333
1334 /* -------------------------------------------------------------------- */
1335 /* We want to build a definition of this connection that */
1336 /* indicates a unique connection. For now we base it on the */
1337 /* Server, UserName, Password, and Instance values. We will */
1338 /* pick these all out of the RUNTIME_MACROS if present. */
1339 /* */
1340 /* First find the runtime macros. */
1341 /* -------------------------------------------------------------------- */
1342 const char *pszRuntimeMacros = NULL;
1343 int i;
1344
1345 for( i = 0; i < (int) poUserDirectives->entries()-1; i += 2 )
1346 {
1347 if( STARTS_WITH_CI((const char *) (*poUserDirectives)(i), "RUNTIME_MACROS") )
1348 pszRuntimeMacros = (*poUserDirectives)(i+1);
1349 }
1350
1351 /* -------------------------------------------------------------------- */
1352 /* Break into name/value pairs. */
1353 /* -------------------------------------------------------------------- */
1354 char **papszTokens = NULL;
1355
1356 if( pszRuntimeMacros != NULL )
1357 papszTokens = CSLTokenizeStringComplex( pszRuntimeMacros, ",",
1358 TRUE, TRUE);
1359
1360 /* -------------------------------------------------------------------- */
1361 /* Look for Name values we want, and append them to the */
1362 /* definition string. */
1363 /* -------------------------------------------------------------------- */
1364 char szDefinition[5000];
1365
1366 sprintf( szDefinition, "%s::", pszDataset );
1367
1368 for( i = 0; i < CSLCount(papszTokens)-1; i += 2 )
1369 {
1370 if( strstr(papszTokens[i],"Server") != NULL
1371 || strstr(papszTokens[i],"Service") != NULL
1372 || strstr(papszTokens[i],"UserName") != NULL
1373 || strstr(papszTokens[i],"Password") != NULL
1374 || strstr(papszTokens[i],"Instance") != NULL )
1375 {
1376 if( strlen(papszTokens[i+1]) + strlen(papszTokens[i]) + 20
1377 < sizeof(szDefinition) - strlen(szDefinition) )
1378 {
1379 sprintf( szDefinition + strlen(szDefinition), "%s=%s;",
1380 papszTokens[i], papszTokens[i+1] );
1381 }
1382 }
1383 }
1384
1385 /* -------------------------------------------------------------------- */
1386 /* Do we already have a reader cached for this definition? */
1387 /* -------------------------------------------------------------------- */
1388 for( i = 0; i < nCachedConnectionCount; i++ )
1389 {
1390 if( strcmp(szDefinition, pasCachedConnections[i].pszDefinition) == 0 )
1391 return;
1392 }
1393
1394 /* -------------------------------------------------------------------- */
1395 /* Added this reader to the cache. */
1396 /* -------------------------------------------------------------------- */
1397 CPLDebug( kPROVIDERNAME,
1398 "Caching IFMEUniversalReader to maintain connection.\n"
1399 "ReaderType=%s, Definition=%s",
1400 pszReaderType, szDefinition );
1401
1402 nCachedConnectionCount++;
1403 pasCachedConnections = (CachedConnection *)
1404 CPLRealloc(pasCachedConnections,
1405 sizeof(CachedConnection) * nCachedConnectionCount);
1406
1407 pasCachedConnections[nCachedConnectionCount-1].poReader = poReader;
1408 pasCachedConnections[nCachedConnectionCount-1].pszReaderType =
1409 CPLStrdup(pszReaderType);
1410 pasCachedConnections[nCachedConnectionCount-1].pszDefinition =
1411 CPLStrdup(szDefinition);
1412 }
1413
1414 /************************************************************************/
1415 /* IsPartOfConnectionCache() */
1416 /* */
1417 /* I this reader being used to maintain a connection cache? */
1418 /************************************************************************/
IsPartOfConnectionCache(IFMEUniversalReader * poReader)1419 int OGRFMEDataSource::IsPartOfConnectionCache( IFMEUniversalReader *poReader )
1420
1421 {
1422 int i;
1423
1424 for( i = 0; i < nCachedConnectionCount; i++ )
1425 if( poReader == pasCachedConnections[i].poReader )
1426 return TRUE;
1427
1428 return FALSE;
1429 }
1430
1431 /************************************************************************/
1432 /* AcquireSession() */
1433 /* */
1434 /* Get unique ownership of the FME session for this thread. */
1435 /************************************************************************/
1436
AcquireSession()1437 IFMESession *OGRFMEDataSource::AcquireSession()
1438
1439 {
1440 FME_MsgNum err;
1441
1442 /* -------------------------------------------------------------------- */
1443 /* Create session mutex if we don't already have one. */
1444 /* -------------------------------------------------------------------- */
1445 if( hSessionMutex == NULL )
1446 {
1447 hSessionMutex = CPLCreateMutex();
1448
1449 CPLDebug( kPROVIDERNAME, "%p:Creating FME session, mutex=%d.",
1450 this, hSessionMutex );
1451 }
1452
1453 /* -------------------------------------------------------------------- */
1454 /* Try to acquire ownership of the session, even if the session */
1455 /* doesn't yet exist. */
1456 /* -------------------------------------------------------------------- */
1457 else
1458 {
1459 #ifdef DEBUG_MUTEX
1460 CPLDebug( kPROVIDERNAME, "%p:Wait for session mutex.", this );
1461 #endif
1462
1463 if( !CPLAcquireMutex( hSessionMutex, 5.0 ) )
1464 {
1465 CPLDebug( kPROVIDERNAME, "%p:Failed to acquire session mutex in 5s.",
1466 this );
1467 }
1468
1469 #ifdef DEBUG_MUTEX
1470 else
1471 CPLDebug( kPROVIDERNAME, "%p:Got session mutex.", this );
1472 #endif
1473 }
1474
1475 /* -------------------------------------------------------------------- */
1476 /* If the session doesn't exist, create it now. */
1477 /* -------------------------------------------------------------------- */
1478 if( poSharedSession == NULL )
1479 {
1480 #ifdef SUPPORT_INDIRECT_FMEDLL
1481 FME_MsgNum (*pfnFME_CreateSession)( void * );
1482 pfnFME_CreateSession = (FME_MsgNum (*)(void*))
1483 CPLGetSymbol( FMEDLL_NAME, "FME_CreateSession" );
1484 if( pfnFME_CreateSession == NULL )
1485 {
1486 CPLReleaseMutex( hSessionMutex );
1487 CPLDebug( kPROVIDERNAME, "Unable to load FME_CreateSession from %s, skipping FME Driver.", FMEDLL_NAME );
1488 return NULL;
1489 }
1490
1491 err = pfnFME_CreateSession( (void *) (&poSharedSession) );
1492 #else
1493 err = FME_createSession(poSharedSession);
1494 #endif
1495 if( err )
1496 {
1497 poSharedSession = NULL;
1498 CPLReleaseMutex( hSessionMutex );
1499 CPLError( CE_Failure, CPLE_AppDefined,
1500 "Failed to create FMESession." );
1501 return NULL;
1502 }
1503
1504 // Dale Nov 26 '01 -- Set up to log "badnews" from FME
1505 // to help track down problems
1506
1507 IFMEStringArray *poSessionDirectives =
1508 poSharedSession->createStringArray();
1509
1510 if( poSessionDirectives == NULL )
1511 {
1512 err = 1;
1513 CPLError( CE_Warning, CPLE_AppDefined,
1514 "Something has gone wonky with createStringArray() on the IFMESession.\n"
1515 "Is it possible you built with gcc 3.2 on Linux? This seems problematic." );
1516 }
1517 else
1518 {
1519 poSessionDirectives->append("FME_DEBUG");
1520 poSessionDirectives->append("BADNEWS");
1521
1522 err = poSharedSession->init( poSessionDirectives );
1523
1524 poSharedSession->destroyStringArray( poSessionDirectives );
1525
1526 if( err )
1527 {
1528 CPLError( CE_Warning, CPLE_AppDefined,
1529 "Failed to initialize FMESession.\n%s",
1530 poSharedSession->getLastErrorMsg());
1531 }
1532 }
1533
1534 if( err )
1535 {
1536 #ifdef SUPPORT_INDIRECT_FMEDLL
1537 int (*pfnFME_destroySession)(void *);
1538
1539 pfnFME_destroySession = (int (*)(void*))
1540 CPLGetSymbol(FMEDLL_NAME, "FME_DestroySession" );
1541 if( pfnFME_destroySession != NULL )
1542 pfnFME_destroySession( (void *) (&poSharedSession) );
1543 #else
1544 FME_destroySession( poSharedSession );
1545 #endif // def SUPPORT_INDIRECT_FMEDLL
1546
1547 poSharedSession = NULL;
1548 CPLReleaseMutex( hSessionMutex );
1549 return NULL;
1550 }
1551 }
1552
1553 return poSharedSession;
1554 }
1555
1556 /************************************************************************/
1557 /* ReleaseSession() */
1558 /* */
1559 /* Release the lock on the FME session. */
1560 /************************************************************************/
1561
ReleaseSession()1562 void OGRFMEDataSource::ReleaseSession()
1563
1564 {
1565 #ifdef DEBUG_MUTEX
1566 CPLDebug( kPROVIDERNAME, "%p:Release session mutex.", this );
1567 #endif
1568
1569 CPLReleaseMutex( hSessionMutex );
1570 }
1571
1572 /************************************************************************/
1573 /* SerializeToXML() */
1574 /* */
1575 /* Convert the information about this datasource, and its */
1576 /* layers into an XML format that can be stored in the */
1577 /* persistent feature cache index. */
1578 /************************************************************************/
1579
SerializeToXML()1580 CPLXMLNode *OGRFMEDataSource::SerializeToXML()
1581
1582 {
1583 CPLXMLNode *psDS;
1584
1585 CPLAssert( bUseCaching );
1586
1587 /* -------------------------------------------------------------------- */
1588 /* Setup data source information. */
1589 /* -------------------------------------------------------------------- */
1590 psDS = CPLCreateXMLNode( NULL, CXT_Element, "DataSource" );
1591
1592 CPLCreateXMLElementAndValue( psDS, "Driver", pszReaderName );
1593 CPLCreateXMLElementAndValue( psDS, "DSName", pszDataset );
1594 CPLCreateXMLElementAndValue( psDS, "RefCount", "0" );
1595 CPLCreateXMLElementAndValue( psDS, "CreationTime", "0" );
1596 CPLCreateXMLElementAndValue( psDS, "LastUseTime", "0" );
1597
1598 /* -------------------------------------------------------------------- */
1599 /* Append all the FME user directives in force. */
1600 /* -------------------------------------------------------------------- */
1601 CPLXMLNode *psUD;
1602
1603 psUD = CPLCreateXMLNode( psDS, CXT_Element, "UserDirectives" );
1604 for( int i = 0; i < (int) poUserDirectives->entries(); i++ )
1605 CPLCreateXMLElementAndValue( psUD, "Directive",
1606 (*poUserDirectives)(i) );
1607
1608 /* -------------------------------------------------------------------- */
1609 /* Now append all the layer information. */
1610 /* -------------------------------------------------------------------- */
1611 for( int iLayer = 0; iLayer < nLayers; iLayer++ )
1612 {
1613 OGRFMELayerCached * poLayer = (OGRFMELayerCached *) papoLayers[iLayer];
1614 CPLXMLNode *psLayer;
1615
1616 psLayer = poLayer->SerializeToXML();
1617 CPLAddXMLChild( psDS, psLayer );
1618 }
1619
1620 return psDS;
1621 }
1622
1623 /************************************************************************/
1624 /* InitializeFromXML() */
1625 /************************************************************************/
1626
InitializeFromXML(CPLXMLNode * psDS)1627 int OGRFMEDataSource::InitializeFromXML( CPLXMLNode *psDS )
1628
1629 {
1630 CPLAssert( bUseCaching );
1631
1632 /* -------------------------------------------------------------------- */
1633 /* Loop over layers, instantiating from the cached data. */
1634 /* -------------------------------------------------------------------- */
1635 CPLXMLNode *psLayerN;
1636
1637 for( psLayerN = psDS->psChild; psLayerN != NULL;
1638 psLayerN = psLayerN->psNext )
1639 {
1640 OGRFMELayerCached *poNewLayer;
1641
1642 if( !EQUAL(psLayerN->pszValue,"OGRLayer") )
1643 continue;
1644
1645 poNewLayer = new OGRFMELayerCached( this );
1646
1647 /* -------------------------------------------------------------------- */
1648 /* Initialize the layer from the XML. */
1649 /* -------------------------------------------------------------------- */
1650 if( !poNewLayer->InitializeFromXML( psLayerN ) )
1651 {
1652 // this is *not* proper cleanup
1653 CPLAssert( FALSE );
1654 nLayers = 0;
1655
1656 return FALSE;
1657 }
1658
1659 /* -------------------------------------------------------------------- */
1660 /* Assign the spatial index. We should really change this to */
1661 /* check if it succeeds! */
1662 /* -------------------------------------------------------------------- */
1663 poNewLayer->AssignIndex(
1664 CPLGetXMLValue( psLayerN, "SpatialCacheName",
1665 "<missing cachename>" ),
1666 NULL, NULL );
1667
1668 /* -------------------------------------------------------------------- */
1669 /* Add the layer to the layer list. */
1670 /* -------------------------------------------------------------------- */
1671 papoLayers = (OGRFMELayer **)
1672 CPLRealloc(papoLayers, sizeof(void*) * ++nLayers );
1673 papoLayers[nLayers-1] = poNewLayer;
1674 }
1675
1676 return TRUE;
1677 }
1678
1679 /************************************************************************/
1680 /* FME2OGRSpatialRef() */
1681 /* */
1682 /* Translate an FME coordinate system into an */
1683 /* OGRSpatialReference using the coordinate system manager */
1684 /* getCoordSysAsOGCDef() method. We assume the session has */
1685 /* already been acquired. */
1686 /************************************************************************/
1687
1688 OGRSpatialReference *
FME2OGRSpatialRef(const char * pszCoordsys)1689 OGRFMEDataSource::FME2OGRSpatialRef( const char *pszCoordsys )
1690
1691 {
1692 IFMEString *poOGCDef;
1693
1694 poOGCDef = poSession->createString();
1695
1696 poSession->coordSysManager()->getCoordSysAsOGCDef(
1697 pszCoordsys, *poOGCDef );
1698
1699 const char *pszWKT = poOGCDef->data();
1700 OGRSpatialReference oSRS;
1701
1702 if( oSRS.importFromWkt( pszWKT ) == OGRERR_NONE )
1703 {
1704 poSession->destroyString( poOGCDef );
1705 return oSRS.Clone();
1706 }
1707 else
1708 {
1709 poSession->destroyString( poOGCDef );
1710 return NULL;
1711 }
1712 }
1713