LTP GCOV extension - code coverage report
Current view: directory - ogr/ogrsf_frmts/mitab - mitab_tabview.cpp
Test: gdal_filtered.info
Date: 2010-07-12 Instrumented lines: 635
Code covered: 0.0 % Executed lines: 0

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

Generated by: LTP GCOV extension version 1.5