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