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