LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/mitab - mitab_tabview.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 663 0 0.0 %
Date: 2012-04-28 Functions: 48 0 0.0 %

       1                 : /**********************************************************************
       2                 :  * $Id: mitab_tabview.cpp,v 1.22 2010-07-07 19:00:15 aboudreault Exp $
       3                 :  *
       4                 :  * Name:     mitab_tabfile.cpp
       5                 :  * Project:  MapInfo TAB Read/Write library
       6                 :  * Language: C++
       7                 :  * Purpose:  Implementation of the TABView class, used to handle .TAB
       8                 :  *           datasets composed of a number of .TAB files linked through 
       9                 :  *           indexed fields.
      10                 :  * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
      11                 :  *
      12                 :  **********************************************************************
      13                 :  * Copyright (c) 1999-2002, Daniel Morissette
      14                 :  *
      15                 :  * Permission is hereby granted, free of charge, to any person obtaining a
      16                 :  * copy of this software and associated documentation files (the "Software"),
      17                 :  * to deal in the Software without restriction, including without limitation
      18                 :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      19                 :  * and/or sell copies of the Software, and to permit persons to whom the
      20                 :  * Software is furnished to do so, subject to the following conditions:
      21                 :  * 
      22                 :  * The above copyright notice and this permission notice shall be included
      23                 :  * in all copies or substantial portions of the Software.
      24                 :  * 
      25                 :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
      26                 :  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      27                 :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
      28                 :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      29                 :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      30                 :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 
      31                 :  * DEALINGS IN THE SOFTWARE.
      32                 :  **********************************************************************
      33                 :  *
      34                 :  * $Log: mitab_tabview.cpp,v $
      35                 :  * Revision 1.22  2010-07-07 19:00:15  aboudreault
      36                 :  * Cleanup Win32 Compile Warnings (GDAL bug #2930)
      37                 :  *
      38                 :  * Revision 1.21  2010-07-05 19:01:20  aboudreault
      39                 :  * Reverted last SetFeature change in mitab_capi.cpp and fixed another memory leak
      40                 :  *
      41                 :  * Revision 1.20  2010-01-07 20:39:12  aboudreault
      42                 :  * Added support to handle duplicate field names, Added validation to check if a field name start with a number (bug 2141)
      43                 :  *
      44                 :  * Revision 1.19  2008-03-05 20:35:39  dmorissette
      45                 :  * Replace MITAB 1.x SetFeature() with a CreateFeature() for V2.x (bug 1859)
      46                 :  *
      47                 :  * Revision 1.18  2008/01/29 20:46:32  dmorissette
      48                 :  * Added support for v9 Time and DateTime fields (byg 1754)
      49                 :  *
      50                 :  * Revision 1.17  2007/06/21 14:00:23  dmorissette
      51                 :  * Added missing cast in isspace() calls to avoid failed assertion on Windows
      52                 :  * (MITAB bug 1737, GDAL ticket 1678))
      53                 :  *
      54                 :  * Revision 1.16  2007/06/12 13:52:38  dmorissette
      55                 :  * Added IMapInfoFile::SetCharset() method (bug 1734)
      56                 :  *
      57                 :  * Revision 1.15  2007/06/12 12:50:40  dmorissette
      58                 :  * Use Quick Spatial Index by default until bug 1732 is fixed (broken files
      59                 :  * produced by current coord block splitting technique).
      60                 :  *
      61                 :  * Revision 1.14  2007/03/21 21:15:56  dmorissette
      62                 :  * Added SetQuickSpatialIndexMode() which generates a non-optimal spatial
      63                 :  * index but results in faster write time (bug 1669)
      64                 :  *
      65                 :  * Revision 1.13  2004/06/30 20:29:04  dmorissette
      66                 :  * Fixed refs to old address danmo@videotron.ca
      67                 :  *
      68                 :  * Revision 1.12  2002/02/22 20:44:51  julien
      69                 :  * Prevent infinite loop with TABRelation by suppress the m_poCurFeature object
      70                 :  * from the class and setting it in the calling function and add GetFeature in
      71                 :  * the class. (bug 706)
      72                 :  *
      73                 :  * Revision 1.11  2002/01/10 05:13:22  daniel
      74                 :  * Prevent crash if .IND file is deleted (but 703)
      75                 :  *
      76                 :  * Revision 1.10  2002/01/10 04:52:58  daniel
      77                 :  * Support 'select * ...' syntax + 'open table..." directives with/without .tab
      78                 :  *
      79                 :  * Revision 1.9  2001/06/27 19:52:26  warmerda
      80                 :  * use VSIUnlink() instead of unlink()
      81                 :  *
      82                 :  * Revision 1.8  2001/03/15 03:57:51  daniel
      83                 :  * Added implementation for new OGRLayer::GetExtent(), returning data MBR.
      84                 :  *
      85                 :  * Revision 1.7  2000/09/28 16:39:44  warmerda
      86                 :  * avoid warnings for unused, and unitialized variables
      87                 :  *
      88                 :  * Revision 1.6  2000/02/28 17:12:22  daniel
      89                 :  * Write support for joined tables and indexed fields
      90                 :  *
      91                 :  * Revision 1.5  2000/01/15 22:30:45  daniel
      92                 :  * Switch to MIT/X-Consortium OpenSource license
      93                 :  *
      94                 :  * Revision 1.4  1999/12/19 17:40:16  daniel
      95                 :  * Init + delete m_poRelation properly
      96                 :  *
      97                 :  * Revision 1.3  1999/12/14 05:53:00  daniel
      98                 :  * Fixed compile warnings
      99                 :  *
     100                 :  * Revision 1.2  1999/12/14 04:04:10  daniel
     101                 :  * Added bforceFlags to GetBounds() and GetFeatureCountByType()
     102                 :  *
     103                 :  * Revision 1.1  1999/12/14 02:10:32  daniel
     104                 :  * Initial revision
     105                 :  *
     106                 :  **********************************************************************/
     107                 : 
     108                 : #include "mitab.h"
     109                 : #include "mitab_utils.h"
     110                 : 
     111                 : #include <ctype.h>      /* isspace() */
     112                 : 
     113                 : /*=====================================================================
     114                 :  *                      class TABView
     115                 :  *====================================================================*/
     116                 : 
     117                 : 
     118                 : /**********************************************************************
     119                 :  *                   TABView::TABView()
     120                 :  *
     121                 :  * Constructor.
     122                 :  **********************************************************************/
     123               0 : TABView::TABView()
     124                 : {
     125               0 :     m_pszFname = NULL;
     126               0 :     m_eAccessMode = TABRead;
     127               0 :     m_papszTABFile = NULL;
     128               0 :     m_pszVersion = NULL;
     129                 : 
     130               0 :     m_numTABFiles = 0;
     131               0 :     m_papszTABFnames = NULL;
     132               0 :     m_papoTABFiles = NULL;
     133               0 :     m_nMainTableIndex = -1;
     134                 : 
     135               0 :     m_papszFieldNames = NULL;
     136               0 :     m_papszWhereClause = NULL;
     137                 : 
     138               0 :     m_poRelation = NULL;
     139               0 :     m_bRelFieldsCreated = FALSE;
     140               0 : }
     141                 : 
     142                 : /**********************************************************************
     143                 :  *                   TABView::~TABView()
     144                 :  *
     145                 :  * Destructor.
     146                 :  **********************************************************************/
     147               0 : TABView::~TABView()
     148                 : {
     149               0 :     Close();
     150               0 : }
     151                 : 
     152                 : 
     153               0 : int TABView::GetFeatureCount (int bForce)
     154                 : {
     155                 : 
     156               0 :     if (m_nMainTableIndex != -1)
     157               0 :         return m_papoTABFiles[m_nMainTableIndex]->GetFeatureCount( bForce );
     158                 : 
     159               0 :     return 0;
     160                 : }
     161                 : 
     162               0 : void TABView::ResetReading()
     163                 : {
     164               0 :     if (m_nMainTableIndex != -1)
     165               0 :         m_papoTABFiles[m_nMainTableIndex]->ResetReading();
     166               0 : }
     167                 : 
     168                 : 
     169                 : /**********************************************************************
     170                 :  *                   TABView::Open()
     171                 :  *
     172                 :  * Open a .TAB dataset and the associated files, and initialize the 
     173                 :  * structures to be ready to read features from it.
     174                 :  *
     175                 :  * This class is used to open .TAB files that define a view on
     176                 :  * two other .TAB files.  Regular .TAB datasets should be opened using
     177                 :  * the TABFile class instead.
     178                 :  *
     179                 :  * Set bTestOpenNoError=TRUE to silently return -1 with no error message
     180                 :  * if the file cannot be opened.  This is intended to be used in the
     181                 :  * context of a TestOpen() function.  The default value is FALSE which
     182                 :  * means that an error is reported if the file cannot be opened.
     183                 :  *
     184                 :  * Returns 0 on success, -1 on error.
     185                 :  **********************************************************************/
     186               0 : int TABView::Open(const char *pszFname, const char *pszAccess,
     187                 :                   GBool bTestOpenNoError /*= FALSE*/ )
     188                 : {
     189               0 :     char nStatus = 0;
     190                 :    
     191               0 :     if (m_numTABFiles > 0)
     192                 :     {
     193                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     194               0 :                  "Open() failed: object already contains an open file");
     195               0 :         return -1;
     196                 :     }
     197                 : 
     198                 :     /*-----------------------------------------------------------------
     199                 :      * Validate access mode and call the right open method
     200                 :      *----------------------------------------------------------------*/
     201               0 :     if (EQUALN(pszAccess, "r", 1))
     202                 :     {
     203               0 :         m_eAccessMode = TABRead;
     204               0 :         nStatus = (char)OpenForRead(pszFname, bTestOpenNoError);
     205                 :     }
     206               0 :     else if (EQUALN(pszAccess, "w", 1))
     207                 :     {
     208               0 :         m_eAccessMode = TABWrite;
     209               0 :         nStatus = (char)OpenForWrite(pszFname);
     210                 :     }
     211                 :     else
     212                 :     {
     213                 :         CPLError(CE_Failure, CPLE_NotSupported,
     214               0 :                  "Open() failed: access mode \"%s\" not supported", pszAccess);
     215               0 :         return -1;
     216                 :     }
     217                 : 
     218               0 :     return nStatus;
     219                 : }
     220                 : 
     221                 : 
     222                 : /**********************************************************************
     223                 :  *                   TABView::OpenForRead()
     224                 :  *
     225                 :  * Open for reading
     226                 :  *
     227                 :  * Returns 0 on success, -1 on error.
     228                 :  **********************************************************************/
     229               0 : int TABView::OpenForRead(const char *pszFname, 
     230                 :                          GBool bTestOpenNoError /*= FALSE*/ )
     231                 : {
     232               0 :     char *pszPath = NULL;
     233               0 :     int nFnameLen = 0;
     234                 :    
     235               0 :     m_eAccessMode = TABRead;
     236                 : 
     237                 :     /*-----------------------------------------------------------------
     238                 :      * Read main .TAB (text) file
     239                 :      *----------------------------------------------------------------*/
     240               0 :     m_pszFname = CPLStrdup(pszFname);
     241                 : 
     242                 : #ifndef _WIN32
     243                 :     /*-----------------------------------------------------------------
     244                 :      * On Unix, make sure extension uses the right cases
     245                 :      * We do it even for write access because if a file with the same
     246                 :      * extension already exists we want to overwrite it.
     247                 :      *----------------------------------------------------------------*/
     248               0 :     TABAdjustFilenameExtension(m_pszFname);
     249                 : #endif
     250                 : 
     251                 :     /*-----------------------------------------------------------------
     252                 :      * Open .TAB file... since it's a small text file, we will just load
     253                 :      * it as a stringlist in memory.
     254                 :      *----------------------------------------------------------------*/
     255               0 :     m_papszTABFile = TAB_CSLLoad(m_pszFname);
     256               0 :     if (m_papszTABFile == NULL)
     257                 :     {
     258               0 :         if (!bTestOpenNoError)
     259                 :         {
     260                 :             CPLError(CE_Failure, CPLE_FileIO,
     261               0 :                      "Failed opening %s.", m_pszFname);
     262                 :         }
     263                 :         
     264               0 :         CPLFree(m_pszFname);
     265               0 :         return -1;
     266                 :     }
     267                 : 
     268                 :     /*-------------------------------------------------------------
     269                 :      * Look for a line with the "create view" keyword.
     270                 :      * If there is no "create view", then we may have a valid .TAB file,
     271                 :      * but we do not support it in this class.
     272                 :      *------------------------------------------------------------*/
     273               0 :     GBool bCreateViewFound = FALSE;
     274               0 :     for (int i=0; 
     275               0 :          !bCreateViewFound && m_papszTABFile && m_papszTABFile[i];
     276                 :          i++)
     277                 :     {
     278               0 :         const char *pszStr = m_papszTABFile[i];
     279               0 :         while(*pszStr != '\0' && isspace((unsigned char)*pszStr))
     280               0 :             pszStr++;
     281               0 :         if (EQUALN(pszStr, "create view", 11))
     282               0 :             bCreateViewFound = TRUE;
     283                 :     }
     284                 : 
     285               0 :     if ( !bCreateViewFound )
     286                 :     {
     287               0 :         if (!bTestOpenNoError)
     288                 :             CPLError(CE_Failure, CPLE_NotSupported,
     289                 :                      "%s contains no table view definition.  "
     290                 :                      "This type of .TAB file cannot be read by this library.",
     291               0 :                      m_pszFname);
     292                 :         else
     293               0 :             CPLErrorReset();
     294                 : 
     295               0 :         CPLFree(m_pszFname);
     296                 : 
     297               0 :         return -1;
     298                 :     }
     299                 : 
     300                 :     /*-----------------------------------------------------------------
     301                 :      * OK, this appears to be a valid TAB view dataset...
     302                 :      * Extract the path component from the main .TAB filename
     303                 :      * to build the filename of the sub-tables
     304                 :      *----------------------------------------------------------------*/
     305               0 :     pszPath = CPLStrdup(m_pszFname);
     306               0 :     nFnameLen = strlen(pszPath);
     307               0 :     for( ; nFnameLen > 0; nFnameLen--)
     308                 :     {
     309               0 :         if (pszPath[nFnameLen-1] == '/' || 
     310               0 :             pszPath[nFnameLen-1] == '\\' )
     311                 :         {
     312               0 :             break;
     313                 :         }
     314               0 :         pszPath[nFnameLen-1] = '\0';
     315                 :     }
     316                 : 
     317                 :     /*-----------------------------------------------------------------
     318                 :      * Extract the useful info from the TAB header
     319                 :      *----------------------------------------------------------------*/
     320               0 :     if (ParseTABFile(pszPath, bTestOpenNoError) != 0)
     321                 :     {
     322                 :         // Failed parsing... an error has already been produced if necessary
     323               0 :         CPLFree(pszPath);
     324               0 :         Close();
     325               0 :         return -1;
     326                 :     }
     327               0 :     CPLFree(pszPath);
     328               0 :     pszPath = NULL;
     329                 : 
     330                 :     /*-----------------------------------------------------------------
     331                 :      * __TODO__ For now, we support only 2 files linked through a single
     332                 :      *          field... so we'll do some validation first to make sure
     333                 :      *          that what we found in the header respects these limitations.
     334                 :      *----------------------------------------------------------------*/
     335               0 :     if (m_numTABFiles != 2)
     336                 :     {
     337               0 :         if (!bTestOpenNoError)
     338                 :             CPLError(CE_Failure, CPLE_NotSupported,
     339                 :                      "Open Failed: Dataset %s defines a view on %d tables. "
     340                 :                      "This is not currently supported.",
     341               0 :                      m_pszFname, m_numTABFiles);
     342               0 :         Close();
     343               0 :         return -1;
     344                 :     }
     345                 : 
     346                 :     /*-----------------------------------------------------------------
     347                 :      * Open all the tab files listed in the view
     348                 :      *----------------------------------------------------------------*/
     349               0 :     m_papoTABFiles = (TABFile**)CPLCalloc(m_numTABFiles, sizeof(TABFile*));
     350                 : 
     351               0 :     for (int iFile=0; iFile < m_numTABFiles; iFile++)
     352                 :     {
     353                 : #ifndef _WIN32
     354               0 :         TABAdjustFilenameExtension(m_papszTABFnames[iFile]);
     355                 : #endif
     356                 :         
     357               0 :         m_papoTABFiles[iFile] = new TABFile;
     358                 :    
     359               0 :         if ( m_papoTABFiles[iFile]->Open(m_papszTABFnames[iFile],
     360               0 :                                          "rb", bTestOpenNoError) != 0)
     361                 :         {
     362                 :             // Open Failed... an error has already been reported, just return.
     363               0 :             if (bTestOpenNoError)
     364               0 :                 CPLErrorReset();
     365               0 :             Close();
     366               0 :             return -1;
     367                 :         }
     368                 :     }
     369                 : 
     370                 :     /*-----------------------------------------------------------------
     371                 :      * Create TABRelation... this will build FeatureDefn, etc.
     372                 :      * __TODO__ For now this assumes only 2 tables in the view...
     373                 :      *----------------------------------------------------------------*/
     374               0 :     m_poRelation = new TABRelation;
     375                 :     
     376               0 :     CPLAssert(m_nMainTableIndex == 0);
     377               0 :     CPLAssert(CSLCount(m_papszWhereClause) == 5);
     378               0 :     char *pszTableName = TABGetBasename(m_pszFname);
     379               0 :     if ( m_poRelation->Init(pszTableName,
     380                 :                             m_papoTABFiles[0], m_papoTABFiles[1],
     381               0 :                             m_papszWhereClause[4], m_papszWhereClause[2],
     382                 :                             m_papszFieldNames)  != 0 )
     383                 :     {
     384                 :         // An error should already have been reported
     385               0 :         CPLFree(pszTableName);
     386               0 :         Close();
     387               0 :         return -1;
     388                 :     }
     389               0 :     CPLFree(pszTableName);
     390                 : 
     391               0 :     return 0;
     392                 : }
     393                 : 
     394                 : 
     395                 : /**********************************************************************
     396                 :  *                   TABView::OpenForWrite()
     397                 :  *
     398                 :  * Create a new TABView dataset
     399                 :  *
     400                 :  * Returns 0 on success, -1 on error.
     401                 :  **********************************************************************/
     402               0 : int TABView::OpenForWrite(const char *pszFname)
     403                 : {
     404               0 :     int nFnameLen = 0;
     405                 :    
     406               0 :     m_eAccessMode = TABWrite;
     407                 : 
     408                 :     /*-----------------------------------------------------------------
     409                 :      * Read main .TAB (text) file
     410                 :      *----------------------------------------------------------------*/
     411               0 :     m_pszFname = CPLStrdup(pszFname);
     412                 : 
     413                 : #ifndef _WIN32
     414                 :     /*-----------------------------------------------------------------
     415                 :      * On Unix, make sure extension uses the right cases
     416                 :      * We do it even for write access because if a file with the same
     417                 :      * extension already exists we want to overwrite it.
     418                 :      *----------------------------------------------------------------*/
     419               0 :     TABAdjustFilenameExtension(m_pszFname);
     420                 : #endif
     421                 : 
     422                 :     /*-----------------------------------------------------------------
     423                 :      * Extract the path component from the main .TAB filename
     424                 :      *----------------------------------------------------------------*/
     425               0 :     char *pszPath = CPLStrdup(m_pszFname);
     426               0 :     nFnameLen = strlen(pszPath);
     427               0 :     for( ; nFnameLen > 0; nFnameLen--)
     428                 :     {
     429               0 :         if (pszPath[nFnameLen-1] == '/' || 
     430               0 :             pszPath[nFnameLen-1] == '\\' )
     431                 :         {
     432               0 :             break;
     433                 :         }
     434               0 :         pszPath[nFnameLen-1] = '\0';
     435                 :     }
     436                 : 
     437               0 :     char *pszBasename = TABGetBasename(m_pszFname);
     438                 : 
     439                 :     /*-----------------------------------------------------------------
     440                 :      * Create the 2 TAB files for the view.
     441                 :      *
     442                 :      * __TODO__ For now, we support only 2 files linked through a single
     443                 :      *          field... not sure if anything else than that can be useful
     444                 :      *          anyways.
     445                 :      *----------------------------------------------------------------*/
     446               0 :     m_numTABFiles = 2;
     447               0 :     m_papszTABFnames = NULL;
     448               0 :     m_nMainTableIndex = 0;
     449               0 :     m_bRelFieldsCreated = FALSE;
     450                 : 
     451               0 :     m_papoTABFiles = (TABFile**)CPLCalloc(m_numTABFiles, sizeof(TABFile*));
     452                 : 
     453               0 :     for (int iFile=0; iFile < m_numTABFiles; iFile++)
     454                 :     {
     455                 :         m_papszTABFnames = CSLAppendPrintf(m_papszTABFnames, "%s%s%d.tab", 
     456               0 :                                                pszPath, pszBasename, iFile+1);
     457                 : #ifndef _WIN32
     458               0 :         TABAdjustFilenameExtension(m_papszTABFnames[iFile]);
     459                 : #endif
     460                 :         
     461               0 :         m_papoTABFiles[iFile] = new TABFile;
     462                 :    
     463               0 :         if ( m_papoTABFiles[iFile]->Open(m_papszTABFnames[iFile], "wb") != 0)
     464                 :         {
     465                 :             // Open Failed... an error has already been reported, just return.
     466               0 :             CPLFree(pszPath);
     467               0 :             CPLFree(pszBasename);
     468               0 :             Close();
     469               0 :             return -1;
     470                 :         }
     471                 :     }
     472                 : 
     473                 :     /*-----------------------------------------------------------------
     474                 :      * Create TABRelation... 
     475                 :      *----------------------------------------------------------------*/
     476               0 :     m_poRelation = new TABRelation;
     477                 :     
     478               0 :     if ( m_poRelation->Init(pszBasename,
     479                 :                             m_papoTABFiles[0], m_papoTABFiles[1],
     480                 :                             NULL, NULL, NULL)  != 0 )
     481                 :     {
     482                 :         // An error should already have been reported
     483               0 :         CPLFree(pszPath);
     484               0 :         CPLFree(pszBasename);
     485               0 :         Close();
     486               0 :         return -1;
     487                 :     }
     488                 : 
     489               0 :     CPLFree(pszPath);
     490               0 :     CPLFree(pszBasename);
     491                 : 
     492               0 :     return 0;
     493                 : }
     494                 : 
     495                 : 
     496                 : 
     497                 : /**********************************************************************
     498                 :  *                   TABView::ParseTABFile()
     499                 :  *
     500                 :  * Scan the lines of the TAB file, and store any useful information into
     501                 :  * class members.  The main piece of information being the sub-table
     502                 :  * names, and the list of fields to include in the view that we will
     503                 :  * use to build the OGRFeatureDefn for this file.
     504                 :  *
     505                 :  * It is assumed that the TAB header file is already loaded in m_papszTABFile
     506                 :  *
     507                 :  * This private method should be used only during the Open() call.
     508                 :  *
     509                 :  * Returns 0 on success, -1 on error.
     510                 :  **********************************************************************/
     511               0 : int TABView::ParseTABFile(const char *pszDatasetPath, 
     512                 :                           GBool bTestOpenNoError /*=FALSE*/)
     513                 : {
     514                 :     int         iLine, numLines;
     515               0 :     char        **papszTok=NULL;
     516               0 :     GBool       bInsideTableDef = FALSE;
     517                 : 
     518               0 :     if (m_eAccessMode != TABRead)
     519                 :     {
     520                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     521               0 :                  "ParseTABFile() can be used only with Read access.");
     522               0 :         return -1;
     523                 :     }
     524                 : 
     525               0 :     numLines = CSLCount(m_papszTABFile);
     526                 : 
     527               0 :     for(iLine=0; iLine<numLines; iLine++)
     528                 :     {
     529                 :         /*-------------------------------------------------------------
     530                 :          * Tokenize the next .TAB line, and check first keyword
     531                 :          *------------------------------------------------------------*/
     532               0 :         CSLDestroy(papszTok);
     533               0 :         papszTok = CSLTokenizeStringComplex(m_papszTABFile[iLine], " \t(),;",
     534               0 :                                             TRUE, FALSE);
     535               0 :         if (CSLCount(papszTok) < 2)
     536               0 :             continue;   // All interesting lines have at least 2 tokens
     537                 : 
     538               0 :         if (EQUAL(papszTok[0], "!version"))
     539                 :         {
     540               0 :             m_pszVersion = CPLStrdup(papszTok[1]);
     541                 :         }
     542               0 :         else if (EQUAL(papszTok[0], "!charset"))
     543                 :         {
     544               0 :             CPLFree(m_pszCharset);
     545               0 :             m_pszCharset = CPLStrdup(papszTok[1]);
     546                 :         }
     547               0 :         else if (EQUAL(papszTok[0], "open") &&
     548               0 :                  EQUAL(papszTok[1], "table") &&
     549                 :                  CSLCount(papszTok) >= 3)
     550                 :         {
     551                 :             // Source table name may be either "filename" or "filename.tab"
     552               0 :             int nLen = strlen(papszTok[2]);
     553               0 :             if (nLen > 4 && EQUAL(papszTok[2]+nLen-4, ".tab"))
     554               0 :                 papszTok[2][nLen-4] = '\0';
     555                 : 
     556                 :             m_papszTABFnames = CSLAppendPrintf(m_papszTABFnames, 
     557                 :                                                "%s%s.tab", 
     558               0 :                                                pszDatasetPath, papszTok[2]);
     559                 :         }
     560               0 :         else if (EQUAL(papszTok[0], "create") &&
     561               0 :                  EQUAL(papszTok[1], "view") )
     562                 :         {
     563               0 :             bInsideTableDef = TRUE;
     564                 :         }
     565               0 :         else if (bInsideTableDef &&
     566               0 :                  (EQUAL(papszTok[0],"Select")))
     567                 :         {
     568                 :             /*---------------------------------------------------------
     569                 :              * We found the list of table fields (comma-delimited list)
     570                 :              *--------------------------------------------------------*/
     571                 :             int iTok;
     572               0 :             for(iTok=1; papszTok[iTok] != NULL; iTok++)
     573                 :                 m_papszFieldNames = CSLAddString(m_papszFieldNames, 
     574               0 :                                                  papszTok[iTok]);
     575                 : 
     576                 :         }
     577               0 :         else if (bInsideTableDef &&
     578               0 :                  (EQUAL(papszTok[0],"where")))
     579                 :         {
     580                 :             /*---------------------------------------------------------
     581                 :              * We found the where clause that relates the 2 tables
     582                 :              * Something in the form:
     583                 :              *   where table1.field1=table2.field2
     584                 :              * The tokenized array will contain:
     585                 :              *  {"where", "table1", "field1", "table2", "field2"}
     586                 :              *--------------------------------------------------------*/
     587               0 :             m_papszWhereClause =CSLTokenizeStringComplex(m_papszTABFile[iLine],
     588                 :                                                          " \t(),;=.",
     589               0 :                                                          TRUE, FALSE);
     590                 : 
     591                 :             /*---------------------------------------------------------
     592                 :              * For now we are very limiting on the format of the WHERE
     593                 :              * clause... we will be more permitting as we learn more about
     594                 :              * what it can contain... (I don't want to implement a full SQL 
     595                 :              * parser here!!!).  If you encountered this error,
     596                 :              * (and are reading this!) please report the test dataset 
     597                 :              * that produced the error and I'll see if we can support it.
     598                 :              *--------------------------------------------------------*/
     599               0 :             if (CSLCount( m_papszWhereClause ) != 5)
     600                 :             {
     601               0 :                 if (!bTestOpenNoError)
     602                 :                     CPLError(CE_Failure, CPLE_NotSupported,
     603                 :                      "WHERE clause in %s is not in a supported format: \"%s\"",
     604               0 :                              m_pszFname, m_papszTABFile[iLine]);
     605               0 :                 return -1;
     606                 :             }
     607                 :         }
     608                 :         else
     609                 :         {
     610                 :             // Simply Ignore unrecognized lines
     611                 :         }
     612                 :     }
     613                 : 
     614               0 :     CSLDestroy(papszTok);
     615                 : 
     616                 :     /*-----------------------------------------------------------------
     617                 :      * The main table is the one from which we read the geometries, etc...
     618                 :      * For now we assume it is always the first one in the list
     619                 :      *----------------------------------------------------------------*/
     620               0 :     m_nMainTableIndex = 0;
     621                 : 
     622                 :     /*-----------------------------------------------------------------
     623                 :      * Make sure all required class members are set
     624                 :      *----------------------------------------------------------------*/
     625               0 :     m_numTABFiles = CSLCount(m_papszTABFnames);
     626                 : 
     627               0 :     if (m_pszCharset == NULL)
     628               0 :         m_pszCharset = CPLStrdup("Neutral");
     629               0 :     if (m_pszVersion == NULL)
     630               0 :         m_pszVersion = CPLStrdup("100");
     631                 : 
     632               0 :     if (CSLCount(m_papszFieldNames) == 0 )
     633                 :     {
     634               0 :         if (!bTestOpenNoError)
     635                 :             CPLError(CE_Failure, CPLE_NotSupported,
     636                 :                      "%s: header contains no table field definition.  "
     637                 :                      "This type of .TAB file cannot be read by this library.",
     638               0 :                      m_pszFname);
     639               0 :         return -1;
     640                 :     }
     641                 : 
     642               0 :     if (CSLCount(m_papszWhereClause) == 0 )
     643                 :     {
     644               0 :         if (!bTestOpenNoError)
     645                 :             CPLError(CE_Failure, CPLE_NotSupported,
     646                 :                      "%s: WHERE clause not found or missing in header.  "
     647                 :                      "This type of .TAB file cannot be read by this library.",
     648               0 :                      m_pszFname);
     649               0 :         return -1;
     650                 :     }
     651               0 :     return 0;
     652                 : }
     653                 : 
     654                 : 
     655                 : /**********************************************************************
     656                 :  *                   TABView::WriteTABFile()
     657                 :  *
     658                 :  * Generate the TAB header file.  This is usually done during the 
     659                 :  * Close() call.
     660                 :  *
     661                 :  * Returns 0 on success, -1 on error.
     662                 :  **********************************************************************/
     663               0 : int TABView::WriteTABFile()
     664                 : {
     665                 :     FILE *fp;
     666                 : 
     667               0 :     CPLAssert(m_eAccessMode == TABWrite);
     668               0 :     CPLAssert(m_numTABFiles == 2);
     669               0 :     CPLAssert(GetLayerDefn());
     670                 : 
     671               0 :     char *pszTable  = TABGetBasename(m_pszFname);
     672               0 :     char *pszTable1 = TABGetBasename(m_papszTABFnames[0]);
     673               0 :     char *pszTable2 = TABGetBasename(m_papszTABFnames[1]);
     674                 : 
     675               0 :     if ( (fp = VSIFOpen(m_pszFname, "wt")) != NULL)
     676                 :     {
     677                 :         // Version is always 100, no matter what the sub-table's version is
     678               0 :         fprintf(fp, "!Table\n");
     679               0 :         fprintf(fp, "!Version 100\n");
     680                 : 
     681               0 :         fprintf(fp, "Open Table \"%s\" Hide\n", pszTable1);
     682               0 :         fprintf(fp, "Open Table \"%s\" Hide\n", pszTable2);
     683               0 :         fprintf(fp, "\n");
     684               0 :         fprintf(fp, "Create View %s As\n", pszTable);
     685               0 :         fprintf(fp, "Select ");
     686                 : 
     687               0 :         OGRFeatureDefn *poDefn = GetLayerDefn();
     688               0 :         for(int iField=0; iField<poDefn->GetFieldCount(); iField++)
     689                 :         {
     690               0 :             OGRFieldDefn *poFieldDefn = poDefn->GetFieldDefn(iField);
     691               0 :             if (iField == 0)
     692               0 :                 fprintf(fp, "%s", poFieldDefn->GetNameRef());
     693                 :             else
     694               0 :                 fprintf(fp, ",%s", poFieldDefn->GetNameRef());
     695                 :         }
     696               0 :         fprintf(fp, "\n");
     697                 : 
     698               0 :         fprintf(fp, "From %s, %s\n", pszTable2, pszTable1);
     699                 :         fprintf(fp, "Where %s.%s=%s.%s\n", pszTable2, 
     700                 :                                            m_poRelation->GetRelFieldName(),
     701                 :                                            pszTable1, 
     702               0 :                                            m_poRelation->GetMainFieldName());
     703                 : 
     704                 : 
     705               0 :         VSIFClose(fp);
     706                 :     }
     707                 :     else
     708                 :     {
     709               0 :         CPLFree(pszTable);
     710               0 :         CPLFree(pszTable1);
     711               0 :         CPLFree(pszTable2);
     712                 :         
     713                 :         CPLError(CE_Failure, CPLE_FileIO,
     714               0 :                  "Failed to create file `%s'", m_pszFname);
     715               0 :         return -1;
     716                 :     }
     717                 : 
     718               0 :     CPLFree(pszTable);
     719               0 :     CPLFree(pszTable1);
     720               0 :     CPLFree(pszTable2);
     721                 : 
     722               0 :     return 0;
     723                 : }
     724                 : 
     725                 : 
     726                 : /**********************************************************************
     727                 :  *                   TABView::Close()
     728                 :  *
     729                 :  * Close current file, and release all memory used.
     730                 :  *
     731                 :  * Returns 0 on success, -1 on error.
     732                 :  **********************************************************************/
     733               0 : int TABView::Close()
     734                 : {
     735                 :     // In write access, the main .TAB file has not been written yet.
     736               0 :     if (m_eAccessMode == TABWrite && m_poRelation)
     737               0 :         WriteTABFile();
     738                 : 
     739               0 :     for(int i=0; m_papoTABFiles && i<m_numTABFiles; i++)
     740                 :     {
     741               0 :         if (m_papoTABFiles[i])
     742               0 :             delete m_papoTABFiles[i];  // Automatically closes.
     743                 :     }
     744               0 :     CPLFree(m_papoTABFiles);
     745               0 :     m_papoTABFiles = NULL;
     746               0 :     m_numTABFiles = 0;
     747                 : 
     748                 :     
     749                 :     /*-----------------------------------------------------------------
     750                 :      * __TODO__ OK, MapInfo does not like to see a .map and .id file
     751                 :      * attached to the second table, even if they're empty.
     752                 :      * We'll use a little hack to delete them now, but eventually we
     753                 :      * should avoid creating them at all.
     754                 :      *----------------------------------------------------------------*/
     755               0 :     if (m_eAccessMode == TABWrite && m_pszFname)
     756                 :     {
     757               0 :         m_pszFname[strlen(m_pszFname)-4] = '\0';
     758               0 :         char *pszFile = CPLStrdup(CPLSPrintf("%s2.map", m_pszFname));
     759               0 :         TABAdjustFilenameExtension(pszFile);
     760               0 :         VSIUnlink(pszFile);
     761                 : 
     762               0 :         sprintf(pszFile, "%s2.id", m_pszFname);
     763               0 :         TABAdjustFilenameExtension(pszFile);
     764               0 :         VSIUnlink(pszFile);
     765                 : 
     766               0 :         CPLFree(pszFile);
     767                 :     }
     768                 :     // End of hack!
     769                 : 
     770                 : 
     771               0 :     CPLFree(m_pszFname);
     772               0 :     m_pszFname = NULL;
     773                 : 
     774               0 :     CSLDestroy(m_papszTABFile);
     775               0 :     m_papszTABFile = NULL;
     776                 : 
     777               0 :     CPLFree(m_pszVersion);
     778               0 :     m_pszVersion = NULL;
     779               0 :     CPLFree(m_pszCharset);
     780               0 :     m_pszCharset = NULL;
     781                 : 
     782               0 :     CSLDestroy(m_papszTABFnames);
     783               0 :     m_papszTABFnames = NULL;
     784                 : 
     785               0 :     CSLDestroy(m_papszFieldNames);
     786               0 :     m_papszFieldNames = NULL;
     787               0 :     CSLDestroy(m_papszWhereClause);
     788               0 :     m_papszWhereClause = NULL;
     789                 : 
     790               0 :     m_nMainTableIndex = -1;
     791                 : 
     792               0 :     if (m_poRelation)
     793               0 :         delete m_poRelation;
     794               0 :     m_poRelation = NULL;
     795                 : 
     796               0 :     m_bRelFieldsCreated = FALSE;
     797                 : 
     798               0 :     return 0;
     799                 : }
     800                 : 
     801                 : /**********************************************************************
     802                 :  *                   TABView::SetQuickSpatialIndexMode()
     803                 :  *
     804                 :  * Select "quick spatial index mode". 
     805                 :  *
     806                 :  * The default behavior of MITAB is to generate an optimized spatial index,
     807                 :  * but this results in slower write speed. 
     808                 :  *
     809                 :  * Applications that want faster write speed and do not care
     810                 :  * about the performance of spatial queries on the resulting file can
     811                 :  * use SetQuickSpatialIndexMode() to require the creation of a non-optimal
     812                 :  * spatial index (actually emulating the type of spatial index produced
     813                 :  * by MITAB before version 1.6.0). In this mode writing files can be 
     814                 :  * about 5 times faster, but spatial queries can be up to 30 times slower.
     815                 :  *
     816                 :  * Returns 0 on success, -1 on error.
     817                 :  **********************************************************************/
     818               0 : int TABView::SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode/*=TRUE*/)
     819                 : {
     820               0 :     if (m_eAccessMode != TABWrite || m_numTABFiles == 0)
     821                 :     {
     822                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     823               0 :                  "SetQuickSpatialIndexMode() failed: file not opened for write access.");
     824               0 :         return -1;
     825                 :     }
     826                 : 
     827               0 :     for (int iFile=0; iFile < m_numTABFiles; iFile++)
     828                 :     {
     829               0 :         if ( m_papoTABFiles[iFile]->SetQuickSpatialIndexMode(bQuickSpatialIndexMode) != 0)
     830                 :         {
     831                 :             // An error has already been reported, just return.
     832               0 :             return -1;
     833                 :         }
     834                 :     }
     835                 : 
     836               0 :     return 0;
     837                 : }
     838                 : 
     839                 : 
     840                 : /**********************************************************************
     841                 :  *                   TABView::GetNextFeatureId()
     842                 :  *
     843                 :  * Returns feature id that follows nPrevId, or -1 if it is the
     844                 :  * last feature id.  Pass nPrevId=-1 to fetch the first valid feature id.
     845                 :  **********************************************************************/
     846               0 : int TABView::GetNextFeatureId(int nPrevId)
     847                 : {
     848               0 :     if (m_nMainTableIndex != -1)
     849               0 :         return m_papoTABFiles[m_nMainTableIndex]->GetNextFeatureId(nPrevId);
     850                 : 
     851               0 :     return -1;
     852                 : }
     853                 : 
     854                 : /**********************************************************************
     855                 :  *                   TABView::GetFeatureRef()
     856                 :  *
     857                 :  * Fill and return a TABFeature object for the specified feature id.
     858                 :  *
     859                 :  * The retruned pointer is a reference to an object owned and maintained
     860                 :  * by this TABView object.  It should not be altered or freed by the 
     861                 :  * caller and its contents is guaranteed to be valid only until the next
     862                 :  * call to GetFeatureRef() or Close().
     863                 :  *
     864                 :  * Returns NULL if the specified feature id does not exist of if an
     865                 :  * error happened.  In any case, CPLError() will have been called to
     866                 :  * report the reason of the failure.
     867                 :  **********************************************************************/
     868               0 : TABFeature *TABView::GetFeatureRef(int nFeatureId)
     869                 : {
     870                 :     
     871                 :     /*-----------------------------------------------------------------
     872                 :      * Make sure file is opened 
     873                 :      *----------------------------------------------------------------*/
     874               0 :     if (m_poRelation == NULL)
     875                 :     {
     876                 :         CPLError(CE_Failure, CPLE_IllegalArg,
     877               0 :                  "GetFeatureRef() failed: file is not opened!");
     878               0 :         return NULL;
     879                 :     }
     880                 : 
     881               0 :     if(m_poCurFeature)
     882                 :     {
     883               0 :         delete m_poCurFeature;
     884               0 :         m_poCurFeature = NULL;
     885                 :     }
     886                 : 
     887               0 :     m_poCurFeature = m_poRelation->GetFeature(nFeatureId);
     888               0 :     m_nCurFeatureId = nFeatureId;
     889               0 :     m_poCurFeature->SetFID(m_nCurFeatureId);
     890               0 :     return m_poCurFeature;
     891                 : }
     892                 : 
     893                 : 
     894                 : /**********************************************************************
     895                 :  *                   TABView::CreateFeature()
     896                 :  *
     897                 :  * Write a new feature to this dataset. The passed in feature is updated 
     898                 :  * with the new feature id.
     899                 :  *
     900                 :  * Returns OGRERR_NONE on success, or an appropriate OGRERR_ code if an
     901                 :  * error happened in which case, CPLError() will have been called to
     902                 :  * report the reason of the failure.
     903                 :  **********************************************************************/
     904               0 : OGRErr TABView::CreateFeature(TABFeature *poFeature)
     905                 : {
     906               0 :     if (m_eAccessMode != TABWrite)
     907                 :     {
     908                 :         CPLError(CE_Failure, CPLE_NotSupported,
     909               0 :                  "CreateFeature() can be used only with Write access.");
     910               0 :         return OGRERR_UNSUPPORTED_OPERATION;
     911                 :     }
     912                 : 
     913               0 :     if (m_poRelation == NULL)
     914                 :     {
     915                 :         CPLError(CE_Failure, CPLE_IllegalArg,
     916               0 :                  "CreateFeature() failed: file is not opened!");
     917               0 :         return OGRERR_FAILURE;
     918                 :     }
     919                 : 
     920                 :     /*-----------------------------------------------------------------
     921                 :      * If we're about to write the first feature, then we must finish
     922                 :      * the initialization of the view first by creating the MI_refnum fields
     923                 :      *----------------------------------------------------------------*/
     924               0 :     if (!m_bRelFieldsCreated)
     925                 :     {
     926               0 :         if (m_poRelation->CreateRelFields() != 0)
     927               0 :             return OGRERR_FAILURE;
     928               0 :         m_bRelFieldsCreated = TRUE;
     929                 :     }
     930                 : 
     931               0 :     int nFeatureId = m_poRelation->WriteFeature(poFeature);
     932               0 :     if (nFeatureId < 0)
     933               0 :         return OGRERR_FAILURE;
     934                 : 
     935               0 :     poFeature->SetFID(nFeatureId);
     936                 : 
     937               0 :     return OGRERR_NONE;
     938                 : }
     939                 : 
     940                 : 
     941                 : 
     942                 : /**********************************************************************
     943                 :  *                   TABView::GetLayerDefn()
     944                 :  *
     945                 :  * Returns a reference to the OGRFeatureDefn that will be used to create
     946                 :  * features in this dataset.
     947                 :  *
     948                 :  * Returns a reference to an object that is maintained by this TABView
     949                 :  * object (and thus should not be modified or freed by the caller) or
     950                 :  * NULL if the OGRFeatureDefn has not been initialized yet (i.e. no file
     951                 :  * opened yet)
     952                 :  **********************************************************************/
     953               0 : OGRFeatureDefn *TABView::GetLayerDefn()
     954                 : {
     955               0 :     if (m_poRelation)
     956               0 :         return m_poRelation->GetFeatureDefn();
     957                 : 
     958               0 :     return NULL;
     959                 : }
     960                 : 
     961                 : /**********************************************************************
     962                 :  *                   TABView::SetFeatureDefn()
     963                 :  *
     964                 :  * Set the FeatureDefn for this dataset.
     965                 :  *
     966                 :  * For now, fields passed through SetFeatureDefn will not be mapped
     967                 :  * properly, so this function can be used only with an empty feature defn.
     968                 :  **********************************************************************/
     969               0 : int TABView::SetFeatureDefn(OGRFeatureDefn *poFeatureDefn,
     970                 :                          TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */)
     971                 : {
     972               0 :     if (m_poRelation)
     973               0 :         return m_poRelation->SetFeatureDefn(poFeatureDefn);
     974                 : 
     975               0 :     return -1;
     976                 : }
     977                 : 
     978                 : 
     979                 : /**********************************************************************
     980                 :  *                   TABView::GetNativeFieldType()
     981                 :  *
     982                 :  * Returns the native MapInfo field type for the specified field.
     983                 :  *
     984                 :  * Returns TABFUnknown if file is not opened, or if specified field index is
     985                 :  * invalid.
     986                 :  *
     987                 :  * Note that field ids are positive and start at 0.
     988                 :  **********************************************************************/
     989               0 : TABFieldType TABView::GetNativeFieldType(int nFieldId)
     990                 : {
     991               0 :     if (m_poRelation)
     992               0 :         return m_poRelation->GetNativeFieldType(nFieldId);
     993                 : 
     994               0 :     return TABFUnknown;
     995                 : }
     996                 : 
     997                 : 
     998                 : /**********************************************************************
     999                 :  *                   TABView::AddFieldNative()
    1000                 :  *
    1001                 :  * Create a new field using a native mapinfo data type... this is an 
    1002                 :  * alternative to defining fields through the OGR interface.
    1003                 :  * This function should be called after creating a new dataset, but before 
    1004                 :  * writing the first feature.
    1005                 :  *
    1006                 :  * This function will build/update the OGRFeatureDefn that will have to be
    1007                 :  * used when writing features to this dataset.
    1008                 :  *
    1009                 :  * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn().
    1010                 :  *
    1011                 :  * Returns 0 on success, -1 on error.
    1012                 :  **********************************************************************/
    1013               0 : int TABView::AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
    1014                 :                             int nWidth /*=0*/, int nPrecision /*=0*/,
    1015                 :                             GBool bIndexed /*=FALSE*/, GBool bUnique/*=FALSE*/, int bApproxOK)
    1016                 : {
    1017               0 :     if (m_poRelation)
    1018                 :         return m_poRelation->AddFieldNative(pszName, eMapInfoType,
    1019                 :                                             nWidth, nPrecision,
    1020               0 :                                             bIndexed, bUnique, bApproxOK);
    1021                 : 
    1022               0 :     return -1;
    1023                 : }
    1024                 : 
    1025                 : /**********************************************************************
    1026                 :  *                   TABView::SetFieldIndexed()
    1027                 :  *
    1028                 :  * Request that a field be indexed.  This will create the .IND file if
    1029                 :  * necessary, etc.
    1030                 :  *
    1031                 :  * Note that field ids are positive and start at 0.
    1032                 :  *
    1033                 :  * Returns 0 on success, -1 on error.
    1034                 :  **********************************************************************/
    1035               0 : int TABView::SetFieldIndexed(int nFieldId)
    1036                 : {
    1037               0 :     if (m_poRelation)
    1038               0 :         return m_poRelation->SetFieldIndexed(nFieldId);
    1039                 : 
    1040               0 :     return -1;
    1041                 : }
    1042                 : 
    1043                 : /**********************************************************************
    1044                 :  *                   TABView::IsFieldIndexed()
    1045                 :  *
    1046                 :  * Returns TRUE if field is indexed, or FALSE otherwise.
    1047                 :  **********************************************************************/
    1048               0 : GBool TABView::IsFieldIndexed(int nFieldId)
    1049                 : {
    1050               0 :     if (m_poRelation)
    1051               0 :         return m_poRelation->IsFieldIndexed(nFieldId);
    1052                 : 
    1053               0 :     return FALSE;
    1054                 : }
    1055                 : 
    1056                 : /**********************************************************************
    1057                 :  *                   TABView::IsFieldUnique()
    1058                 :  *
    1059                 :  * Returns TRUE if field is in the Unique table, or FALSE otherwise.
    1060                 :  **********************************************************************/
    1061               0 : GBool TABView::IsFieldUnique(int nFieldId)
    1062                 : {
    1063               0 :     if (m_poRelation)
    1064               0 :         return m_poRelation->IsFieldUnique(nFieldId);
    1065                 : 
    1066               0 :     return FALSE;
    1067                 : }
    1068                 : 
    1069                 : 
    1070                 : /**********************************************************************
    1071                 :  *                   TABView::GetBounds()
    1072                 :  *
    1073                 :  * Fetch projection coordinates bounds of a dataset.
    1074                 :  *
    1075                 :  * The bForce flag has no effect on TAB files since the bounds are
    1076                 :  * always in the header.
    1077                 :  *
    1078                 :  * Returns 0 on success, -1 on error.
    1079                 :  **********************************************************************/
    1080               0 : int TABView::GetBounds(double &dXMin, double &dYMin, 
    1081                 :                        double &dXMax, double &dYMax,
    1082                 :                        GBool bForce /*= TRUE*/)
    1083                 : {
    1084               0 :     if (m_nMainTableIndex == -1)
    1085                 :     {
    1086                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1087               0 :              "GetBounds() can be called only after dataset has been opened.");
    1088               0 :         return -1;
    1089                 :     }
    1090                 : 
    1091               0 :     return m_papoTABFiles[m_nMainTableIndex]->GetBounds(dXMin, dYMin,
    1092                 :                                                         dXMax, dYMax,
    1093               0 :                                                         bForce);
    1094                 : }
    1095                 : 
    1096                 : /**********************************************************************
    1097                 :  *                   TABView::GetExtent()
    1098                 :  *
    1099                 :  * Fetch extent of the data currently stored in the dataset.
    1100                 :  *
    1101                 :  * The bForce flag has no effect on TAB files since that value is
    1102                 :  * always in the header.
    1103                 :  *
    1104                 :  * Returns OGRERR_NONE/OGRRERR_FAILURE.
    1105                 :  **********************************************************************/
    1106               0 : OGRErr TABView::GetExtent (OGREnvelope *psExtent, int bForce)
    1107                 : {
    1108               0 :     if (m_nMainTableIndex == -1)
    1109                 :     {
    1110                 :         CPLError(CE_Failure, CPLE_AppDefined,
    1111               0 :              "GetExtent() can be called only after dataset has been opened.");
    1112               0 :         return -1;
    1113                 :     }
    1114                 : 
    1115               0 :     return m_papoTABFiles[m_nMainTableIndex]->GetExtent(psExtent, bForce);
    1116                 : 
    1117                 : }
    1118                 : 
    1119                 : /**********************************************************************
    1120                 :  *                   TABView::GetFeatureCountByType()
    1121                 :  *
    1122                 :  * Return number of features of each type.
    1123                 :  *
    1124                 :  * Note that the sum of the 4 returned values may be different from
    1125                 :  * the total number of features since features with NONE geometry
    1126                 :  * are not taken into account here.
    1127                 :  *
    1128                 :  * Note: the bForce flag has nmo effect on .TAB files since the info
    1129                 :  * is always in the header.
    1130                 :  *
    1131                 :  * Returns 0 on success, or silently returns -1 (with no error) if this
    1132                 :  * information is not available.
    1133                 :  **********************************************************************/
    1134               0 : int TABView::GetFeatureCountByType(int &numPoints, int &numLines,
    1135                 :                                    int &numRegions, int &numTexts,
    1136                 :                                    GBool bForce /*= TRUE*/)
    1137                 : {
    1138               0 :     if (m_nMainTableIndex == -1)
    1139               0 :         return -1;
    1140                 : 
    1141               0 :     return m_papoTABFiles[m_nMainTableIndex]->GetFeatureCountByType(numPoints,
    1142                 :                                                                     numLines,
    1143                 :                                                                     numRegions,
    1144                 :                                                                     numTexts,
    1145               0 :                                                                     bForce);
    1146                 : }
    1147                 : 
    1148                 : 
    1149                 : /**********************************************************************
    1150                 :  *                   TABView::GetSpatialRef()
    1151                 :  *
    1152                 :  * Returns a reference to an OGRSpatialReference for this dataset.
    1153                 :  * If the projection parameters have not been parsed yet, then we will
    1154                 :  * parse them before returning.
    1155                 :  *
    1156                 :  * The returned object is owned and maintained by this TABFile and
    1157                 :  * should not be modified or freed by the caller.
    1158                 :  *
    1159                 :  * Returns NULL if the SpatialRef cannot be accessed.
    1160                 :  **********************************************************************/
    1161               0 : OGRSpatialReference *TABView::GetSpatialRef()
    1162                 : {
    1163               0 :     if (m_nMainTableIndex == -1)
    1164                 :     {
    1165                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1166               0 :                  "GetSpatialRef() failed: file has not been opened yet.");
    1167               0 :         return NULL;
    1168                 :     }
    1169                 : 
    1170               0 :     return m_papoTABFiles[m_nMainTableIndex]->GetSpatialRef();
    1171                 : }
    1172                 : 
    1173                 : /**********************************************************************
    1174                 :  *                   TABView::SetSpatialRef()
    1175                 :  **********************************************************************/
    1176               0 : int TABView::SetSpatialRef(OGRSpatialReference *poSpatialRef)
    1177                 : {
    1178               0 :     if (m_nMainTableIndex == -1)
    1179                 :     {
    1180                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1181               0 :                  "SetSpatialRef() failed: file has not been opened yet.");
    1182               0 :         return -1;
    1183                 :     }
    1184                 : 
    1185               0 :     return m_papoTABFiles[m_nMainTableIndex]->SetSpatialRef(poSpatialRef);
    1186                 : }
    1187                 : 
    1188                 : 
    1189                 : 
    1190                 : /**********************************************************************
    1191                 :  *                   TABView::SetBounds()
    1192                 :  **********************************************************************/
    1193               0 : int TABView::SetBounds(double dXMin, double dYMin, 
    1194                 :                        double dXMax, double dYMax)
    1195                 : {
    1196               0 :     if (m_nMainTableIndex == -1)
    1197                 :     {
    1198                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1199               0 :                  "SetBounds() failed: file has not been opened yet.");
    1200               0 :         return -1;
    1201                 :     }
    1202                 : 
    1203               0 :     return m_papoTABFiles[m_nMainTableIndex]->SetBounds(dXMin, dYMin,
    1204               0 :                                                         dXMax, dYMax);
    1205                 : }
    1206                 : 
    1207                 : /************************************************************************/
    1208                 : /*                           TestCapability()                           */
    1209                 : /************************************************************************/
    1210                 : 
    1211               0 : int TABView::TestCapability( const char * pszCap )
    1212                 : 
    1213                 : {
    1214               0 :     if( EQUAL(pszCap,OLCRandomRead) )
    1215               0 :         return TRUE;
    1216                 : 
    1217               0 :     else if( EQUAL(pszCap,OLCSequentialWrite))
    1218               0 :         return TRUE;
    1219                 : 
    1220               0 :     else if( EQUAL(pszCap,OLCRandomWrite))
    1221               0 :         return FALSE;
    1222                 : 
    1223               0 :     else if( EQUAL(pszCap,OLCFastFeatureCount) )
    1224               0 :         return m_poFilterGeom == NULL;
    1225                 : 
    1226               0 :     else if( EQUAL(pszCap,OLCFastSpatialFilter) )
    1227               0 :         return FALSE;
    1228                 : 
    1229               0 :     else if( EQUAL(pszCap,OLCFastGetExtent) )
    1230               0 :         return TRUE;
    1231                 : 
    1232                 :     else 
    1233               0 :         return FALSE;
    1234                 : }
    1235                 : 
    1236                 : 
    1237                 : 
    1238                 : 
    1239                 : 
    1240                 : 
    1241                 : /**********************************************************************
    1242                 :  *                   TABView::Dump()
    1243                 :  *
    1244                 :  * Dump block contents... available only in DEBUG mode.
    1245                 :  **********************************************************************/
    1246                 : #ifdef DEBUG
    1247                 : 
    1248               0 : void TABView::Dump(FILE *fpOut /*=NULL*/)
    1249                 : {
    1250               0 :     if (fpOut == NULL)
    1251               0 :         fpOut = stdout;
    1252                 : 
    1253               0 :     fprintf(fpOut, "----- TABView::Dump() -----\n");
    1254                 : 
    1255               0 :     if (m_numTABFiles > 0)
    1256                 :     {
    1257               0 :         fprintf(fpOut, "File is not opened.\n");
    1258                 :     }
    1259                 :     else
    1260                 :     {
    1261               0 :         fprintf(fpOut, "File is opened: %s\n", m_pszFname);
    1262               0 :         fprintf(fpOut, "View contains %d tables\n", m_numTABFiles);
    1263                 : 
    1264                 :     }
    1265                 : 
    1266               0 :     fflush(fpOut);
    1267               0 : }
    1268                 : 
    1269                 : #endif // DEBUG
    1270                 : 
    1271                 : 
    1272                 : 
    1273                 : /*=====================================================================
    1274                 :  *                      class TABRelation
    1275                 :  *====================================================================*/
    1276                 : 
    1277                 : 
    1278                 : /**********************************************************************
    1279                 :  *                   TABRelation::TABRelation()
    1280                 :  *
    1281                 :  * Constructor.
    1282                 :  **********************************************************************/
    1283               0 : TABRelation::TABRelation()
    1284                 : {
    1285               0 :     m_poMainTable = NULL;
    1286               0 :     m_pszMainFieldName = NULL;
    1287               0 :     m_nMainFieldNo = -1;
    1288                 : 
    1289               0 :     m_poRelTable = NULL;
    1290               0 :     m_pszRelFieldName = NULL;
    1291               0 :     m_nRelFieldNo = -1;
    1292               0 :     m_nRelFieldIndexNo = -1;
    1293               0 :     m_poRelINDFileRef = NULL;
    1294                 : 
    1295               0 :     m_nUniqueRecordNo = 0;
    1296                 : 
    1297               0 :     m_panMainTableFieldMap = NULL;
    1298               0 :     m_panRelTableFieldMap = NULL;
    1299                 : 
    1300               0 :     m_poDefn = NULL;
    1301               0 : }
    1302                 : 
    1303                 : /**********************************************************************
    1304                 :  *                   TABRelation::~TABRelation()
    1305                 :  *
    1306                 :  * Destructor.
    1307                 :  **********************************************************************/
    1308               0 : TABRelation::~TABRelation()
    1309                 : {
    1310               0 :     ResetAllMembers();
    1311               0 : }
    1312                 : 
    1313                 : /**********************************************************************
    1314                 :  *                   TABRelation::ResetAllMembers()
    1315                 :  *
    1316                 :  * Reset all class members.
    1317                 :  **********************************************************************/
    1318               0 : void TABRelation::ResetAllMembers()
    1319                 : {
    1320               0 :     m_poMainTable = NULL;
    1321               0 :     CPLFree(m_pszMainFieldName);
    1322               0 :     m_pszMainFieldName = NULL;
    1323               0 :     m_nMainFieldNo = -1;
    1324                 : 
    1325               0 :     m_poRelTable = NULL;
    1326               0 :     CPLFree(m_pszRelFieldName);
    1327               0 :     m_pszRelFieldName = NULL;
    1328               0 :     m_nRelFieldNo = -1;
    1329               0 :     m_nRelFieldIndexNo = -1;
    1330                 : 
    1331               0 :     m_nUniqueRecordNo = 0;
    1332                 : 
    1333                 :     // No need to close m_poRelINDFileRef since we only got a ref. to it
    1334               0 :     m_poRelINDFileRef = NULL;
    1335                 : 
    1336               0 :     CPLFree(m_panMainTableFieldMap);
    1337               0 :     m_panMainTableFieldMap = NULL;
    1338               0 :     CPLFree(m_panRelTableFieldMap);
    1339               0 :     m_panRelTableFieldMap = NULL;
    1340                 : 
    1341                 :     /*-----------------------------------------------------------------
    1342                 :      * Note: we have to check the reference count before deleting m_poDefn
    1343                 :      *----------------------------------------------------------------*/
    1344               0 :     if (m_poDefn && m_poDefn->Dereference() == 0)
    1345               0 :         delete m_poDefn;
    1346               0 :     m_poDefn = NULL;
    1347                 : 
    1348               0 : }
    1349                 : 
    1350                 : /**********************************************************************
    1351                 :  *                   TABRelation::Init()
    1352                 :  *
    1353                 :  * Set the details of the relation: the main and related tables, the fields
    1354                 :  * through which they will be connected, and the list of fields to select.
    1355                 :  * After this call, we are ready to read data records.
    1356                 :  *
    1357                 :  * For write access, Init() is called with pszMain/RelFieldName and
    1358                 :  * **papszSelectedFields passed as NULL.  They will have to be set through
    1359                 :  * other methods before a first feature can be written.
    1360                 :  *
    1361                 :  * A new OGRFeatureDefn is also built for the combined tables.
    1362                 :  *
    1363                 :  * Returns 0 on success, or -1 or error.
    1364                 :  **********************************************************************/
    1365               0 : int  TABRelation::Init(const char *pszViewName,
    1366                 :                        TABFile *poMainTable, TABFile *poRelTable,
    1367                 :                        const char *pszMainFieldName,
    1368                 :                        const char *pszRelFieldName,
    1369                 :                        char **papszSelectedFields)
    1370                 : {
    1371               0 :     if (poMainTable == NULL || poRelTable == NULL)
    1372               0 :         return -1;
    1373                 : 
    1374                 :     // We'll need the feature Defn later...
    1375                 :     OGRFeatureDefn *poMainDefn, *poRelDefn;
    1376                 : 
    1377               0 :     poMainDefn = poMainTable->GetLayerDefn();
    1378               0 :     poRelDefn = poRelTable->GetLayerDefn();
    1379                 : 
    1380                 :     /*-----------------------------------------------------------------
    1381                 :      * Keep info for later use about source tables, etc.
    1382                 :      *----------------------------------------------------------------*/
    1383               0 :     ResetAllMembers();
    1384                 : 
    1385               0 :     m_poMainTable = poMainTable;
    1386               0 :     if (pszMainFieldName)
    1387                 :     {
    1388               0 :         m_pszMainFieldName = CPLStrdup(pszMainFieldName);
    1389               0 :         m_nMainFieldNo = poMainDefn->GetFieldIndex(pszMainFieldName);
    1390                 :     }
    1391                 : 
    1392               0 :     m_poRelTable = poRelTable;
    1393               0 :     if (pszRelFieldName)
    1394                 :     {
    1395               0 :         m_pszRelFieldName = CPLStrdup(pszRelFieldName);
    1396               0 :         m_nRelFieldNo = poRelDefn->GetFieldIndex(pszRelFieldName);
    1397               0 :         m_nRelFieldIndexNo = poRelTable->GetFieldIndexNumber(m_nRelFieldNo);
    1398               0 :         m_poRelINDFileRef = poRelTable->GetINDFileRef();
    1399                 : 
    1400               0 :         if (m_nRelFieldIndexNo >= 0 && m_poRelINDFileRef == NULL)
    1401                 :         {
    1402                 :             CPLError(CE_Failure, CPLE_FileIO,
    1403                 :                      "Field %s is indexed but the .IND file is missing.",
    1404               0 :                      pszRelFieldName);
    1405               0 :             return -1;
    1406                 :         }
    1407                 :     }
    1408                 : 
    1409                 :     /*-----------------------------------------------------------------
    1410                 :      * Init field maps.  For each field in each table, a -1 means that
    1411                 :      * the field is not selected, and a value >=0 is the index of the 
    1412                 :      * field in the view's FeatureDefn
    1413                 :      *----------------------------------------------------------------*/
    1414                 :     int i;
    1415               0 :     int numFields1 = (poMainDefn?poMainDefn->GetFieldCount():0);
    1416               0 :     int numFields2 = (poRelDefn?poRelDefn->GetFieldCount():0);
    1417                 : 
    1418               0 :     m_panMainTableFieldMap = (int*)CPLMalloc((numFields1+1)*sizeof(int));
    1419               0 :     for(i=0; i<numFields1; i++)
    1420               0 :         m_panMainTableFieldMap[i] = -1;
    1421               0 :     m_panRelTableFieldMap = (int*)CPLMalloc((numFields2+1)*sizeof(int));
    1422               0 :     for(i=0; i<numFields2; i++)
    1423               0 :         m_panRelTableFieldMap[i] = -1;
    1424                 : 
    1425                 :     /*-----------------------------------------------------------------
    1426                 :      * If selectedFields = "*" then select all fields from both tables
    1427                 :      *----------------------------------------------------------------*/
    1428               0 :     if (CSLCount(papszSelectedFields) == 1 && 
    1429               0 :         EQUAL(papszSelectedFields[0], "*") )
    1430                 :     {
    1431               0 :         CSLDestroy(papszSelectedFields);
    1432               0 :         papszSelectedFields = NULL;
    1433                 : 
    1434               0 :         for(i=0; i<numFields1; i++)
    1435                 :         {
    1436               0 :             OGRFieldDefn *poFieldDefn = poMainDefn->GetFieldDefn(i);
    1437                 : 
    1438                 :             papszSelectedFields = CSLAddString(papszSelectedFields, 
    1439               0 :                                                poFieldDefn->GetNameRef());
    1440                 :         }
    1441                 : 
    1442               0 :         for(i=0; i<numFields2; i++)
    1443                 :         {
    1444               0 :             OGRFieldDefn *poFieldDefn = poRelDefn->GetFieldDefn(i);
    1445                 : 
    1446               0 :             if (CSLFindString(papszSelectedFields, 
    1447                 :                               poFieldDefn->GetNameRef()) != -1)
    1448               0 :                 continue;  // Avoid duplicate field name in view
    1449                 : 
    1450                 :             papszSelectedFields = CSLAddString(papszSelectedFields, 
    1451               0 :                                                poFieldDefn->GetNameRef());
    1452                 :         }
    1453                 : 
    1454                 :     }
    1455                 : 
    1456                 :     /*-----------------------------------------------------------------
    1457                 :      * Create new FeatureDefn and copy selected fields definitions
    1458                 :      * while updating the appropriate field maps.
    1459                 :      *----------------------------------------------------------------*/
    1460               0 :     int nIndex, numSelFields = CSLCount(papszSelectedFields);
    1461                 :     OGRFieldDefn *poFieldDefn;
    1462                 : 
    1463               0 :     m_poDefn = new OGRFeatureDefn(pszViewName);
    1464                 :     // Ref count defaults to 0... set it to 1
    1465               0 :     m_poDefn->Reference();
    1466                 : 
    1467               0 :     for(i=0; i<numSelFields ; i++)
    1468                 :     {
    1469               0 :         if (poMainDefn &&
    1470               0 :             (nIndex=poMainDefn->GetFieldIndex(papszSelectedFields[i])) >=0)
    1471                 :         {
    1472                 :             /* Field from the main table
    1473                 :              */
    1474               0 :             poFieldDefn = poMainDefn->GetFieldDefn(nIndex);
    1475               0 :             m_poDefn->AddFieldDefn(poFieldDefn);
    1476               0 :             m_panMainTableFieldMap[nIndex] = m_poDefn->GetFieldCount()-1;
    1477                 :         }
    1478               0 :         else if (poRelDefn &&
    1479               0 :                  (nIndex=poRelDefn->GetFieldIndex(papszSelectedFields[i]))>=0)
    1480                 :         {
    1481                 :             /* Field from the related table
    1482                 :              */
    1483               0 :             poFieldDefn = poRelDefn->GetFieldDefn(nIndex);
    1484               0 :             m_poDefn->AddFieldDefn(poFieldDefn);
    1485               0 :             m_panRelTableFieldMap[nIndex] = m_poDefn->GetFieldCount()-1;
    1486                 :         }
    1487                 :         else
    1488                 :         {
    1489                 :             // Hummm... field does not exist... likely an unsupported feature!
    1490                 :             // At least send a warning and ignore the field.
    1491                 :             CPLError(CE_Warning, CPLE_IllegalArg,
    1492                 :                      "Selected Field %s not found in source tables %s and %s",
    1493                 :                      papszSelectedFields[i], 
    1494               0 :                      poMainDefn->GetName(), poRelDefn->GetName());
    1495                 :         }
    1496                 :     }
    1497                 : 
    1498               0 :     return 0;
    1499                 : }
    1500                 : 
    1501                 : 
    1502                 : /**********************************************************************
    1503                 :  *                   TABRelation::CreateRelFields()
    1504                 :  *
    1505                 :  * For write access, create the integer fields in each table that will
    1506                 :  * link them, and setup everything to be ready to write the first feature.
    1507                 :  *
    1508                 :  * This function should be called just before writing the first feature.
    1509                 :  *
    1510                 :  * Returns 0 on success, or -1 or error.
    1511                 :  **********************************************************************/
    1512               0 : int  TABRelation::CreateRelFields()
    1513                 : {
    1514                 :     int i;
    1515                 : 
    1516                 :     /*-----------------------------------------------------------------
    1517                 :      * Create the field in each table.  
    1518                 :      * The default name is "MI_refnum" but if a field with the same name
    1519                 :      * already exists then we'll try to generate a unique name.
    1520                 :      *----------------------------------------------------------------*/
    1521               0 :     m_pszMainFieldName = CPLStrdup("MI_Refnum      ");
    1522               0 :     strcpy(m_pszMainFieldName, "MI_Refnum");
    1523               0 :     i = 1;
    1524               0 :     while(m_poDefn->GetFieldIndex(m_pszMainFieldName) >= 0)
    1525                 :     {
    1526               0 :         sprintf(m_pszMainFieldName, "MI_Refnum_%d", i++);
    1527                 :     }
    1528               0 :     m_pszRelFieldName = CPLStrdup(m_pszMainFieldName);
    1529                 : 
    1530               0 :     m_nMainFieldNo = m_nRelFieldNo = -1;
    1531               0 :     if (m_poMainTable->AddFieldNative(m_pszMainFieldName,
    1532               0 :                                       TABFInteger, 0, 0) == 0)
    1533               0 :         m_nMainFieldNo = m_poMainTable->GetLayerDefn()->GetFieldCount()-1;
    1534                 : 
    1535               0 :     if (m_poRelTable->AddFieldNative(m_pszRelFieldName,
    1536               0 :                                      TABFInteger, 0, 0) == 0)
    1537               0 :         m_nRelFieldNo = m_poRelTable->GetLayerDefn()->GetFieldCount()-1;
    1538                 : 
    1539               0 :     if (m_nMainFieldNo == -1 || m_nRelFieldNo == -1)
    1540               0 :         return -1;
    1541                 : 
    1542               0 :     if (m_poMainTable->SetFieldIndexed(m_nMainFieldNo) == -1)
    1543               0 :         return -1;
    1544                 : 
    1545               0 :     if ((m_nRelFieldIndexNo=m_poRelTable->SetFieldIndexed(m_nRelFieldNo)) ==-1)
    1546               0 :         return -1;
    1547                 : 
    1548               0 :     m_poRelINDFileRef = m_poRelTable->GetINDFileRef();
    1549                 : 
    1550                 :     /*-----------------------------------------------------------------
    1551                 :      * Update field maps
    1552                 :      *----------------------------------------------------------------*/
    1553                 :     OGRFeatureDefn *poMainDefn, *poRelDefn;
    1554                 : 
    1555               0 :     poMainDefn = m_poMainTable->GetLayerDefn();
    1556               0 :     poRelDefn = m_poRelTable->GetLayerDefn();
    1557                 : 
    1558                 :     m_panMainTableFieldMap = (int*)CPLRealloc(m_panMainTableFieldMap,
    1559               0 :                                       poMainDefn->GetFieldCount()*sizeof(int));
    1560               0 :     m_panMainTableFieldMap[poMainDefn->GetFieldCount()-1] = -1;
    1561                 : 
    1562                 :     m_panRelTableFieldMap = (int*)CPLRealloc(m_panRelTableFieldMap,
    1563               0 :                                       poRelDefn->GetFieldCount()*sizeof(int));
    1564               0 :     m_panRelTableFieldMap[poRelDefn->GetFieldCount()-1] = -1;
    1565                 : 
    1566                 :     /*-----------------------------------------------------------------
    1567                 :      * Make sure the first unique field (in poRelTable) is indexed since
    1568                 :      * it is the one against which we will try to match records.
    1569                 :      *----------------------------------------------------------------*/
    1570               0 :     if ( m_poRelTable->SetFieldIndexed(0) == -1)
    1571               0 :         return -1;
    1572                 : 
    1573               0 :     return 0;
    1574                 : }
    1575                 : 
    1576                 : /**********************************************************************
    1577                 :  *                   TABRelation::GetFeature()
    1578                 :  *
    1579                 :  * Fill and return a TABFeature object for the specified feature id.
    1580                 :  *
    1581                 :  * The retuned pointer is a new TABFeature that will have to be freed
    1582                 :  * by the caller.
    1583                 :  *
    1584                 :  * Returns NULL if the specified feature id does not exist of if an
    1585                 :  * error happened.  In any case, CPLError() will have been called to
    1586                 :  * report the reason of the failure.
    1587                 :  *
    1588                 :  * __TODO__ The current implementation fetches the features from each table
    1589                 :  * and creates a 3rd feature to merge them.  There would be room for 
    1590                 :  * optimization, at least by avoiding the duplication of the geometry 
    1591                 :  * which can be big sometimes... but this would imply changes at the
    1592                 :  * lower-level in the lib. and we won't go there yet.
    1593                 :  **********************************************************************/
    1594               0 : TABFeature *TABRelation::GetFeature(int nFeatureId)
    1595                 : {
    1596                 :     TABFeature *poMainFeature;
    1597                 :     TABFeature *poCurFeature;
    1598                 : 
    1599                 :     /*-----------------------------------------------------------------
    1600                 :      * Make sure init() has been called
    1601                 :      *----------------------------------------------------------------*/
    1602               0 :     if (m_poMainTable == NULL || m_poRelTable == NULL)
    1603                 :     {
    1604                 :         CPLError(CE_Failure, CPLE_IllegalArg,
    1605               0 :                  "GetFeatureRef() failed: object not initialized yet!");
    1606               0 :         return NULL;
    1607                 :     }
    1608                 : 
    1609                 :     /*-----------------------------------------------------------------
    1610                 :      * Read main feature and create a new one of the right type
    1611                 :      *----------------------------------------------------------------*/
    1612               0 :     if ((poMainFeature = m_poMainTable->GetFeatureRef(nFeatureId)) == NULL)
    1613                 :     {
    1614                 :         // Feature cannot be read from main table... 
    1615                 :         // an error has already been reported.
    1616               0 :         return NULL;
    1617                 :     }
    1618                 : 
    1619               0 :     poCurFeature = poMainFeature->CloneTABFeature(m_poDefn);
    1620                 : 
    1621                 :     /*-----------------------------------------------------------------
    1622                 :      * Keep track of FID and copy the geometry 
    1623                 :      *----------------------------------------------------------------*/
    1624               0 :     poCurFeature->SetFID(nFeatureId);
    1625                 : 
    1626               0 :     if (poCurFeature->GetFeatureClass() != TABFCNoGeomFeature)
    1627                 :     {
    1628                 :         OGRGeometry *poGeom;
    1629               0 :         poGeom = poMainFeature->GetGeometryRef();
    1630               0 :         poCurFeature->SetGeometry(poGeom);
    1631                 :     }
    1632                 : 
    1633                 :     /*-----------------------------------------------------------------
    1634                 :      * Fetch feature from related table
    1635                 :      *
    1636                 :      * __TODO__ Right now we support only many-to-1 relationships, but
    1637                 :      *          it might be possible to have several related entries
    1638                 :      *          for a single key, and in this case we should return
    1639                 :      *          one new feature for each of them.
    1640                 :      *----------------------------------------------------------------*/
    1641               0 :     TABFeature *poRelFeature=NULL;
    1642                 :     GByte *pKey = BuildFieldKey(poMainFeature, m_nMainFieldNo,
    1643               0 :                             m_poMainTable->GetNativeFieldType(m_nMainFieldNo),
    1644               0 :                                 m_nRelFieldIndexNo);
    1645                 :     int i;
    1646               0 :     int nRelFeatureId = m_poRelINDFileRef->FindFirst(m_nRelFieldIndexNo, pKey);
    1647                 :     
    1648               0 :     if (nRelFeatureId > 0)
    1649               0 :         poRelFeature = m_poRelTable->GetFeatureRef(nRelFeatureId);
    1650                 : 
    1651                 :     /*-----------------------------------------------------------------
    1652                 :      * Copy fields from poMainFeature
    1653                 :      *----------------------------------------------------------------*/
    1654               0 :     for(i=0; i<poMainFeature->GetFieldCount(); i++)
    1655                 :     {
    1656               0 :         if (m_panMainTableFieldMap[i] != -1)
    1657                 :         {
    1658                 :             poCurFeature->SetField(m_panMainTableFieldMap[i], 
    1659               0 :                                      poMainFeature->GetRawFieldRef(i));
    1660                 :         }
    1661                 :     }
    1662                 : 
    1663                 :     /*-----------------------------------------------------------------
    1664                 :      * Copy fields from poRelFeature...
    1665                 :      *
    1666                 :      * NOTE: For now, if no corresponding feature is found in RelTable
    1667                 :      *       then we will just leave the corresponding fields unset.
    1668                 :      *----------------------------------------------------------------*/
    1669               0 :     for(i=0; poRelFeature && i<poRelFeature->GetFieldCount(); i++)
    1670                 :     {
    1671               0 :         if (m_panRelTableFieldMap[i] != -1)
    1672                 :         {
    1673                 :             poCurFeature->SetField(m_panRelTableFieldMap[i], 
    1674               0 :                                      poRelFeature->GetRawFieldRef(i));
    1675                 :         }
    1676                 :     }
    1677                 : 
    1678               0 :     return poCurFeature;
    1679                 : }
    1680                 : 
    1681                 : 
    1682                 : 
    1683                 : /**********************************************************************
    1684                 :  *                   TABRelation::BuildFieldKey()
    1685                 :  *
    1686                 :  * Return the index key for the specified field in poFeature.
    1687                 :  * Simply maps the call to the proper method in the TABINDFile class.
    1688                 :  *
    1689                 :  * Returns a reference to a TABINDFile internal buffer that should not
    1690                 :  * be freed by the caller.
    1691                 :  **********************************************************************/
    1692               0 : GByte *TABRelation::BuildFieldKey(TABFeature *poFeature, int nFieldNo,
    1693                 :                                   TABFieldType eType, int nIndexNo)
    1694                 : {
    1695               0 :     GByte *pKey = NULL;
    1696                 : 
    1697               0 :     switch(eType)
    1698                 :     {
    1699                 :       case TABFChar:
    1700                 :         pKey = m_poRelINDFileRef->BuildKey(nIndexNo,
    1701               0 :                              poFeature->GetFieldAsString(nFieldNo));
    1702               0 :         break;
    1703                 : 
    1704                 :       case TABFDecimal:
    1705                 :       case TABFFloat:
    1706                 :         pKey = m_poRelINDFileRef->BuildKey(nIndexNo,
    1707               0 :                              poFeature->GetFieldAsDouble(nFieldNo));
    1708               0 :         break;
    1709                 : 
    1710                 :       // __TODO__ DateTime fields are 8 bytes long, not supported yet by
    1711                 :       // the indexing code (see bug #1844).
    1712                 :       case TABFDateTime:
    1713                 :         CPLError(CE_Failure, CPLE_NotSupported,
    1714               0 :                  "TABRelation on field of type DateTime not supported yet.");
    1715               0 :         break;
    1716                 : 
    1717                 :       case TABFInteger:
    1718                 :       case TABFSmallInt:
    1719                 :       case TABFDate:
    1720                 :       case TABFTime:
    1721                 :       case TABFLogical:
    1722                 :       default:
    1723                 :         pKey = m_poRelINDFileRef->BuildKey(nIndexNo,
    1724               0 :                              poFeature->GetFieldAsInteger(nFieldNo));
    1725                 :         break;
    1726                 :     }
    1727                 : 
    1728               0 :     return pKey;
    1729                 : }
    1730                 : 
    1731                 : 
    1732                 : /**********************************************************************
    1733                 :  *                   TABRelation::GetNativeFieldType()
    1734                 :  *
    1735                 :  * Returns the native MapInfo field type for the specified field.
    1736                 :  *
    1737                 :  * Returns TABFUnknown if file is not opened, or if specified field index is
    1738                 :  * invalid.
    1739                 :  *
    1740                 :  * Note that field ids are positive and start at 0.
    1741                 :  **********************************************************************/
    1742               0 : TABFieldType TABRelation::GetNativeFieldType(int nFieldId)
    1743                 : {
    1744                 :     int i, numFields;
    1745                 : 
    1746               0 :     if (m_poMainTable==NULL || m_poRelTable==NULL ||
    1747                 :         m_panMainTableFieldMap==NULL || m_panRelTableFieldMap==NULL)
    1748               0 :         return TABFUnknown;
    1749                 : 
    1750                 :     /*-----------------------------------------------------------------
    1751                 :      * Look for nFieldId in the field maps and call the corresponding
    1752                 :      * TAB file's GetNativeFieldType()
    1753                 :      *----------------------------------------------------------------*/
    1754               0 :     numFields = m_poMainTable->GetLayerDefn()->GetFieldCount();
    1755               0 :     for(i=0; i<numFields; i++)
    1756                 :     {
    1757               0 :         if (m_panMainTableFieldMap[i] == nFieldId)
    1758                 :         {
    1759               0 :             return m_poMainTable->GetNativeFieldType(i);
    1760                 :         }
    1761                 :     }
    1762                 : 
    1763               0 :     numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
    1764               0 :     for(i=0; i<numFields; i++)
    1765                 :     {
    1766               0 :         if (m_panRelTableFieldMap[i] == nFieldId)
    1767                 :         {
    1768               0 :             return m_poRelTable->GetNativeFieldType(i);
    1769                 :         }
    1770                 :     }
    1771                 : 
    1772               0 :     return TABFUnknown;
    1773                 : }
    1774                 : 
    1775                 : 
    1776                 : /**********************************************************************
    1777                 :  *                   TABRelation::AddFieldNative()
    1778                 :  *
    1779                 :  * Create a new field using a native mapinfo data type... this is an 
    1780                 :  * alternative to defining fields through the OGR interface.
    1781                 :  * This function should be called after creating a new dataset, but before 
    1782                 :  * writing the first feature.
    1783                 :  *
    1784                 :  * This function will build/update the OGRFeatureDefn that will have to be
    1785                 :  * used when writing features to this dataset.
    1786                 :  *
    1787                 :  * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn().
    1788                 :  *
    1789                 :  * Returns 0 on success, -1 on error.
    1790                 :  **********************************************************************/
    1791               0 : int TABRelation::AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
    1792                 :                                 int nWidth /*=0*/, int nPrecision /*=0*/,
    1793                 :                                 GBool bIndexed /*=FALSE*/, GBool bUnique/*=FALSE*/, int bApproxOK)
    1794                 : {
    1795               0 :     if (m_poMainTable==NULL || m_poRelTable==NULL ||
    1796                 :         m_panMainTableFieldMap==NULL || m_panRelTableFieldMap==NULL)
    1797               0 :         return -1;
    1798                 : 
    1799               0 :     if (!bUnique)
    1800                 :     {
    1801                 :         /*-------------------------------------------------------------
    1802                 :          * Add field to poMainTable and to m_poDefn
    1803                 :          *------------------------------------------------------------*/
    1804               0 :         if (m_poMainTable->AddFieldNative(pszName, eMapInfoType,
    1805                 :                                           nWidth, nPrecision,
    1806               0 :                                           bIndexed, bUnique, bApproxOK) != 0)
    1807               0 :             return -1;
    1808                 : 
    1809               0 :         OGRFeatureDefn *poMainDefn = m_poMainTable->GetLayerDefn();
    1810                 : 
    1811                 :         m_panMainTableFieldMap = (int*)CPLRealloc(m_panMainTableFieldMap,
    1812               0 :                                       poMainDefn->GetFieldCount()*sizeof(int));
    1813                 : 
    1814                 :         m_poDefn->AddFieldDefn(poMainDefn->GetFieldDefn(poMainDefn->
    1815               0 :                                                           GetFieldCount()-1));
    1816                 : 
    1817               0 :         m_panMainTableFieldMap[poMainDefn->GetFieldCount()-1] = 
    1818               0 :                                             m_poDefn->GetFieldCount()-1;
    1819                 :     }
    1820                 :     else
    1821                 :     {
    1822                 :         /*-------------------------------------------------------------
    1823                 :          * Add field to poRelTable and to m_poDefn
    1824                 :          *------------------------------------------------------------*/
    1825               0 :         if (m_poRelTable->AddFieldNative(pszName, eMapInfoType,
    1826                 :                                          nWidth, nPrecision,
    1827               0 :                                          bIndexed, bUnique, bApproxOK) != 0)
    1828               0 :             return -1;
    1829                 : 
    1830               0 :         OGRFeatureDefn *poRelDefn = m_poRelTable->GetLayerDefn();
    1831                 : 
    1832                 :         m_panRelTableFieldMap = (int*)CPLRealloc(m_panRelTableFieldMap,
    1833               0 :                                       poRelDefn->GetFieldCount()*sizeof(int));
    1834                 : 
    1835                 :         m_poDefn->AddFieldDefn(poRelDefn->GetFieldDefn(poRelDefn->
    1836               0 :                                                          GetFieldCount()-1));
    1837                 : 
    1838               0 :         m_panRelTableFieldMap[poRelDefn->GetFieldCount()-1] =  
    1839               0 :                                             m_poDefn->GetFieldCount()-1;
    1840                 : 
    1841                 :         // The first field in this table must be indexed.
    1842               0 :         if (poRelDefn->GetFieldCount() == 1)
    1843               0 :             m_poRelTable->SetFieldIndexed(0);
    1844                 :     }
    1845                 : 
    1846               0 :     return 0;
    1847                 : }
    1848                 : 
    1849                 : 
    1850                 : /**********************************************************************
    1851                 :  *                   TABRelation::IsFieldIndexed()
    1852                 :  *
    1853                 :  * Returns TRUE is specified field is indexed.
    1854                 :  *
    1855                 :  * Note that field ids are positive and start at 0.
    1856                 :  **********************************************************************/
    1857               0 : GBool TABRelation::IsFieldIndexed(int nFieldId)
    1858                 : {
    1859                 :     int i, numFields;
    1860                 : 
    1861               0 :     if (m_poMainTable==NULL || m_poRelTable==NULL ||
    1862                 :         m_panMainTableFieldMap==NULL || m_panRelTableFieldMap==NULL)
    1863               0 :         return FALSE;
    1864                 : 
    1865                 :     /*-----------------------------------------------------------------
    1866                 :      * Look for nFieldId in the field maps and call the corresponding
    1867                 :      * TAB file's GetNativeFieldType()
    1868                 :      *----------------------------------------------------------------*/
    1869               0 :     numFields = m_poMainTable->GetLayerDefn()->GetFieldCount();
    1870               0 :     for(i=0; i<numFields; i++)
    1871                 :     {
    1872               0 :         if (m_panMainTableFieldMap[i] == nFieldId)
    1873                 :         {
    1874               0 :             return m_poMainTable->IsFieldIndexed(i);
    1875                 :         }
    1876                 :     }
    1877                 : 
    1878               0 :     numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
    1879               0 :     for(i=0; i<numFields; i++)
    1880                 :     {
    1881               0 :         if (m_panRelTableFieldMap[i] == nFieldId)
    1882                 :         {
    1883               0 :             return m_poRelTable->IsFieldIndexed(i);
    1884                 :         }
    1885                 :     }
    1886                 : 
    1887               0 :     return FALSE;
    1888                 : }
    1889                 : 
    1890                 : /**********************************************************************
    1891                 :  *                   TABRelation::SetFieldIndexed()
    1892                 :  *
    1893                 :  * Request that the specified field be indexed.  This will create the .IND
    1894                 :  * file, etc.
    1895                 :  *
    1896                 :  * Note that field ids are positive and start at 0.
    1897                 :  *
    1898                 :  * Returns 0 on success, -1 on error.
    1899                 :  **********************************************************************/
    1900               0 : int TABRelation::SetFieldIndexed(int nFieldId)
    1901                 : {
    1902                 :     int i, numFields;
    1903                 : 
    1904               0 :     if (m_poMainTable==NULL || m_poRelTable==NULL ||
    1905                 :         m_panMainTableFieldMap==NULL || m_panRelTableFieldMap==NULL)
    1906               0 :         return -1;
    1907                 : 
    1908                 :     /*-----------------------------------------------------------------
    1909                 :      * Look for nFieldId in the field maps and call the corresponding
    1910                 :      * TAB file's GetNativeFieldType()
    1911                 :      *----------------------------------------------------------------*/
    1912               0 :     numFields = m_poMainTable->GetLayerDefn()->GetFieldCount();
    1913               0 :     for(i=0; i<numFields; i++)
    1914                 :     {
    1915               0 :         if (m_panMainTableFieldMap[i] == nFieldId)
    1916                 :         {
    1917               0 :             return m_poMainTable->SetFieldIndexed(i);
    1918                 :         }
    1919                 :     }
    1920                 : 
    1921               0 :     numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
    1922               0 :     for(i=0; i<numFields; i++)
    1923                 :     {
    1924               0 :         if (m_panRelTableFieldMap[i] == nFieldId)
    1925                 :         {
    1926               0 :             return m_poRelTable->SetFieldIndexed(i);
    1927                 :         }
    1928                 :     }
    1929                 : 
    1930               0 :     return -1;
    1931                 : }
    1932                 : 
    1933                 : /**********************************************************************
    1934                 :  *                   TABRelation::IsFieldUnique()
    1935                 :  *
    1936                 :  * Returns TRUE is specified field is part of the unique table (poRelTable).
    1937                 :  *
    1938                 :  * Note that field ids are positive and start at 0.
    1939                 :  **********************************************************************/
    1940               0 : GBool TABRelation::IsFieldUnique(int nFieldId)
    1941                 : {
    1942                 :     int i, numFields;
    1943                 : 
    1944               0 :     if (m_poMainTable==NULL || m_poRelTable==NULL ||
    1945                 :         m_panMainTableFieldMap==NULL || m_panRelTableFieldMap==NULL)
    1946               0 :         return FALSE;
    1947                 : 
    1948                 :     /*-----------------------------------------------------------------
    1949                 :      * Look for nFieldId in the poRelTable field map
    1950                 :      *----------------------------------------------------------------*/
    1951               0 :     numFields = m_poRelTable->GetLayerDefn()->GetFieldCount();
    1952               0 :     for(i=0; i<numFields; i++)
    1953                 :     {
    1954               0 :         if (m_panRelTableFieldMap[i] == nFieldId)
    1955                 :         {
    1956               0 :             return TRUE;  // If it's here then it is unique!
    1957                 :         }
    1958                 :     }
    1959                 : 
    1960               0 :     return FALSE;
    1961                 : }
    1962                 : 
    1963                 : /**********************************************************************
    1964                 :  *                   TABRelation::WriteFeature()
    1965                 :  *
    1966                 :  * Write a feature to this dataset.  
    1967                 :  *
    1968                 :  * For now only sequential writes are supported (i.e. with nFeatureId=-1)
    1969                 :  * but eventually we should be able to do random access by specifying
    1970                 :  * a value through nFeatureId.
    1971                 :  *
    1972                 :  * Returns the new featureId (> 0) on success, or -1 if an
    1973                 :  * error happened in which case, CPLError() will have been called to
    1974                 :  * report the reason of the failure.
    1975                 :  **********************************************************************/
    1976               0 : int TABRelation::WriteFeature(TABFeature *poFeature, int nFeatureId /*=-1*/)
    1977                 : {
    1978               0 :     TABFeature *poMainFeature=NULL;
    1979                 : 
    1980               0 :     if (nFeatureId != -1)
    1981                 :     {
    1982                 :         CPLError(CE_Failure, CPLE_NotSupported,
    1983               0 :                  "WriteFeature(): random access not implemented yet.");
    1984               0 :         return -1;
    1985                 :     }
    1986                 : 
    1987               0 :     CPLAssert(m_poMainTable && m_poRelTable);
    1988                 : 
    1989                 :     // We'll need the feature Defn later...
    1990                 :     OGRFeatureDefn *poMainDefn, *poRelDefn;
    1991                 : 
    1992               0 :     poMainDefn = m_poMainTable->GetLayerDefn();
    1993               0 :     poRelDefn = m_poRelTable->GetLayerDefn();
    1994                 : 
    1995                 :     /*-----------------------------------------------------------------
    1996                 :      * Create one feature for each table
    1997                 :      * Copy the geometry only to the feature from the main table
    1998                 :      *----------------------------------------------------------------*/
    1999               0 :     poMainFeature = poFeature->CloneTABFeature(poMainDefn);
    2000                 : 
    2001               0 :     if (poFeature->GetFeatureClass() != TABFCNoGeomFeature)
    2002                 :     {
    2003                 :         OGRGeometry *poGeom;
    2004               0 :         poGeom = poFeature->GetGeometryRef();
    2005               0 :         poMainFeature->SetGeometry(poGeom);
    2006                 :     }
    2007                 : 
    2008                 :     /*-----------------------------------------------------------------
    2009                 :      * Copy fields to poMainFeature
    2010                 :      *----------------------------------------------------------------*/
    2011               0 :     for(int i=0; i<poMainDefn->GetFieldCount(); i++)
    2012                 :     {
    2013               0 :         if (m_panMainTableFieldMap[i] != -1)
    2014                 :         {
    2015                 :             poMainFeature->SetField(i, 
    2016               0 :                       poFeature->GetRawFieldRef(m_panMainTableFieldMap[i]));
    2017                 :         }
    2018                 :     }
    2019                 : 
    2020                 :     /*-----------------------------------------------------------------
    2021                 :      * Look for a record id for the unique fields, and write a new 
    2022                 :      * record if necessary
    2023                 :      *----------------------------------------------------------------*/
    2024               0 :     int nRecordNo = 0;
    2025               0 :     int nUniqueIndexNo=-1;
    2026               0 :     if (m_panMainTableFieldMap[0] != -1)
    2027               0 :         nUniqueIndexNo =m_poRelTable->GetFieldIndexNumber( 0 );
    2028                 : 
    2029               0 :     if (nUniqueIndexNo > 0)
    2030                 :     {
    2031                 :         GByte *pKey = BuildFieldKey(poFeature, 0,
    2032               0 :                                     m_poRelTable->GetNativeFieldType(0),
    2033               0 :                                     nUniqueIndexNo);
    2034                 : 
    2035               0 :         if ((nRecordNo=m_poRelINDFileRef->FindFirst(nUniqueIndexNo, pKey))==-1)
    2036               0 :             return -1;
    2037                 : 
    2038               0 :         if (nRecordNo == 0)
    2039                 :         {
    2040                 :             /*---------------------------------------------------------
    2041                 :              * No record in poRelTable yet for this unique value...
    2042                 :              * add one now...
    2043                 :              *--------------------------------------------------------*/
    2044               0 :             TABFeature *poRelFeature = new TABFeature(poRelDefn);
    2045                 : 
    2046               0 :             for(int i=0;  i<poRelDefn->GetFieldCount(); i++)
    2047                 :             {
    2048               0 :                 if (m_panRelTableFieldMap[i] != -1)
    2049                 :                 {
    2050                 :                     poRelFeature->SetField(i, 
    2051               0 :                           poFeature->GetRawFieldRef(m_panRelTableFieldMap[i]));
    2052                 :                 }
    2053                 :             }
    2054                 : 
    2055               0 :             nRecordNo = ++m_nUniqueRecordNo;
    2056                 : 
    2057               0 :             poRelFeature->SetField(m_nRelFieldNo, nRecordNo);
    2058                 : 
    2059               0 :             if (m_poRelTable->CreateFeature(poRelFeature) == OGRERR_NONE)
    2060               0 :                 return -1;
    2061                 : 
    2062               0 :             delete poRelFeature;
    2063                 :         }
    2064                 :     }
    2065                 : 
    2066                 : 
    2067                 :     /*-----------------------------------------------------------------
    2068                 :      * Write poMainFeature to the main table
    2069                 :      *----------------------------------------------------------------*/
    2070               0 :     poMainFeature->SetField(m_nMainFieldNo, nRecordNo);
    2071                 : 
    2072               0 :     if (m_poMainTable->CreateFeature(poMainFeature) != OGRERR_NONE)
    2073               0 :         nFeatureId = poMainFeature->GetFID();
    2074                 :     else
    2075               0 :         nFeatureId = -1;
    2076                 : 
    2077               0 :     delete poMainFeature;
    2078                 : 
    2079               0 :     return nFeatureId;
    2080                 : }
    2081                 : 
    2082                 : 
    2083                 : /**********************************************************************
    2084                 :  *                   TABFile::SetFeatureDefn()
    2085                 :  *
    2086                 :  * NOT FULLY IMPLEMENTED YET... 
    2087                 :  *
    2088                 :  * Returns 0 on success, -1 on error.
    2089                 :  **********************************************************************/
    2090               0 : int TABRelation::SetFeatureDefn(OGRFeatureDefn *poFeatureDefn,
    2091                 :                          TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */)
    2092                 : {
    2093               0 :     if (m_poDefn && m_poDefn->GetFieldCount() > 0)
    2094                 :     {
    2095               0 :         CPLAssert(m_poDefn==NULL);
    2096               0 :         return -1;
    2097                 :     }
    2098                 : 
    2099                 :     /*-----------------------------------------------------------------
    2100                 :      * Keep a reference to the OGRFeatureDefn... we'll have to take the
    2101                 :      * reference count into account when we are done with it.
    2102                 :      *----------------------------------------------------------------*/
    2103               0 :     if (m_poDefn && m_poDefn->Dereference() == 0)
    2104               0 :         delete m_poDefn;
    2105                 : 
    2106               0 :     m_poDefn = poFeatureDefn;
    2107               0 :     m_poDefn->Reference();
    2108                 : 
    2109               0 :     return 0;
    2110                 : }

Generated by: LCOV version 1.7