LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/mitab - mitab_tabfile.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 801 385 48.1 %
Date: 2011-12-18 Functions: 35 19 54.3 %

       1                 : /**********************************************************************
       2                 :  * $Id: mitab_tabfile.cpp,v 1.78 2010-10-08 18:40:12 aboudreault Exp $
       3                 :  *
       4                 :  * Name:     mitab_tabfile.cpp
       5                 :  * Project:  MapInfo TAB Read/Write library
       6                 :  * Language: C++
       7                 :  * Purpose:  Implementation of the TABFile class, the main class of the lib.
       8                 :  *           To be used by external programs to handle reading/writing of
       9                 :  *           features from/to TAB datasets.
      10                 :  * Author:   Daniel Morissette, dmorissette@dmsolutions.ca
      11                 :  *
      12                 :  **********************************************************************
      13                 :  * Copyright (c) 1999-2003, 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_tabfile.cpp,v $
      35                 :  * Revision 1.78  2010-10-08 18:40:12  aboudreault
      36                 :  * Fixed missing initializations that cause crashes
      37                 :  *
      38                 :  * Revision 1.77  2010-10-08 18:38:13  aboudreault
      39                 :  * Added attribute index support for the sql queries in mapinfo tab format (GDAL bug #3687)
      40                 :  *
      41                 :  * Revision 1.76  2010-07-07 19:00:15  aboudreault
      42                 :  * Cleanup Win32 Compile Warnings (GDAL bug #2930)
      43                 :  *
      44                 :  * Revision 1.75  2010-07-05 14:58:33  aboudreault
      45                 :  * Fixed bad feature count after we deleted a feature in MapInfo (bug 2227)
      46                 :  *
      47                 :  * Revision 1.74  2010-01-07 20:39:12  aboudreault
      48                 :  * Added support to handle duplicate field names, Added validation to check if a field name start with a number (bug 2141)
      49                 :  *
      50                 :  * Revision 1.73  2008-11-27 20:50:23  aboudreault
      51                 :  * Improved support for OGR date/time types. New Read/Write methods (bug 1948)
      52                 :  * Added support of OGR date/time types for MIF features.
      53                 :  *
      54                 :  * Revision 1.72  2008/11/17 22:06:21  aboudreault
      55                 :  * Added support to use OFTDateTime/OFTDate/OFTTime type when compiled with
      56                 :  * OGR and fixed reading/writing support for these types.
      57                 :  *
      58                 :  * Revision 1.71  2008/09/26 14:40:24  aboudreault
      59                 :  * Fixed bug: MITAB doesn't support writing DateTime type (bug 1948)
      60                 :  *
      61                 :  * Revision 1.70  2008/06/13 18:39:21  aboudreault
      62                 :  * Fixed problem with corrupt pointer if file not found (bug 1899) and
      63                 :  * fixed tabdump build problem if DEBUG option not provided (bug 1898)
      64                 :  *
      65                 :  * Revision 1.69  2008/03/05 20:59:10  dmorissette
      66                 :  * Purged CVS logs in header
      67                 :  *
      68                 :  * Revision 1.68  2008/03/05 20:35:39  dmorissette
      69                 :  * Replace MITAB 1.x SetFeature() with a CreateFeature() for V2.x (bug 1859)
      70                 :  *
      71                 :  * Revision 1.67  2008/01/29 21:56:39  dmorissette
      72                 :  * Update dataset version properly for Date/Time/DateTime field types (#1754)
      73                 :  *
      74                 :  * Revision 1.66  2008/01/29 20:46:32  dmorissette
      75                 :  * Added support for v9 Time and DateTime fields (byg 1754)
      76                 :  *
      77                 :  * Revision 1.65  2007/09/12 20:22:31  dmorissette
      78                 :  * Added TABFeature::CreateFromMapInfoType()
      79                 :  *
      80                 :  * Revision 1.64  2007/06/21 14:00:23  dmorissette
      81                 :  * Added missing cast in isspace() calls to avoid failed assertion on Windows
      82                 :  * (MITAB bug 1737, GDAL ticket 1678))
      83                 :  *
      84                 :  * Revision 1.63  2007/06/12 13:52:38  dmorissette
      85                 :  * Added IMapInfoFile::SetCharset() method (bug 1734)
      86                 :  *
      87                 :  * Revision 1.62  2007/06/12 12:50:40  dmorissette
      88                 :  * Use Quick Spatial Index by default until bug 1732 is fixed (broken files
      89                 :  * produced by current coord block splitting technique).
      90                 :  *
      91                 :  * Revision 1.61  2007/03/21 21:15:56  dmorissette
      92                 :  * Added SetQuickSpatialIndexMode() which generates a non-optimal spatial
      93                 :  * index but results in faster write time (bug 1669)
      94                 :  *
      95                 :  * ...
      96                 :  *
      97                 :  * Revision 1.1  1999/07/12 04:18:25  daniel
      98                 :  * Initial checkin
      99                 :  *
     100                 :  **********************************************************************/
     101                 : 
     102                 : #include "mitab.h"
     103                 : #include "mitab_utils.h"
     104                 : #include "cpl_minixml.h"
     105                 : 
     106                 : #include <ctype.h>      /* isspace() */
     107                 : 
     108                 : /*=====================================================================
     109                 :  *                      class TABFile
     110                 :  *====================================================================*/
     111                 : 
     112                 : 
     113                 : /**********************************************************************
     114                 :  *                   TABFile::TABFile()
     115                 :  *
     116                 :  * Constructor.
     117                 :  **********************************************************************/
     118               4 : TABFile::TABFile()
     119                 : {
     120               4 :     m_eAccessMode = TABRead;
     121               4 :     m_pszFname = NULL;
     122               4 :     m_papszTABFile = NULL;
     123               4 :     m_nVersion = 300;
     124               4 :     m_eTableType = TABTableNative;
     125                 : 
     126               4 :     m_poMAPFile = NULL;
     127               4 :     m_poDATFile = NULL;
     128               4 :     m_poINDFile = NULL;
     129               4 :     m_poDefn = NULL;
     130               4 :     m_poSpatialRef = NULL;
     131               4 :     m_poCurFeature = NULL;
     132               4 :     m_nCurFeatureId = 0;
     133               4 :     m_nLastFeatureId = 0;
     134               4 :     m_panIndexNo = NULL;
     135                 : 
     136               4 :     bUseSpatialTraversal = FALSE;
     137                 : 
     138               4 :     m_panMatchingFIDs = NULL; 
     139               4 :     m_iMatchingFID = 0; 
     140               4 : }
     141                 : 
     142                 : /**********************************************************************
     143                 :  *                   TABFile::~TABFile()
     144                 :  *
     145                 :  * Destructor.
     146                 :  **********************************************************************/
     147               4 : TABFile::~TABFile()
     148                 : {
     149               4 :     Close();
     150               4 : }
     151                 : 
     152                 : 
     153                 : /************************************************************************/
     154                 : /*                          GetFeatureCount()                           */
     155                 : /************************************************************************/
     156               0 : int TABFile::GetFeatureCount (int bForce)
     157                 : {
     158                 :     
     159               0 :     if( m_poFilterGeom != NULL || m_poAttrQuery != NULL || bForce)
     160               0 :         return OGRLayer::GetFeatureCount( bForce );
     161                 :     else
     162               0 :         return m_nLastFeatureId;
     163                 : }
     164                 : 
     165                 : /************************************************************************/
     166                 : /*                            ResetReading()                            */
     167                 : /************************************************************************/
     168              10 : void TABFile::ResetReading()
     169                 : {
     170              10 :     CPLFree(m_panMatchingFIDs);
     171              10 :     m_panMatchingFIDs = NULL;
     172              10 :     m_iMatchingFID = 0;
     173                 :     
     174              10 :     m_nCurFeatureId = 0;
     175              10 :     if( m_poMAPFile != NULL )
     176              10 :         m_poMAPFile->ResetReading();
     177                 : 
     178                 : /* -------------------------------------------------------------------- */
     179                 : /*      Decide whether to operate in spatial traversal mode or not,     */
     180                 : /*      and ensure the current spatial filter is applied to the map     */
     181                 : /*      file object.                                                    */
     182                 : /* -------------------------------------------------------------------- */
     183              10 :     if( m_poMAPFile )
     184                 :     {
     185              10 :         bUseSpatialTraversal = FALSE;
     186                 :     
     187              10 :         m_poMAPFile->ResetCoordFilter();
     188                 : 
     189              10 :         if( m_poFilterGeom != NULL )
     190                 :         {
     191               1 :             OGREnvelope  sEnvelope;
     192                 :             TABVertex sMin, sMax;
     193                 :             TABMAPHeaderBlock *poHeader;
     194                 :     
     195               1 :             poHeader = m_poMAPFile->GetHeaderBlock();
     196                 : 
     197               1 :             m_poFilterGeom->getEnvelope( &sEnvelope );
     198               1 :             m_poMAPFile->GetCoordFilter( sMin, sMax );
     199                 : 
     200               1 :             if( sEnvelope.MinX > sMin.x 
     201                 :                 || sEnvelope.MinY > sMin.y
     202                 :                 || sEnvelope.MaxX < sMax.x
     203                 :                 || sEnvelope.MaxY < sMax.y )
     204                 :             {
     205               1 :                 bUseSpatialTraversal = TRUE;
     206               1 :                 sMin.x = sEnvelope.MinX;
     207               1 :                 sMin.y = sEnvelope.MinY;
     208               1 :                 sMax.x = sEnvelope.MaxX;
     209               1 :                 sMax.y = sEnvelope.MaxY;
     210               1 :                 m_poMAPFile->SetCoordFilter( sMin, sMax );
     211                 :             }
     212                 :         }
     213                 :     }
     214              10 : }
     215                 : 
     216                 : /**********************************************************************
     217                 :  *                   TABFile::Open()
     218                 :  *
     219                 :  * Open a .TAB dataset and the associated files, and initialize the 
     220                 :  * structures to be ready to read features from (or write to) it.
     221                 :  *
     222                 :  * Supported access modes are "r" (read-only) and "w" (create new dataset).
     223                 :  *
     224                 :  * Set bTestOpenNoError=TRUE to silently return -1 with no error message
     225                 :  * if the file cannot be opened.  This is intended to be used in the
     226                 :  * context of a TestOpen() function.  The default value is FALSE which
     227                 :  * means that an error is reported if the file cannot be opened.
     228                 :  *
     229                 :  * Note that dataset extents will have to be set using SetBounds() before
     230                 :  * any feature can be written to a newly created dataset.
     231                 :  *
     232                 :  * In read mode, a valid dataset must have at least a .TAB and a .DAT file.
     233                 :  * The .MAP and .ID files are optional and if they do not exist then
     234                 :  * all features will be returned with NONE geometry.
     235                 :  *
     236                 :  * Returns 0 on success, -1 on error.
     237                 :  **********************************************************************/
     238               4 : int TABFile::Open(const char *pszFname, const char *pszAccess,
     239                 :                   GBool bTestOpenNoError /*=FALSE*/ )
     240                 : {
     241               4 :     char *pszTmpFname = NULL;
     242               4 :     int nFnameLen = 0;
     243                 : 
     244               4 :     CPLErrorReset();
     245                 :    
     246               4 :     if (m_poMAPFile)
     247                 :     {
     248                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
     249               0 :                  "Open() failed: object already contains an open file");
     250                 : 
     251               0 :         return -1;
     252                 :     }
     253                 : 
     254                 :     /*-----------------------------------------------------------------
     255                 :      * Validate access mode
     256                 :      *----------------------------------------------------------------*/
     257               4 :     if (EQUALN(pszAccess, "r", 1))
     258                 :     {
     259               2 :         m_eAccessMode = TABRead;
     260               2 :         pszAccess = "rb";
     261                 :     }
     262               2 :     else if (EQUALN(pszAccess, "w", 1))
     263                 :     {
     264               2 :         m_eAccessMode = TABWrite;
     265               2 :         pszAccess = "wb";
     266                 :     }
     267                 :     else
     268                 :     {
     269               0 :         if (!bTestOpenNoError)
     270                 :             CPLError(CE_Failure, CPLE_FileIO,
     271               0 :                  "Open() failed: access mode \"%s\" not supported", pszAccess);
     272                 :         else
     273               0 :             CPLErrorReset();
     274                 : 
     275               0 :         return -1;
     276                 :     }
     277                 : 
     278                 :     /*-----------------------------------------------------------------
     279                 :      * Make sure filename has a .TAB extension... 
     280                 :      *----------------------------------------------------------------*/
     281               4 :     m_pszFname = CPLStrdup(pszFname);
     282               4 :     nFnameLen = strlen(m_pszFname);
     283                 : 
     284               4 :     if (nFnameLen > 4 && (strcmp(m_pszFname+nFnameLen-4, ".TAB")==0 ||
     285                 :                      strcmp(m_pszFname+nFnameLen-4, ".MAP")==0 ||
     286                 :                      strcmp(m_pszFname+nFnameLen-4, ".DAT")==0 ) )
     287               0 :         strcpy(m_pszFname+nFnameLen-4, ".TAB");
     288               8 :     else if (nFnameLen > 4 && (EQUAL(m_pszFname+nFnameLen-4, ".tab") ||
     289                 :                                EQUAL(m_pszFname+nFnameLen-4, ".map") ||
     290                 :                                EQUAL(m_pszFname+nFnameLen-4, ".dat") ) )
     291               4 :         strcpy(m_pszFname+nFnameLen-4, ".tab");
     292                 :     else
     293                 :     {
     294               0 :         if (!bTestOpenNoError)
     295                 :             CPLError(CE_Failure, CPLE_FileIO,
     296                 :                      "Open() failed for %s: invalid filename extension",
     297               0 :                      m_pszFname);
     298                 :         else
     299               0 :             CPLErrorReset();
     300                 : 
     301               0 :         CPLFree(m_pszFname);
     302               0 :         m_pszFname = NULL;
     303               0 :         return -1;
     304                 :     }
     305                 : 
     306               4 :     pszTmpFname = CPLStrdup(m_pszFname);
     307                 : 
     308                 : 
     309                 : #ifndef _WIN32
     310                 :     /*-----------------------------------------------------------------
     311                 :      * On Unix, make sure extension uses the right cases
     312                 :      * We do it even for write access because if a file with the same
     313                 :      * extension already exists we want to overwrite it.
     314                 :      *----------------------------------------------------------------*/
     315               4 :     TABAdjustFilenameExtension(m_pszFname);
     316                 : #endif
     317                 : 
     318                 :     /*-----------------------------------------------------------------
     319                 :      * Handle .TAB file... depends on access mode.
     320                 :      *----------------------------------------------------------------*/
     321               4 :     if (m_eAccessMode == TABRead)
     322                 :     {
     323                 :         /*-------------------------------------------------------------
     324                 :          * Open .TAB file... since it's a small text file, we will just load
     325                 :          * it as a stringlist in memory.
     326                 :          *------------------------------------------------------------*/
     327               2 :         m_papszTABFile = TAB_CSLLoad(m_pszFname);
     328               2 :         if (m_papszTABFile == NULL)
     329                 :         {
     330               0 :             if (!bTestOpenNoError)
     331                 :             {
     332                 :                 CPLError(CE_Failure, CPLE_FileIO,
     333               0 :                          "Failed opening %s.", m_pszFname);
     334                 :             }
     335               0 :             CPLFree(m_pszFname);
     336               0 :             m_pszFname = NULL;
     337               0 :             CSLDestroy(m_papszTABFile);
     338               0 :             m_papszTABFile = NULL;
     339               0 :             CPLFree( pszTmpFname );
     340               0 :             return -1;
     341                 :         }
     342                 : 
     343                 :         /*-------------------------------------------------------------
     344                 :          * Do a first pass on the TAB header to establish the type of 
     345                 :          * dataset we have (NATIVE, DBF, etc.)... and also to know if
     346                 :          * it is a supported type.
     347                 :          *------------------------------------------------------------*/
     348               2 :         if ( ParseTABFileFirstPass(bTestOpenNoError) != 0 )
     349                 :         {
     350                 :             // No need to produce an error... it's already been done if 
     351                 :             // necessary... just cleanup and exit.
     352                 : 
     353               0 :             CPLFree(m_pszFname);
     354               0 :             m_pszFname = NULL;
     355               0 :             CSLDestroy(m_papszTABFile);
     356               0 :             m_papszTABFile = NULL;
     357               0 :             CPLFree( pszTmpFname );
     358                 : 
     359               0 :             return -1;
     360                 :         }
     361                 :     }
     362                 :     else
     363                 :     {
     364                 :         /*-------------------------------------------------------------
     365                 :          * In Write access mode, the .TAB file will be written during the 
     366                 :          * Close() call... we will just set some defaults here.
     367                 :          *------------------------------------------------------------*/
     368               2 :         m_nVersion = 300;
     369               2 :         CPLFree(m_pszCharset);
     370               2 :         m_pszCharset = CPLStrdup("Neutral");
     371               2 :         m_eTableType = TABTableNative;
     372                 : 
     373                 :         /*-------------------------------------------------------------
     374                 :          * Do initial setup of feature definition.
     375                 :          *------------------------------------------------------------*/
     376               2 :         char *pszFeatureClassName = TABGetBasename(m_pszFname);
     377               2 :         m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
     378               2 :         m_poDefn->Reference();
     379               2 :         CPLFree(pszFeatureClassName);
     380                 :     }
     381                 : 
     382                 : 
     383                 :     /*-----------------------------------------------------------------
     384                 :      * Open .DAT file (or .DBF)
     385                 :      *----------------------------------------------------------------*/
     386               4 :     if (nFnameLen > 4 && strcmp(pszTmpFname+nFnameLen-4, ".TAB")==0)
     387                 :     {
     388               0 :         if (m_eTableType == TABTableDBF)
     389               0 :             strcpy(pszTmpFname+nFnameLen-4, ".DBF");
     390                 :         else  // Default is NATIVE
     391               0 :             strcpy(pszTmpFname+nFnameLen-4, ".DAT");
     392                 :     }
     393                 :     else 
     394                 :     {
     395               4 :         if (m_eTableType == TABTableDBF)
     396               0 :             strcpy(pszTmpFname+nFnameLen-4, ".dbf");
     397                 :         else  // Default is NATIVE
     398               4 :             strcpy(pszTmpFname+nFnameLen-4, ".dat");
     399                 :     }
     400                 : 
     401                 : #ifndef _WIN32
     402               4 :     TABAdjustFilenameExtension(pszTmpFname);
     403                 : #endif
     404                 : 
     405               4 :     m_poDATFile = new TABDATFile;
     406                 :    
     407               4 :     if ( m_poDATFile->Open(pszTmpFname, pszAccess, m_eTableType) != 0)
     408                 :     {
     409                 :         // Open Failed... an error has already been reported, just return.
     410               0 :         CPLFree(pszTmpFname);
     411               0 :         Close();
     412               0 :         if (bTestOpenNoError)
     413               0 :             CPLErrorReset();
     414                 : 
     415               0 :         return -1;
     416                 :     }
     417                 : 
     418               4 :     m_nLastFeatureId = m_poDATFile->GetNumRecords();
     419                 : 
     420                 : 
     421                 :     /*-----------------------------------------------------------------
     422                 :      * Parse .TAB file field defs and build FeatureDefn (only in read access)
     423                 :      *----------------------------------------------------------------*/
     424               4 :     if (m_eAccessMode == TABRead && ParseTABFileFields() != 0)
     425                 :     {
     426                 :         // Failed... an error has already been reported, just return.
     427               0 :         CPLFree(pszTmpFname);
     428               0 :         Close();
     429               0 :         if (bTestOpenNoError)
     430               0 :             CPLErrorReset();
     431                 : 
     432               0 :         return -1;
     433                 :     }
     434                 : 
     435                 : 
     436                 :     /*-----------------------------------------------------------------
     437                 :      * Open .MAP (and .ID) file
     438                 :      * Note that the .MAP and .ID files are optional.  Failure to open them
     439                 :      * is not an error... it simply means that all features will be returned
     440                 :      * with NONE geometry.
     441                 :      *----------------------------------------------------------------*/
     442               4 :     if (nFnameLen > 4 && strcmp(pszTmpFname+nFnameLen-4, ".DAT")==0)
     443               0 :         strcpy(pszTmpFname+nFnameLen-4, ".MAP");
     444                 :     else 
     445               4 :         strcpy(pszTmpFname+nFnameLen-4, ".map");
     446                 : 
     447                 : #ifndef _WIN32
     448               4 :     TABAdjustFilenameExtension(pszTmpFname);
     449                 : #endif
     450                 : 
     451               4 :     m_poMAPFile = new TABMAPFile;
     452               4 :     if (m_eAccessMode == TABRead)
     453                 :     {
     454                 :         /*-------------------------------------------------------------
     455                 :          * Read access: .MAP/.ID are optional... try to open but return
     456                 :          * no error if files do not exist.
     457                 :          *------------------------------------------------------------*/
     458               2 :         if (m_poMAPFile->Open(pszTmpFname, pszAccess, TRUE) < 0)
     459                 :         {
     460                 :             // File exists, but Open Failed... 
     461                 :             // we have to produce an error message
     462               0 :             if (!bTestOpenNoError)
     463                 :                 CPLError(CE_Failure, CPLE_FileIO, 
     464               0 :                          "Open() failed for %s", pszTmpFname);
     465                 :             else
     466               0 :                 CPLErrorReset();
     467                 : 
     468               0 :             CPLFree(pszTmpFname);
     469               0 :             Close();
     470               0 :             return -1;
     471                 :         }
     472                 : 
     473                 :         /*-------------------------------------------------------------
     474                 :          * Set geometry type if the geometry objects are uniform.
     475                 :          *------------------------------------------------------------*/
     476               2 :         int numPoints=0, numRegions=0, numTexts=0, numLines=0;
     477                 : 
     478               2 :         GetFeatureCountByType( numPoints, numLines, numRegions, numTexts);
     479                 : 
     480               2 :         numPoints += numTexts;
     481               2 :         if( numPoints > 0 && numLines == 0 && numRegions == 0 )
     482               0 :             m_poDefn->SetGeomType( wkbPoint );
     483               2 :         else if( numPoints == 0 && numLines > 0 && numRegions == 0 )
     484               0 :             m_poDefn->SetGeomType( wkbLineString );
     485                 :         else
     486                 :             /* we leave it unknown indicating a mixture */;
     487                 :     }
     488               2 :     else if (m_poMAPFile->Open(pszTmpFname, pszAccess) != 0)
     489                 :     {
     490                 :         // Open Failed for write... 
     491                 :         // an error has already been reported, just return.
     492               0 :         CPLFree(pszTmpFname);
     493               0 :         Close();
     494               0 :         if (bTestOpenNoError)
     495               0 :             CPLErrorReset();
     496                 : 
     497               0 :         return -1;
     498                 :     }
     499                 : 
     500                 :     /*-----------------------------------------------------------------
     501                 :      * Initializing the attribute index (.IND) support
     502                 :      *----------------------------------------------------------------*/
     503                 : 
     504               4 :     CPLXMLNode *psRoot = CPLCreateXMLNode( NULL, CXT_Element, "OGRMILayerAttrIndex" );
     505               4 :     CPLCreateXMLElementAndValue( psRoot, "MIIDFilename", CPLResetExtension( pszFname, "IND" ) );
     506               4 :     OGRFeatureDefn *poLayerDefn = GetLayerDefn();
     507               4 :     int iField, iIndexIndex, bHasIndex = 0;
     508               8 :     for( iField = 0; iField < poLayerDefn->GetFieldCount(); iField++ )
     509                 :     {
     510               4 :         iIndexIndex = GetFieldIndexNumber(iField);
     511               4 :         if (iIndexIndex > 0)
     512                 :         {
     513               0 :             CPLXMLNode *psIndex = CPLCreateXMLNode( psRoot, CXT_Element, "OGRMIAttrIndex" );
     514               0 :             CPLCreateXMLElementAndValue( psIndex, "FieldIndex", CPLSPrintf( "%d", iField ) );
     515                 :             CPLCreateXMLElementAndValue( psIndex, "FieldName", 
     516               0 :                                      poLayerDefn->GetFieldDefn(iField)->GetNameRef() );
     517               0 :             CPLCreateXMLElementAndValue( psIndex, "IndexIndex", CPLSPrintf( "%d", iIndexIndex ) );
     518               0 :             bHasIndex = 1;
     519                 :         }     
     520                 :     }
     521                 : 
     522               4 :     if (bHasIndex)
     523                 :     {
     524               0 :         char *pszRawXML = CPLSerializeXMLTree( psRoot );
     525               0 :         InitializeIndexSupport( pszRawXML );
     526               0 :         CPLFree( pszRawXML );
     527                 :     }
     528                 : 
     529               4 :     CPLDestroyXMLNode( psRoot );
     530                 : 
     531               4 :     CPLFree(pszTmpFname);
     532               4 :     pszTmpFname = NULL;
     533                 : 
     534                 :     /*-----------------------------------------------------------------
     535                 :      * __TODO__ we could probably call GetSpatialRef() here to force
     536                 :      * parsing the projection information... this would allow us to 
     537                 :      * assignSpatialReference() on the geometries that we return.
     538                 :      *----------------------------------------------------------------*/
     539                 : 
     540               4 :     return 0;
     541                 : }
     542                 : 
     543                 : 
     544                 : /**********************************************************************
     545                 :  *                   TABFile::ParseTABFileFirstPass()
     546                 :  *
     547                 :  * Do a first pass in the TAB header file to establish the table type, etc.
     548                 :  * and store any useful information into class members.
     549                 :  *
     550                 :  * This private method should be used only during the Open() call.
     551                 :  *
     552                 :  * Returns 0 on success, -1 on error.
     553                 :  **********************************************************************/
     554               2 : int TABFile::ParseTABFileFirstPass(GBool bTestOpenNoError)
     555                 : {
     556               2 :     int         iLine, numLines, numFields = 0;
     557               2 :     char        **papszTok=NULL;
     558               2 :     GBool       bInsideTableDef = FALSE, bFoundTableFields=FALSE;
     559                 : 
     560               2 :     if (m_eAccessMode != TABRead)
     561                 :     {
     562                 :         CPLError(CE_Failure, CPLE_NotSupported,
     563               0 :                  "ParseTABFile() can be used only with Read access.");
     564               0 :         return -1;
     565                 :     }
     566                 : 
     567               2 :     numLines = CSLCount(m_papszTABFile);
     568                 : 
     569              20 :     for(iLine=0; iLine<numLines; iLine++)
     570                 :     {
     571                 :         /*-------------------------------------------------------------
     572                 :          * Tokenize the next .TAB line, and check first keyword
     573                 :          *------------------------------------------------------------*/
     574              18 :         CSLDestroy(papszTok);
     575              18 :         papszTok = CSLTokenizeStringComplex(m_papszTABFile[iLine], " \t(),;",
     576              36 :                                             TRUE, FALSE);
     577              18 :         if (CSLCount(papszTok) < 2)
     578               4 :             continue;   // All interesting lines have at least 2 tokens
     579                 : 
     580              14 :         if (EQUAL(papszTok[0], "!version"))
     581                 :         {
     582               2 :             m_nVersion = atoi(papszTok[1]);
     583               2 :             if (m_nVersion == 100)
     584                 :             {
     585                 :                 /* Version 100 files contain only the fields definition,
     586                 :                  * so we set default values for the other params.
     587                 :                  */
     588               0 :                 bInsideTableDef = TRUE;
     589               0 :                 CPLFree(m_pszCharset);
     590               0 :                 m_pszCharset = CPLStrdup("Neutral");
     591               0 :                 m_eTableType = TABTableNative;
     592                 :             }
     593                 : 
     594                 :         }
     595              12 :         else if (EQUAL(papszTok[0], "!edit_version"))
     596                 :         {
     597                 :             /* Sometimes, V450 files have version 300 + edit_version 450
     598                 :              * for us version and edit_version are the same 
     599                 :              */
     600               0 :             m_nVersion = atoi(papszTok[1]);
     601                 :         }
     602              12 :         else if (EQUAL(papszTok[0], "!charset"))
     603                 :         {
     604               2 :             CPLFree(m_pszCharset);
     605               2 :             m_pszCharset = CPLStrdup(papszTok[1]);
     606                 :         }
     607              14 :         else if (EQUAL(papszTok[0], "Definition") &&
     608               2 :                  EQUAL(papszTok[1], "Table") )
     609                 :         {
     610               2 :             bInsideTableDef = TRUE;
     611                 :         }
     612              16 :         else if (bInsideTableDef && !bFoundTableFields &&
     613               6 :                  (EQUAL(papszTok[0], "Type") || EQUAL(papszTok[0],"FORMAT:")) )
     614                 :         {
     615               4 :             if (EQUAL(papszTok[1], "NATIVE") || EQUAL(papszTok[1], "LINKED"))
     616               2 :                 m_eTableType = TABTableNative;
     617               0 :             else if (EQUAL(papszTok[1], "DBF"))
     618               0 :                 m_eTableType = TABTableDBF;
     619                 :             else
     620                 :             {
     621                 :                 // Type=ACCESS, or other unsupported type... cannot open!
     622               0 :                 if (!bTestOpenNoError)
     623                 :                     CPLError(CE_Failure, CPLE_NotSupported,
     624                 :                              "Unsupported table type '%s' in file %s.  "
     625                 :                       "This type of .TAB file cannot be read by this library.",
     626               0 :                              papszTok[1], m_pszFname);
     627               0 :                 CSLDestroy(papszTok);
     628               0 :                 return -1;
     629                 :             }
     630                 :         }
     631               8 :         else if (bInsideTableDef && !bFoundTableFields &&
     632               2 :                  (EQUAL(papszTok[0],"Fields") || EQUAL(papszTok[0],"FIELDS:")))
     633                 :         {
     634                 :             /*---------------------------------------------------------
     635                 :              * We found the list of table fields
     636                 :              * Just remember number of fields... the field types will be
     637                 :              * parsed inside ParseTABFileFields() later...
     638                 :              *--------------------------------------------------------*/
     639               2 :             bFoundTableFields = TRUE;
     640               2 :             numFields = atoi(papszTok[1]);
     641                 : 
     642               2 :             if (numFields < 1 || numFields>2048 || iLine+numFields >= numLines)
     643                 :             {
     644               0 :                 if (!bTestOpenNoError)
     645                 :                     CPLError(CE_Failure, CPLE_FileIO,
     646                 :                          "Invalid number of fields (%s) at line %d in file %s",
     647               0 :                              papszTok[1], iLine+1, m_pszFname);
     648                 : 
     649               0 :                 CSLDestroy(papszTok);
     650               0 :                 return -1;
     651                 :             }
     652                 : 
     653               2 :             bInsideTableDef = FALSE;
     654                 :         }/* end of fields section*/
     655                 :         else
     656                 :         {
     657                 :             // Simply Ignore unrecognized lines
     658                 :         }
     659                 :     }
     660                 : 
     661               2 :     CSLDestroy(papszTok);
     662                 : 
     663               2 :     if (m_pszCharset == NULL)
     664               0 :         m_pszCharset = CPLStrdup("Neutral");
     665                 : 
     666               2 :     if (numFields == 0)
     667                 :     {
     668               0 :         if (!bTestOpenNoError)
     669                 :             CPLError(CE_Failure, CPLE_NotSupported,
     670                 :                      "%s contains no table field definition.  "
     671                 :                      "This type of .TAB file cannot be read by this library.",
     672               0 :                      m_pszFname);
     673               0 :         return -1;
     674                 :     }
     675                 : 
     676               2 :     return 0;
     677                 : }
     678                 : 
     679                 : /**********************************************************************
     680                 :  *                   TABFile::ParseTABFileFields()
     681                 :  *
     682                 :  * Extract the field definition from the TAB header file, validate
     683                 :  * with what we have in the previously opened .DAT or .DBF file, and 
     684                 :  * finally build the m_poDefn OGRFeatureDefn for this dataset.
     685                 :  *
     686                 :  * This private method should be used only during the Open() call and after
     687                 :  * ParseTABFileFirstPass() has been called.
     688                 :  *
     689                 :  * Returns 0 on success, -1 on error.
     690                 :  **********************************************************************/
     691               2 : int TABFile::ParseTABFileFields()
     692                 : {
     693               2 :     int         iLine, numLines=0, numTok, nStatus;
     694               2 :     char        **papszTok=NULL;
     695                 :     OGRFieldDefn *poFieldDefn;
     696                 : 
     697               2 :     if (m_eAccessMode != TABRead)
     698                 :     {
     699                 :         CPLError(CE_Failure, CPLE_NotSupported,
     700               0 :                  "ParseTABFile() can be used only with Read access.");
     701               0 :         return -1;
     702                 :     }
     703                 : 
     704               2 :     char *pszFeatureClassName = TABGetBasename(m_pszFname);
     705               2 :     m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
     706               2 :     CPLFree(pszFeatureClassName);
     707                 :     // Ref count defaults to 0... set it to 1
     708               2 :     m_poDefn->Reference();
     709                 : 
     710                 :     /*-------------------------------------------------------------
     711                 :      * Scan for fields.
     712                 :      *------------------------------------------------------------*/
     713               2 :     numLines = CSLCount(m_papszTABFile);
     714              14 :     for(iLine=0; iLine<numLines; iLine++)
     715                 :     {
     716                 :         /*-------------------------------------------------------------
     717                 :          * Tokenize the next .TAB line, and check first keyword
     718                 :          *------------------------------------------------------------*/
     719              14 :         const char *pszStr = m_papszTABFile[iLine];
     720              36 :         while(*pszStr != '\0' && isspace((unsigned char)*pszStr))
     721               8 :             pszStr++;
     722                 : 
     723              14 :         if (EQUALN(pszStr, "Fields", 6))
     724                 :         {
     725                 :             /*---------------------------------------------------------
     726                 :              * We found the list of table fields
     727                 :              *--------------------------------------------------------*/
     728                 :             int iField, numFields;
     729               2 :             numFields = atoi(pszStr+7);
     730               2 :             if (numFields < 1 || numFields > 2048 || 
     731                 :                 iLine+numFields >= numLines)
     732                 :             {
     733                 :                 CPLError(CE_Failure, CPLE_FileIO,
     734                 :                          "Invalid number of fields (%s) at line %d in file %s",
     735               0 :                          pszStr+7, iLine+1, m_pszFname);
     736               0 :                 CSLDestroy(papszTok);
     737               0 :                 return -1;
     738                 :             }
     739                 : 
     740                 :             // Alloc the array to keep track of indexed fields
     741               2 :             m_panIndexNo = (int *)CPLCalloc(numFields, sizeof(int));
     742                 : 
     743               2 :             iLine++;
     744               2 :             poFieldDefn = NULL;
     745               6 :             for(iField=0; iField<numFields; iField++, iLine++)
     746                 :             {
     747                 :                 /*-----------------------------------------------------
     748                 :                  * For each field definition found in the .TAB:
     749                 :                  * Pass the info to the DAT file object.  It will validate
     750                 :                  * the info with what is found in the .DAT header, and will
     751                 :                  * also use this info later to interpret field values.
     752                 :                  *
     753                 :                  * We also create the OGRFieldDefn at the same time to 
     754                 :                  * initialize the OGRFeatureDefn
     755                 :                  *----------------------------------------------------*/
     756               4 :                 CSLDestroy(papszTok);
     757               4 :                 papszTok = CSLTokenizeStringComplex(m_papszTABFile[iLine], 
     758                 :                                                     " \t(),;",
     759               8 :                                                     TRUE, FALSE);
     760               4 :                 numTok = CSLCount(papszTok);
     761               4 :                 nStatus = -1;
     762               4 :                 CPLAssert(m_poDefn);
     763               6 :                 if (numTok >= 3 && EQUAL(papszTok[1], "char"))
     764                 :                 {
     765                 :                     /*-------------------------------------------------
     766                 :                      * CHAR type
     767                 :                      *------------------------------------------------*/
     768                 :                     nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField, 
     769               2 :                                                                papszTok[0],
     770                 :                                                                TABFChar,
     771               2 :                                                             atoi(papszTok[2]),
     772               6 :                                                                0);
     773               2 :                     poFieldDefn = new OGRFieldDefn(papszTok[0], OFTString);
     774               2 :                     poFieldDefn->SetWidth(atoi(papszTok[2]));
     775                 :                 }
     776               3 :                 else if (numTok >= 2 && EQUAL(papszTok[1], "integer"))
     777                 :                 {
     778                 :                     /*-------------------------------------------------
     779                 :                      * INTEGER type
     780                 :                      *------------------------------------------------*/
     781                 :                     nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField, 
     782               1 :                                                                papszTok[0],
     783                 :                                                                TABFInteger,
     784                 :                                                                0,
     785               1 :                                                                0);
     786               1 :                     poFieldDefn = new OGRFieldDefn(papszTok[0], OFTInteger);
     787                 :                 }
     788               1 :                 else if (numTok >= 2 && EQUAL(papszTok[1], "smallint"))
     789                 :                 {
     790                 :                     /*-------------------------------------------------
     791                 :                      * SMALLINT type
     792                 :                      *------------------------------------------------*/
     793                 :                     nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField, 
     794               0 :                                                                papszTok[0],
     795                 :                                                                TABFSmallInt,
     796                 :                                                                0,
     797               0 :                                                                0);
     798               0 :                     poFieldDefn = new OGRFieldDefn(papszTok[0], OFTInteger);
     799                 :                 }
     800               1 :                 else if (numTok >= 4 && EQUAL(papszTok[1], "decimal"))
     801                 :                 {
     802                 :                     /*-------------------------------------------------
     803                 :                      * DECIMAL type
     804                 :                      *------------------------------------------------*/
     805                 :                     nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField, 
     806               0 :                                                                papszTok[0],
     807                 :                                                                TABFDecimal,
     808               0 :                                                            atoi(papszTok[2]),
     809               0 :                                                            atoi(papszTok[3]));
     810               0 :                     poFieldDefn = new OGRFieldDefn(papszTok[0], OFTReal);
     811               0 :                     poFieldDefn->SetWidth(atoi(papszTok[2]));
     812               0 :                     poFieldDefn->SetPrecision(atoi(papszTok[3]));
     813                 :                 }
     814               2 :                 else if (numTok >= 2 && EQUAL(papszTok[1], "float"))
     815                 :                 {
     816                 :                     /*-------------------------------------------------
     817                 :                      * FLOAT type
     818                 :                      *------------------------------------------------*/
     819                 :                     nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField, 
     820               1 :                                                                papszTok[0],
     821                 :                                                                TABFFloat,
     822               1 :                                                                0, 0);
     823               1 :                     poFieldDefn = new OGRFieldDefn(papszTok[0], OFTReal);
     824                 :                 }
     825               0 :                 else if (numTok >= 2 && EQUAL(papszTok[1], "date"))
     826                 :                 {
     827                 :                     /*-------------------------------------------------
     828                 :                      * DATE type (returned as a string: "DD/MM/YYYY")
     829                 :                      *------------------------------------------------*/
     830                 :                     nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField, 
     831               0 :                                                                papszTok[0],
     832                 :                                                                TABFDate,
     833                 :                                                                0,
     834               0 :                                                                0);
     835               0 :                     poFieldDefn = new OGRFieldDefn(papszTok[0], 
     836                 : #ifdef MITAB_USE_OFTDATETIME
     837               0 :                                                    OFTDate);
     838                 : #else
     839                 :                                                    OFTString);
     840                 : #endif
     841               0 :                     poFieldDefn->SetWidth(10);
     842                 :                 }
     843               0 :                 else if (numTok >= 2 && EQUAL(papszTok[1], "time"))
     844                 :                 {
     845                 :                     /*-------------------------------------------------
     846                 :                      * TIME type (returned as a string: "HH:MM:SS")
     847                 :                      *------------------------------------------------*/
     848                 :                     nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField, 
     849               0 :                                                                papszTok[0],
     850                 :                                                                TABFTime,
     851                 :                                                                0,
     852               0 :                                                                0);
     853               0 :                     poFieldDefn = new OGRFieldDefn(papszTok[0], 
     854                 : #ifdef MITAB_USE_OFTDATETIME
     855               0 :                                                    OFTTime);
     856                 : #else
     857                 :                                                    OFTString);
     858                 : #endif
     859               0 :                     poFieldDefn->SetWidth(9);
     860                 :                 }
     861               0 :                 else if (numTok >= 2 && EQUAL(papszTok[1], "datetime"))
     862                 :                 {
     863                 :                     /*-------------------------------------------------
     864                 :                      * DATETIME type (returned as a string: "DD/MM/YYYY HH:MM:SS")
     865                 :                      *------------------------------------------------*/
     866                 :                     nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField, 
     867               0 :                                                                papszTok[0],
     868                 :                                                                TABFDateTime,
     869                 :                                                                0,
     870               0 :                                                                0);
     871               0 :                     poFieldDefn = new OGRFieldDefn(papszTok[0], 
     872                 : #ifdef MITAB_USE_OFTDATETIME
     873               0 :                                                    OFTDateTime);
     874                 : #else
     875                 :                                                    OFTString);
     876                 : #endif
     877               0 :                     poFieldDefn->SetWidth(19);
     878                 :                 }
     879               0 :                 else if (numTok >= 2 && EQUAL(papszTok[1], "logical"))
     880                 :                 {
     881                 :                     /*-------------------------------------------------
     882                 :                      * LOGICAL type (value "T" or "F")
     883                 :                      *------------------------------------------------*/
     884                 :                     nStatus = m_poDATFile->ValidateFieldInfoFromTAB(iField, 
     885               0 :                                                                papszTok[0],
     886                 :                                                                TABFLogical,
     887                 :                                                                0,
     888               0 :                                                                0);
     889               0 :                     poFieldDefn = new OGRFieldDefn(papszTok[0], OFTString);
     890               0 :                     poFieldDefn->SetWidth(1);
     891                 :                 }
     892                 :                 else 
     893               0 :                     nStatus = -1; // Unrecognized field type or line corrupt
     894                 : 
     895               4 :                 if (nStatus != 0)
     896                 :                 {
     897                 :                     CPLError(CE_Failure, CPLE_FileIO,
     898                 :                      "Failed to parse field definition at line %d in file %s", 
     899               0 :                              iLine+1, m_pszFname);
     900               0 :                     CSLDestroy(papszTok);
     901               0 :                     return -1;
     902                 :                 }
     903                 :                 /*-----------------------------------------------------
     904                 :                  * Keep track of index number if present
     905                 :                  *----------------------------------------------------*/
     906               4 :                 if (numTok >= 4 && EQUAL(papszTok[numTok-2], "index"))
     907                 :                 {
     908               0 :                     m_panIndexNo[iField] = atoi(papszTok[numTok-1]);
     909                 :                 }
     910                 :                 else
     911                 :                 {
     912               4 :                     m_panIndexNo[iField] = 0;
     913                 :                 }
     914                 : 
     915                 :                 /*-----------------------------------------------------
     916                 :                  * Add the FieldDefn to the FeatureDefn and continue with
     917                 :                  * the next one.
     918                 :                  *----------------------------------------------------*/
     919               4 :                 m_poDefn->AddFieldDefn(poFieldDefn);
     920                 :                 // AddFieldDenf() takes a copy, so we delete the original
     921               4 :                 if (poFieldDefn) delete poFieldDefn;
     922               4 :                 poFieldDefn = NULL;
     923                 :             }
     924                 : 
     925                 :             /*---------------------------------------------------------
     926                 :              * OK, we're done... end the loop now.
     927                 :              *--------------------------------------------------------*/
     928               2 :             break;
     929                 :         }/* end of fields section*/
     930                 :         else
     931                 :         {
     932                 :             // Simply Ignore unrecognized lines
     933                 :         }
     934                 : 
     935                 :     }
     936                 : 
     937               2 :     CSLDestroy(papszTok);
     938                 : 
     939               2 :     if (m_poDefn->GetFieldCount() == 0)
     940                 :     {
     941                 :         CPLError(CE_Failure, CPLE_NotSupported,
     942                 :                  "%s contains no table field definition.  "
     943                 :                  "This type of .TAB file cannot be read by this library.",
     944               0 :                  m_pszFname);
     945               0 :         return -1;
     946                 :     }
     947                 : 
     948               2 :     return 0;
     949                 : }
     950                 : 
     951                 : /**********************************************************************
     952                 :  *                   TABFile::WriteTABFile()
     953                 :  *
     954                 :  * Generate the .TAB file using mainly the attribute fields definition.
     955                 :  *
     956                 :  * This private method should be used only during the Close() call with
     957                 :  * write access mode.
     958                 :  *
     959                 :  * Returns 0 on success, -1 on error.
     960                 :  **********************************************************************/
     961               2 : int TABFile::WriteTABFile()
     962                 : {
     963                 :     FILE *fp;
     964                 : 
     965               2 :     if (m_eAccessMode != TABWrite)
     966                 :     {
     967                 :         CPLError(CE_Failure, CPLE_NotSupported,
     968               0 :                  "WriteTABFile() can be used only with Write access.");
     969               0 :         return -1;
     970                 :     }
     971                 : 
     972               2 :     if ( (fp = VSIFOpen(m_pszFname, "wt")) != NULL)
     973                 :     {
     974               2 :         fprintf(fp, "!table\n");
     975               2 :         fprintf(fp, "!version %d\n", m_nVersion);
     976               2 :         fprintf(fp, "!charset %s\n", m_pszCharset);
     977               2 :         fprintf(fp, "\n");
     978                 : 
     979               2 :         if (m_poDefn && m_poDefn->GetFieldCount() > 0)
     980                 :         {
     981                 :             int iField;
     982                 :             OGRFieldDefn *poFieldDefn;
     983                 :             const char *pszFieldType;
     984                 : 
     985               2 :             fprintf(fp, "Definition Table\n");
     986               2 :             fprintf(fp, "  Type NATIVE Charset \"%s\"\n", m_pszCharset);
     987               2 :             fprintf(fp, "  Fields %d\n", m_poDefn->GetFieldCount());
     988                 : 
     989               6 :             for(iField=0; iField<m_poDefn->GetFieldCount(); iField++)
     990                 :             {
     991               4 :                 poFieldDefn = m_poDefn->GetFieldDefn(iField);
     992               4 :                 switch(GetNativeFieldType(iField))
     993                 :                 {
     994                 :                   case TABFChar:
     995                 :                     pszFieldType = CPLSPrintf("Char (%d)", 
     996               2 :                                               poFieldDefn->GetWidth());
     997               2 :                     break;
     998                 :                   case TABFDecimal:
     999                 :                     pszFieldType = CPLSPrintf("Decimal (%d,%d)",
    1000                 :                                               poFieldDefn->GetWidth(),
    1001               0 :                                               poFieldDefn->GetPrecision());
    1002               0 :                     break;
    1003                 :                 case TABFInteger:
    1004               1 :                     pszFieldType = "Integer";
    1005               1 :                     break;
    1006                 :                   case TABFSmallInt:
    1007               0 :                     pszFieldType = "SmallInt";
    1008               0 :                     break;
    1009                 :                   case TABFFloat:
    1010               1 :                     pszFieldType = "Float";
    1011               1 :                     break;
    1012                 :                   case TABFLogical:
    1013               0 :                     pszFieldType = "Logical";
    1014               0 :                     break;
    1015                 :                   case TABFDate:
    1016               0 :                     pszFieldType = "Date";
    1017               0 :                     break;
    1018                 :                   case TABFTime:
    1019               0 :                     pszFieldType = "Time";
    1020               0 :                     break;
    1021                 :                   case TABFDateTime:
    1022               0 :                     pszFieldType = "DateTime";
    1023               0 :                     break;
    1024                 :                   default:
    1025                 :                     // Unsupported field type!!!  This should never happen.
    1026                 :                     CPLError(CE_Failure, CPLE_AssertionFailed,
    1027               0 :                              "WriteTABFile(): Unsupported field type");
    1028               0 :                     VSIFClose(fp);
    1029               0 :                     return -1;
    1030                 :                 }
    1031                 : 
    1032               4 :                 if (GetFieldIndexNumber(iField) == 0)
    1033                 :                 {
    1034                 :                     fprintf(fp, "    %s %s ;\n", poFieldDefn->GetNameRef(), 
    1035               4 :                             pszFieldType );
    1036                 :                 }
    1037                 :                 else
    1038                 :                 {
    1039                 :                     fprintf(fp, "    %s %s Index %d ;\n", 
    1040                 :                             poFieldDefn->GetNameRef(), pszFieldType,
    1041               0 :                             GetFieldIndexNumber(iField) );
    1042                 :                 }
    1043                 :                 
    1044                 :             }
    1045                 :         }
    1046                 :         else
    1047                 :         {
    1048               0 :             fprintf(fp, "Definition Table\n");
    1049               0 :             fprintf(fp, "  Type NATIVE Charset \"%s\"\n", m_pszCharset);
    1050               0 :             fprintf(fp, "  Fields 1\n");
    1051               0 :             fprintf(fp, "    FID Integer ;\n" );
    1052                 :         }
    1053                 : 
    1054               2 :         VSIFClose(fp);
    1055                 :     }
    1056                 :     else
    1057                 :     {
    1058                 :         CPLError(CE_Failure, CPLE_FileIO,
    1059               0 :                  "Failed to create file `%s'", m_pszFname);
    1060               0 :         return -1;
    1061                 :     }
    1062                 : 
    1063               2 :     return 0;
    1064                 : }
    1065                 : 
    1066                 : /**********************************************************************
    1067                 :  *                   TABFile::Close()
    1068                 :  *
    1069                 :  * Close current file, and release all memory used.
    1070                 :  *
    1071                 :  * Returns 0 on success, -1 on error.
    1072                 :  **********************************************************************/
    1073               4 : int TABFile::Close()
    1074                 : {
    1075                 :     // Commit the latest changes to the file...
    1076                 :     
    1077                 :     // In Write access, it's time to write the .TAB file.
    1078               4 :     if (m_eAccessMode == TABWrite && m_poMAPFile)
    1079                 :     {
    1080                 :         // First update file version number...
    1081               2 :         int nMapObjVersion = m_poMAPFile->GetMinTABFileVersion();
    1082               2 :         m_nVersion = MAX(m_nVersion, nMapObjVersion);
    1083                 : 
    1084               2 :         WriteTABFile();
    1085                 :     }
    1086                 : 
    1087               4 :     if (m_poMAPFile)
    1088                 :     {
    1089               4 :         m_poMAPFile->Close();
    1090               4 :         delete m_poMAPFile;
    1091               4 :         m_poMAPFile = NULL;
    1092                 :     }
    1093                 : 
    1094               4 :     if (m_poDATFile)
    1095                 :     {
    1096               4 :         m_poDATFile->Close();
    1097               4 :         delete m_poDATFile;
    1098               4 :         m_poDATFile = NULL;
    1099                 :     }
    1100                 : 
    1101               4 :     if (m_poINDFile)
    1102                 :     {
    1103               0 :         m_poINDFile->Close();
    1104               0 :         delete m_poINDFile;
    1105               0 :         m_poINDFile = NULL;
    1106                 :     }
    1107                 : 
    1108               4 :     if (m_poCurFeature)
    1109                 :     {
    1110               1 :         delete m_poCurFeature;
    1111               1 :         m_poCurFeature = NULL;
    1112                 :     }
    1113                 : 
    1114                 :     /*-----------------------------------------------------------------
    1115                 :      * Note: we have to check the reference count before deleting 
    1116                 :      * m_poSpatialRef and m_poDefn
    1117                 :      *----------------------------------------------------------------*/
    1118               4 :     if (m_poDefn )
    1119                 :     {
    1120               4 :         int nRefCount = m_poDefn->Dereference();
    1121                 : 
    1122               4 :         CPLAssert( nRefCount >= 0 );
    1123                 : 
    1124               4 :         if( nRefCount == 0 )
    1125               3 :             delete m_poDefn;
    1126               4 :         m_poDefn = NULL;
    1127                 :     }
    1128                 :     
    1129               4 :     if (m_poSpatialRef && m_poSpatialRef->Dereference() == 0)
    1130               2 :         delete m_poSpatialRef;
    1131               4 :     m_poSpatialRef = NULL;
    1132                 :     
    1133               4 :     CSLDestroy(m_papszTABFile);
    1134               4 :     m_papszTABFile = NULL;
    1135                 : 
    1136               4 :     CPLFree(m_pszFname);
    1137               4 :     m_pszFname = NULL;
    1138                 : 
    1139               4 :     CPLFree(m_pszCharset);
    1140               4 :     m_pszCharset = NULL;
    1141                 : 
    1142               4 :     CPLFree(m_panIndexNo);
    1143               4 :     m_panIndexNo = NULL;
    1144                 : 
    1145               4 :     CPLFree(m_panMatchingFIDs);
    1146               4 :     m_panMatchingFIDs = NULL;
    1147                 : 
    1148               4 :     return 0;
    1149                 : }
    1150                 : 
    1151                 : /**********************************************************************
    1152                 :  *                   TABFile::SetQuickSpatialIndexMode()
    1153                 :  *
    1154                 :  * Select "quick spatial index mode". 
    1155                 :  *
    1156                 :  * The default behavior of MITAB is to generate an optimized spatial index,
    1157                 :  * but this results in slower write speed. 
    1158                 :  *
    1159                 :  * Applications that want faster write speed and do not care
    1160                 :  * about the performance of spatial queries on the resulting file can
    1161                 :  * use SetQuickSpatialIndexMode() to require the creation of a non-optimal
    1162                 :  * spatial index (actually emulating the type of spatial index produced
    1163                 :  * by MITAB before version 1.6.0). In this mode writing files can be 
    1164                 :  * about 5 times faster, but spatial queries can be up to 30 times slower.
    1165                 :  *
    1166                 :  * Returns 0 on success, -1 on error.
    1167                 :  **********************************************************************/
    1168               0 : int TABFile::SetQuickSpatialIndexMode(GBool bQuickSpatialIndexMode/*=TRUE*/)
    1169                 : {
    1170               0 :     if (m_eAccessMode != TABWrite || m_poMAPFile == NULL)
    1171                 :     {
    1172                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1173               0 :                  "SetQuickSpatialIndexMode() failed: file not opened for write access.");
    1174               0 :         return -1;
    1175                 :     }
    1176                 : 
    1177                 : 
    1178               0 :     return m_poMAPFile->SetQuickSpatialIndexMode(bQuickSpatialIndexMode);
    1179                 : }
    1180                 : 
    1181                 : 
    1182                 : 
    1183                 : /**********************************************************************
    1184                 :  *                   TABFile::GetNextFeatureId()
    1185                 :  *
    1186                 :  * Returns feature id that follows nPrevId, or -1 if it is the
    1187                 :  * last feature id.  Pass nPrevId=-1 to fetch the first valid feature id.
    1188                 :  **********************************************************************/
    1189              57 : int TABFile::GetNextFeatureId(int nPrevId)
    1190                 : {
    1191              57 :     if (m_eAccessMode != TABRead)
    1192                 :     {
    1193                 :         CPLError(CE_Failure, CPLE_NotSupported,
    1194               0 :                  "GetNextFeatureId() can be used only with Read access.");
    1195               0 :         return -1;
    1196                 :     }
    1197                 : 
    1198                 :     /*-----------------------------------------------------------------
    1199                 :      * Are we using spatial rather than .ID based traversal?
    1200                 :      *----------------------------------------------------------------*/
    1201              57 :     if( bUseSpatialTraversal )
    1202              11 :         return m_poMAPFile->GetNextFeatureId( nPrevId );
    1203                 : 
    1204                 :     /*-----------------------------------------------------------------
    1205                 :      * Should we use an attribute index traversal?
    1206                 :      *----------------------------------------------------------------*/
    1207              46 :     if( m_poAttrQuery != NULL)
    1208                 :     {
    1209              32 :         if( m_panMatchingFIDs == NULL )
    1210                 :         {
    1211              32 :             m_iMatchingFID = 0;
    1212                 :             m_panMatchingFIDs = m_poAttrQuery->EvaluateAgainstIndices( this,
    1213              32 :                                                                  NULL );
    1214                 :         }
    1215              32 :         if( m_panMatchingFIDs != NULL )
    1216                 :         {
    1217               0 :             if( m_panMatchingFIDs[m_iMatchingFID] == OGRNullFID )
    1218               0 :                 return OGRNullFID;
    1219                 : 
    1220               0 :             return m_panMatchingFIDs[m_iMatchingFID++] + 1;
    1221                 :         }
    1222                 :     }
    1223                 : 
    1224                 :     /*-----------------------------------------------------------------
    1225                 :      * Establish what the next logical feature ID should be
    1226                 :      *----------------------------------------------------------------*/
    1227              46 :     int nFeatureId = -1;
    1228                 : 
    1229              51 :     if (nPrevId <= 0 && m_nLastFeatureId > 0)
    1230               5 :         nFeatureId = 1;       // Feature Ids start at 1
    1231              79 :     else if (nPrevId > 0 && nPrevId < m_nLastFeatureId)
    1232              38 :         nFeatureId = nPrevId + 1;
    1233                 :     else
    1234                 :     {
    1235                 :         // This was the last feature
    1236               3 :         return OGRNullFID;
    1237                 :     }
    1238                 : 
    1239                 :     /*-----------------------------------------------------------------
    1240                 :      * Skip any feature with NONE geometry and a deleted attribute record
    1241                 :      *----------------------------------------------------------------*/
    1242              86 :     while(nFeatureId <= m_nLastFeatureId)
    1243                 :     {
    1244              43 :         if ( m_poMAPFile->MoveToObjId(nFeatureId) != 0 ||
    1245                 :              m_poDATFile->GetRecordBlock(nFeatureId) == NULL )
    1246                 :         {
    1247                 :             CPLError(CE_Failure, CPLE_IllegalArg,
    1248                 :                      "GetNextFeatureId() failed: unable to set read pointer "
    1249               0 :                      "to feature id %d",  nFeatureId);
    1250               0 :             return -1;
    1251                 :         }
    1252                 : 
    1253                 : // __TODO__ Add a test here to check if object is deleted, 
    1254                 : // i.e. 0x40 set on object_id in object block
    1255              43 :         if (m_poMAPFile->GetCurObjType() != TAB_GEOM_NONE ||
    1256                 :             m_poDATFile->IsCurrentRecordDeleted() == FALSE)
    1257                 :         {
    1258                 :             // This feature contains at least a geometry or some attributes...
    1259                 :             // return its id.
    1260              43 :             return nFeatureId;
    1261                 :         }
    1262                 : 
    1263               0 :         nFeatureId++;
    1264                 :     }
    1265                 : 
    1266                 :     // If we reached this point, then we kept skipping deleted features
    1267                 :     // and stopped when EOF was reached.
    1268               0 :     return -1;
    1269                 : }
    1270                 : 
    1271                 : /**********************************************************************
    1272                 :  *                   TABFile::GetNextFeatureId_Spatial()
    1273                 :  *
    1274                 :  * Returns feature id that follows nPrevId, or -1 if it is the
    1275                 :  * last feature id, but by traversing the spatial tree instead of the
    1276                 :  * direct object index.  Generally speaking the feature id's will be
    1277                 :  * returned in an unordered fashion.  
    1278                 :  **********************************************************************/
    1279               0 : int TABFile::GetNextFeatureId_Spatial(int nPrevId)
    1280                 : {
    1281               0 :     if (m_eAccessMode != TABRead)
    1282                 :     {
    1283                 :         CPLError(CE_Failure, CPLE_NotSupported,
    1284               0 :             "GetNextFeatureId_Spatial() can be used only with Read access.");
    1285               0 :         return -1;
    1286                 :     }
    1287                 : 
    1288               0 :     if( m_poMAPFile == NULL )
    1289                 :     {
    1290                 :         CPLError(CE_Failure, CPLE_NotSupported,
    1291               0 :             "GetNextFeatureId_Spatial() requires availability of .MAP file." );
    1292               0 :         return -1;
    1293                 :     }
    1294                 : 
    1295               0 :     return m_poMAPFile->GetNextFeatureId( nPrevId );
    1296                 : }
    1297                 : 
    1298                 : /**********************************************************************
    1299                 :  *                   TABFile::GetFeatureRef()
    1300                 :  *
    1301                 :  * Fill and return a TABFeature object for the specified feature id.
    1302                 :  *
    1303                 :  * The retruned pointer is a reference to an object owned and maintained
    1304                 :  * by this TABFile object.  It should not be altered or freed by the 
    1305                 :  * caller and its contents is guaranteed to be valid only until the next
    1306                 :  * call to GetFeatureRef() or Close().
    1307                 :  *
    1308                 :  * Returns NULL if the specified feature id does not exist of if an
    1309                 :  * error happened.  In any case, CPLError() will have been called to
    1310                 :  * report the reason of the failure.
    1311                 :  *
    1312                 :  * If an unsupported object type is encountered (likely from a newer version
    1313                 :  * of MapInfo) then a valid feature will be returned with a NONE geometry,
    1314                 :  * and a warning will be produced with code TAB_WarningFeatureTypeNotSupported
    1315                 :  * CPLGetLastErrorNo() should be used to detect that case.
    1316                 :  **********************************************************************/
    1317              53 : TABFeature *TABFile::GetFeatureRef(int nFeatureId)
    1318                 : {
    1319              53 :     CPLErrorReset();
    1320                 : 
    1321              53 :     if (m_eAccessMode != TABRead)
    1322                 :     {
    1323                 :         CPLError(CE_Failure, CPLE_NotSupported,
    1324               0 :                  "GetFeatureRef() can be used only with Read access.");
    1325               0 :         return NULL;
    1326                 :     }
    1327                 : 
    1328                 :     /*-----------------------------------------------------------------
    1329                 :      * Make sure file is opened and Validate feature id by positioning
    1330                 :      * the read pointers for the .MAP and .DAT files to this feature id.
    1331                 :      *----------------------------------------------------------------*/
    1332              53 :     if (m_poMAPFile == NULL)
    1333                 :     {
    1334                 :         CPLError(CE_Failure, CPLE_IllegalArg,
    1335               0 :                  "GetFeatureRef() failed: file is not opened!");
    1336               0 :         return NULL;
    1337                 :     }
    1338                 : 
    1339                 : 
    1340              53 :     if (nFeatureId <= 0 || nFeatureId > m_nLastFeatureId ||
    1341                 :         m_poMAPFile->MoveToObjId(nFeatureId) != 0 ||
    1342                 :         m_poDATFile->GetRecordBlock(nFeatureId) == NULL )
    1343                 :     {
    1344                 :         //     CPLError(CE_Failure, CPLE_IllegalArg,
    1345                 :         //    "GetFeatureRef() failed: invalid feature id %d", 
    1346                 :         //    nFeatureId);
    1347               0 :         return NULL;
    1348                 :     }
    1349                 :     
    1350                 :     /*-----------------------------------------------------------------
    1351                 :      * Flush current feature object
    1352                 :      * __TODO__ try to reuse if it is already of the right type
    1353                 :      *----------------------------------------------------------------*/
    1354              53 :     if (m_poCurFeature)
    1355                 :     {
    1356              31 :         delete m_poCurFeature;
    1357              31 :         m_poCurFeature = NULL;
    1358                 :     }
    1359                 : 
    1360                 :     /*-----------------------------------------------------------------
    1361                 :      * Create new feature object of the right type
    1362                 :      * Unsupported object types are returned as raw TABFeature (i.e. NONE
    1363                 :      * geometry)
    1364                 :      *----------------------------------------------------------------*/
    1365                 :     m_poCurFeature = TABFeature::CreateFromMapInfoType(m_poMAPFile->GetCurObjType(), 
    1366              53 :                                                        m_poDefn);
    1367                 : 
    1368                 :     /*-----------------------------------------------------------------
    1369                 :      * Read fields from the .DAT file
    1370                 :      * GetRecordBlock() has already been called above...
    1371                 :      *----------------------------------------------------------------*/
    1372              53 :     if (m_poCurFeature->ReadRecordFromDATFile(m_poDATFile) != 0)
    1373                 :     {
    1374               0 :         delete m_poCurFeature;
    1375               0 :         m_poCurFeature = NULL;
    1376               0 :         return NULL;
    1377                 :     }
    1378                 : 
    1379                 :     /*-----------------------------------------------------------------
    1380                 :      * Read geometry from the .MAP file
    1381                 :      * MoveToObjId() has already been called above...
    1382                 :      *----------------------------------------------------------------*/
    1383                 :     TABMAPObjHdr *poObjHdr = 
    1384                 :         TABMAPObjHdr::NewObj((GByte)m_poMAPFile->GetCurObjType(), 
    1385              53 :                              m_poMAPFile->GetCurObjId());
    1386                 :     // Note that poObjHdr==NULL is a valid case if geometry type is NONE
    1387                 : 
    1388             106 :     if ((poObjHdr && poObjHdr->ReadObj(m_poMAPFile->GetCurObjBlock()) != 0) ||
    1389              53 :         m_poCurFeature->ReadGeometryFromMAPFile(m_poMAPFile, poObjHdr) != 0)
    1390                 :     {
    1391               0 :         delete m_poCurFeature;
    1392               0 :         m_poCurFeature = NULL;
    1393               0 :         if (poObjHdr) 
    1394               0 :             delete poObjHdr;
    1395               0 :         return NULL;
    1396                 :     }
    1397              53 :     if (poObjHdr)       // May be NULL if feature geometry type is NONE
    1398              53 :         delete poObjHdr; 
    1399                 : 
    1400              53 :     m_nCurFeatureId = nFeatureId;
    1401              53 :     m_poCurFeature->SetFID(m_nCurFeatureId);
    1402                 : 
    1403              53 :     m_poCurFeature->SetRecordDeleted(m_poDATFile->IsCurrentRecordDeleted());
    1404                 : 
    1405              53 :     return m_poCurFeature;
    1406                 : }
    1407                 : 
    1408                 : /**********************************************************************
    1409                 :  *                   TABFile::WriteFeature()
    1410                 :  *
    1411                 :  * Write a feature to this dataset.  
    1412                 :  *
    1413                 :  * For now only sequential writes are supported (i.e. with nFeatureId=-1)
    1414                 :  * but eventually we should be able to do random access by specifying
    1415                 :  * a value through nFeatureId.
    1416                 :  *
    1417                 :  * Returns the new featureId (> 0) on success, or -1 if an
    1418                 :  * error happened in which case, CPLError() will have been called to
    1419                 :  * report the reason of the failure.
    1420                 :  **********************************************************************/
    1421              13 : int TABFile::WriteFeature(TABFeature *poFeature, int nFeatureId /*=-1*/)
    1422                 : {
    1423              13 :     if (m_eAccessMode != TABWrite)
    1424                 :     {
    1425                 :         CPLError(CE_Failure, CPLE_NotSupported,
    1426               0 :                  "WriteFeature() can be used only with Write access.");
    1427               0 :         return -1;
    1428                 :     }
    1429                 : 
    1430              13 :     if (nFeatureId != -1)
    1431                 :     {
    1432                 :         CPLError(CE_Failure, CPLE_NotSupported,
    1433               0 :                  "WriteFeature(): random access not implemented yet.");
    1434               0 :         return -1;
    1435                 :     }
    1436                 : 
    1437                 :     /*-----------------------------------------------------------------
    1438                 :      * Make sure file is opened and establish new feature id.
    1439                 :      *----------------------------------------------------------------*/
    1440              13 :     if (m_poMAPFile == NULL)
    1441                 :     {
    1442                 :         CPLError(CE_Failure, CPLE_IllegalArg,
    1443               0 :                  "WriteFeature() failed: file is not opened!");
    1444               0 :         return -1;
    1445                 :     }
    1446                 : 
    1447              13 :     if (m_nLastFeatureId < 1)
    1448                 :     {
    1449                 :         /*-------------------------------------------------------------
    1450                 :          * OK, this is the first feature in the dataset... make sure the
    1451                 :          * .DAT schema has been initialized.
    1452                 :          *------------------------------------------------------------*/
    1453               2 :         if (m_poDefn == NULL)
    1454               0 :             SetFeatureDefn(poFeature->GetDefnRef(), NULL);
    1455                 : 
    1456                 :         /*-------------------------------------------------------------
    1457                 :          * Special hack to write out at least one field if none are in 
    1458                 :          * OGRFeatureDefn.
    1459                 :          *------------------------------------------------------------*/
    1460               2 :         if( m_poDATFile->GetNumFields() == 0 )
    1461                 :         {
    1462                 :             CPLError(CE_Warning, CPLE_IllegalArg,
    1463               0 :                      "MapInfo tables must contain at least 1 column, adding dummy FID column.");
    1464               0 :             m_poDATFile->AddField("FID", TABFInteger, 10, 0 );
    1465                 :         }
    1466                 : 
    1467               2 :         nFeatureId = m_nLastFeatureId = 1;
    1468                 :     }
    1469                 :     else
    1470                 :     {
    1471              11 :         nFeatureId = ++ m_nLastFeatureId;
    1472                 :     }
    1473                 : 
    1474                 : 
    1475                 :     /*-----------------------------------------------------------------
    1476                 :      * Write fields to the .DAT file and update .IND if necessary
    1477                 :      *----------------------------------------------------------------*/
    1478              26 :     if (m_poDATFile == NULL ||
    1479                 :         m_poDATFile->GetRecordBlock(nFeatureId) == NULL ||
    1480                 :         poFeature->WriteRecordToDATFile(m_poDATFile, m_poINDFile,
    1481              13 :                                         m_panIndexNo) != 0 )
    1482                 :     {
    1483                 :         CPLError(CE_Failure, CPLE_FileIO,
    1484                 :                  "Failed writing attributes for feature id %d in %s",
    1485               0 :                  nFeatureId, m_pszFname);
    1486               0 :         return -1;
    1487                 :     }
    1488                 : 
    1489                 :     /*-----------------------------------------------------------------
    1490                 :      * Write geometry to the .MAP file
    1491                 :      * The call to PrepareNewObj() takes care of the .ID file.
    1492                 :      *----------------------------------------------------------------*/
    1493                 :     TABMAPObjHdr *poObjHdr = 
    1494              13 :         TABMAPObjHdr::NewObj((GByte)poFeature->ValidateMapInfoType(m_poMAPFile),
    1495              13 :                              nFeatureId);
    1496                 :     
    1497                 :     /*-----------------------------------------------------------------
    1498                 :      * ValidateMapInfoType() may have returned TAB_GEOM_NONE if feature
    1499                 :      * contained an invalid geometry for its class. Need to catch that
    1500                 :      * case and return the error.
    1501                 :      *----------------------------------------------------------------*/
    1502              14 :     if (poObjHdr->m_nType == TAB_GEOM_NONE &&
    1503               1 :         poFeature->GetFeatureClass() != TABFCNoGeomFeature )
    1504                 :     {
    1505                 :         CPLError(CE_Failure, CPLE_FileIO,
    1506                 :                  "Invalid geometry for feature id %d in %s",
    1507               0 :                  nFeatureId, m_pszFname);
    1508               0 :         return -1;
    1509                 :     }
    1510                 : 
    1511                 :     /*-----------------------------------------------------------------
    1512                 :      * The ValidateMapInfoType() call above has forced calculation of the
    1513                 :      * feature's IntMBR. Store that value in the ObjHdr for use by
    1514                 :      * PrepareNewObj() to search the best node to insert the feature.
    1515                 :      *----------------------------------------------------------------*/
    1516              13 :     if ( poObjHdr && poObjHdr->m_nType != TAB_GEOM_NONE)
    1517                 :     {
    1518                 :         poFeature->GetIntMBR(poObjHdr->m_nMinX, poObjHdr->m_nMinY,
    1519              12 :                              poObjHdr->m_nMaxX, poObjHdr->m_nMaxY);
    1520                 :     }
    1521                 : 
    1522              26 :     if ( poObjHdr == NULL || m_poMAPFile == NULL ||
    1523                 :          m_poMAPFile->PrepareNewObj(poObjHdr) != 0 ||
    1524              13 :          poFeature->WriteGeometryToMAPFile(m_poMAPFile, poObjHdr) != 0 ||
    1525                 :          m_poMAPFile->CommitNewObj(poObjHdr) != 0 )
    1526                 :     {
    1527                 :         CPLError(CE_Failure, CPLE_FileIO,
    1528                 :                  "Failed writing geometry for feature id %d in %s",
    1529               0 :                  nFeatureId, m_pszFname);
    1530               0 :         if (poObjHdr)
    1531               0 :             delete poObjHdr;
    1532               0 :         return -1;
    1533                 :     }
    1534                 : 
    1535              13 :     delete poObjHdr;
    1536                 : 
    1537              13 :     return nFeatureId;
    1538                 : }
    1539                 : 
    1540                 : 
    1541                 : /**********************************************************************
    1542                 :  *                   TABFile::CreateFeature()
    1543                 :  *
    1544                 :  * Write a new feature to this dataset. The passed in feature is updated 
    1545                 :  * with the new feature id.
    1546                 :  *
    1547                 :  * Returns OGRERR_NONE on success, or an appropriate OGRERR_ code if an
    1548                 :  * error happened in which case, CPLError() will have been called to
    1549                 :  * report the reason of the failure.
    1550                 :  **********************************************************************/
    1551              13 : OGRErr TABFile::CreateFeature(TABFeature *poFeature)
    1552                 : {
    1553              13 :     int nFeatureId = -1;
    1554                 : 
    1555              13 :     nFeatureId = WriteFeature(poFeature, -1);
    1556                 : 
    1557              13 :     if (nFeatureId == -1)
    1558               0 :         return OGRERR_FAILURE;
    1559                 : 
    1560              13 :     poFeature->SetFID(nFeatureId);
    1561                 : 
    1562              13 :     return OGRERR_NONE;
    1563                 : }
    1564                 : 
    1565                 : /**********************************************************************
    1566                 :  *                   TABFile::SetFeature()
    1567                 :  *
    1568                 :  * Implementation of OGRLayer's SetFeature(), enabled only for
    1569                 :  * random write access   
    1570                 :  **********************************************************************/
    1571               0 : OGRErr TABFile::SetFeature( OGRFeature *poFeature )
    1572                 : 
    1573                 : {
    1574                 : //TODO: See CreateFeature()
    1575                 : // Need to convert OGRFeature to TABFeature, extract FID and then forward
    1576                 : // forward call to SetFeature(TABFeature, fid)
    1577               0 :     return OGRERR_UNSUPPORTED_OPERATION;
    1578                 : }
    1579                 : 
    1580                 : 
    1581                 : /**********************************************************************
    1582                 :  *                   TABFile::GetLayerDefn()
    1583                 :  *
    1584                 :  * Returns a reference to the OGRFeatureDefn that will be used to create
    1585                 :  * features in this dataset.
    1586                 :  *
    1587                 :  * Returns a reference to an object that is maintained by this TABFile
    1588                 :  * object (and thus should not be modified or freed by the caller) or
    1589                 :  * NULL if the OGRFeatureDefn has not been initialized yet (i.e. no file
    1590                 :  * opened yet)
    1591                 :  **********************************************************************/
    1592              43 : OGRFeatureDefn *TABFile::GetLayerDefn()
    1593                 : {
    1594              43 :     return m_poDefn;
    1595                 : }
    1596                 : 
    1597                 : /**********************************************************************
    1598                 :  *                   TABFile::SetFeatureDefn()
    1599                 :  *
    1600                 :  * Pass a reference to the OGRFeatureDefn that will be used to create
    1601                 :  * features in this dataset.  This function should be called after
    1602                 :  * creating a new dataset, but before writing the first feature.
    1603                 :  * All features that will be written to this dataset must share this same
    1604                 :  * OGRFeatureDefn.
    1605                 :  *
    1606                 :  * A reference to the OGRFeatureDefn will be kept and will be used to
    1607                 :  * build the .DAT file, etc.
    1608                 :  *
    1609                 :  * Returns 0 on success, -1 on error.
    1610                 :  **********************************************************************/
    1611               0 : int TABFile::SetFeatureDefn(OGRFeatureDefn *poFeatureDefn,
    1612                 :                          TABFieldType *paeMapInfoNativeFieldTypes /* =NULL */)
    1613                 : {
    1614                 :     int           iField, numFields;
    1615                 :     OGRFieldDefn *poFieldDefn;
    1616               0 :     TABFieldType eMapInfoType = TABFUnknown;
    1617               0 :     int nStatus = 0;
    1618                 : 
    1619               0 :     if (m_eAccessMode != TABWrite)
    1620                 :     {
    1621                 :         CPLError(CE_Failure, CPLE_NotSupported,
    1622               0 :                  "SetFeatureDefn() can be used only with Write access.");
    1623               0 :         return -1;
    1624                 :     }
    1625                 : 
    1626                 :     /*-----------------------------------------------------------------
    1627                 :      * Keep a reference to the OGRFeatureDefn... we'll have to take the
    1628                 :      * reference count into account when we are done with it.
    1629                 :      *----------------------------------------------------------------*/
    1630               0 :     if (m_poDefn && m_poDefn->Dereference() == 0)
    1631               0 :         delete m_poDefn;
    1632                 : 
    1633               0 :     m_poDefn = poFeatureDefn;
    1634               0 :     m_poDefn->Reference();
    1635                 : 
    1636                 :     /*-----------------------------------------------------------------
    1637                 :      * Pass field information to the .DAT file, after making sure that
    1638                 :      * it has been created and that it does not contain any field
    1639                 :      * definition yet.
    1640                 :      *----------------------------------------------------------------*/
    1641               0 :     if (m_poDATFile== NULL || m_poDATFile->GetNumFields() > 0 )
    1642                 :     {
    1643                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1644                 :                  "SetFeatureDefn() can be called only once in a newly "
    1645               0 :                  "created dataset.");
    1646               0 :         return -1;
    1647                 :     }
    1648                 : 
    1649               0 :     numFields = poFeatureDefn->GetFieldCount();
    1650               0 :     for(iField=0; nStatus==0 && iField < numFields; iField++)
    1651                 :     {
    1652               0 :         poFieldDefn = m_poDefn->GetFieldDefn(iField);
    1653                 : 
    1654                 :         /*-------------------------------------------------------------
    1655                 :          * Make sure field name is valid... check for special chars, etc.
    1656                 :          *------------------------------------------------------------*/
    1657               0 :         char *pszCleanName = TABCleanFieldName(poFieldDefn->GetNameRef());
    1658               0 :         if (!EQUAL(pszCleanName, poFieldDefn->GetNameRef()))
    1659               0 :             poFieldDefn->SetName(pszCleanName);
    1660               0 :         CPLFree(pszCleanName);
    1661               0 :         pszCleanName = NULL;
    1662                 : 
    1663               0 :         if (paeMapInfoNativeFieldTypes)
    1664                 :         {
    1665               0 :             eMapInfoType = paeMapInfoNativeFieldTypes[iField];
    1666                 :         }
    1667                 :         else
    1668                 :         {
    1669                 :             /*---------------------------------------------------------
    1670                 :              * Map OGRFieldTypes to MapInfo native types
    1671                 :              *--------------------------------------------------------*/
    1672               0 :             switch(poFieldDefn->GetType())
    1673                 :             {
    1674                 :               case OFTInteger:
    1675               0 :                 eMapInfoType = TABFInteger;
    1676               0 :                 break;
    1677                 :               case OFTReal:
    1678               0 :                 eMapInfoType = TABFFloat;
    1679               0 :                 break;
    1680                 :               case OFTDateTime:
    1681               0 :                 eMapInfoType = TABFDateTime;
    1682               0 :                 break;
    1683                 :               case OFTDate:
    1684               0 :                 eMapInfoType = TABFDate;
    1685               0 :                 break;
    1686                 :               case OFTTime:
    1687               0 :                 eMapInfoType = TABFTime;
    1688               0 :                 break;
    1689                 :               case OFTString:
    1690                 :               default:
    1691               0 :                 eMapInfoType = TABFChar;
    1692                 :             }
    1693                 :         }
    1694                 : 
    1695                 :         nStatus = m_poDATFile->AddField(poFieldDefn->GetNameRef(),
    1696                 :                                             eMapInfoType,
    1697                 :                                             poFieldDefn->GetWidth(),
    1698               0 :                                             poFieldDefn->GetPrecision());
    1699                 :     }
    1700                 : 
    1701                 :     /*-----------------------------------------------------------------
    1702                 :      * Alloc the array to keep track of indexed fields (default=NOT indexed)
    1703                 :      *----------------------------------------------------------------*/
    1704               0 :     m_panIndexNo = (int *)CPLCalloc(numFields, sizeof(int));
    1705                 : 
    1706               0 :     return nStatus;
    1707                 : }
    1708                 : 
    1709                 : /**********************************************************************
    1710                 :  *                   TABFile::AddFieldNative()
    1711                 :  *
    1712                 :  * Create a new field using a native mapinfo data type... this is an 
    1713                 :  * alternative to defining fields through the OGR interface.
    1714                 :  * This function should be called after creating a new dataset, but before 
    1715                 :  * writing the first feature.
    1716                 :  *
    1717                 :  * This function will build/update the OGRFeatureDefn that will have to be
    1718                 :  * used when writing features to this dataset.
    1719                 :  *
    1720                 :  * A reference to the OGRFeatureDefn can be obtained using GetLayerDefn().
    1721                 :  *
    1722                 :  * Note: The bUnique flag has no effect on TABFiles.  See the TABView class.
    1723                 :  *
    1724                 :  * Returns 0 on success, -1 on error.
    1725                 :  **********************************************************************/
    1726               4 : int TABFile::AddFieldNative(const char *pszName, TABFieldType eMapInfoType,
    1727                 :                             int nWidth /*=0*/, int nPrecision /*=0*/,
    1728                 :                             GBool bIndexed /*=FALSE*/, GBool /*bUnique=FALSE*/, int bApproxOK)
    1729                 : {
    1730                 :     OGRFieldDefn *poFieldDefn;
    1731               4 :     int nStatus = 0;
    1732               4 :     char *pszCleanName = NULL;
    1733                 :     char szNewFieldName[31+1]; /* 31 is the max characters for a field name*/
    1734               4 :     int nRenameNum = 1;
    1735                 : 
    1736               4 :     if (m_eAccessMode != TABWrite)
    1737                 :     {
    1738                 :         CPLError(CE_Failure, CPLE_NotSupported,
    1739               0 :                  "AddFieldNative() can be used only with Write access.");
    1740               0 :         return -1;
    1741                 :     }
    1742                 : 
    1743                 :     /*-----------------------------------------------------------------
    1744                 :      * Check that call happens at the right time in dataset's life.
    1745                 :      *----------------------------------------------------------------*/
    1746               4 :     if (m_eAccessMode != TABWrite || 
    1747                 :         m_nLastFeatureId > 0 || m_poDATFile == NULL)
    1748                 :     {
    1749                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    1750                 :                  "AddFieldNative() must be called after opening a new "
    1751               0 :                  "dataset, but before writing the first feature to it.");
    1752               0 :         return -1;
    1753                 :     }
    1754                 : 
    1755                 :     /*-----------------------------------------------------------------
    1756                 :      * Create new OGRFeatureDefn if not done yet...
    1757                 :      *----------------------------------------------------------------*/
    1758               4 :     if (m_poDefn== NULL)
    1759                 :     {
    1760               0 :         char *pszFeatureClassName = TABGetBasename(m_pszFname);
    1761               0 :         m_poDefn = new OGRFeatureDefn(pszFeatureClassName);
    1762               0 :         CPLFree(pszFeatureClassName);
    1763                 :         // Ref count defaults to 0... set it to 1
    1764               0 :         m_poDefn->Reference();
    1765                 :     }
    1766                 : 
    1767                 :     /*-----------------------------------------------------------------
    1768                 :      * Validate field width... must be <= 254
    1769                 :      *----------------------------------------------------------------*/
    1770               4 :     if (nWidth > 254)
    1771                 :     {
    1772                 :         CPLError(CE_Warning, CPLE_IllegalArg,
    1773                 :                  "Invalid size (%d) for field '%s'.  "
    1774               0 :                  "Size must be 254 or less.", nWidth, pszName);
    1775               0 :         nWidth=254;
    1776                 :     }
    1777                 : 
    1778                 :     /*-----------------------------------------------------------------
    1779                 :      * Map fields with width=0 (variable length in OGR) to a valid default
    1780                 :      *----------------------------------------------------------------*/
    1781               4 :     if (eMapInfoType == TABFDecimal && nWidth == 0)
    1782               0 :         nWidth=20;
    1783               4 :     else if (nWidth == 0)
    1784               0 :         nWidth=254; /* char fields */
    1785                 : 
    1786                 :     /*-----------------------------------------------------------------
    1787                 :      * Make sure field name is valid... check for special chars, etc.
    1788                 :      * (pszCleanName will have to be freed.)
    1789                 :      *----------------------------------------------------------------*/
    1790               4 :     pszCleanName = TABCleanFieldName(pszName);
    1791                 : 
    1792               4 :     if( !bApproxOK &&
    1793                 :         ( m_poDefn->GetFieldIndex(pszCleanName) >= 0 ||
    1794                 :           !EQUAL(pszName, pszCleanName) ) )
    1795                 :     {
    1796                 :         CPLError( CE_Failure, CPLE_NotSupported,
    1797                 :                   "Failed to add field named '%s'",
    1798               0 :                   pszName );
    1799                 :     }
    1800                 : 
    1801               4 :     strncpy(szNewFieldName, pszCleanName, 31);
    1802               4 :     szNewFieldName[31] = '\0';
    1803                 : 
    1804               8 :     while (m_poDefn->GetFieldIndex(szNewFieldName) >= 0 && nRenameNum < 10) 
    1805               0 :       sprintf( szNewFieldName, "%.29s_%.1d", pszCleanName, nRenameNum++ );
    1806                 : 
    1807               8 :     while (m_poDefn->GetFieldIndex(szNewFieldName) >= 0 && nRenameNum < 100) 
    1808               0 :       sprintf( szNewFieldName, "%.29s%.2d", pszCleanName, nRenameNum++ );
    1809                 : 
    1810               4 :     if (m_poDefn->GetFieldIndex(szNewFieldName) >= 0)
    1811                 :     {
    1812                 :       CPLError( CE_Failure, CPLE_NotSupported, 
    1813                 :                 "Too many field names like '%s' when truncated to 31 letters " 
    1814               0 :                 "for MapInfo format.", pszCleanName );
    1815                 :     }
    1816                 : 
    1817               4 :     if( !EQUAL(pszCleanName,szNewFieldName) ) 
    1818                 :     {
    1819                 :       CPLError( CE_Warning, CPLE_NotSupported,
    1820                 :                 "Normalized/laundered field name: '%s' to '%s'",
    1821                 :                 pszCleanName,
    1822               0 :                 szNewFieldName );
    1823                 :     }
    1824                 : 
    1825                 :     /*-----------------------------------------------------------------
    1826                 :      * Map MapInfo native types to OGR types
    1827                 :      *----------------------------------------------------------------*/
    1828               4 :     poFieldDefn = NULL;
    1829                 : 
    1830               4 :     switch(eMapInfoType)
    1831                 :     {
    1832                 :       case TABFChar:
    1833                 :         /*-------------------------------------------------
    1834                 :          * CHAR type
    1835                 :          *------------------------------------------------*/
    1836               2 :         poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTString);
    1837               2 :         poFieldDefn->SetWidth(nWidth);
    1838               2 :         break;
    1839                 :       case TABFInteger:
    1840                 :         /*-------------------------------------------------
    1841                 :          * INTEGER type
    1842                 :          *------------------------------------------------*/
    1843               1 :         poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTInteger);
    1844               1 :         break;
    1845                 :       case TABFSmallInt:
    1846                 :         /*-------------------------------------------------
    1847                 :          * SMALLINT type
    1848                 :          *------------------------------------------------*/
    1849               0 :         poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTInteger);
    1850               0 :         break;
    1851                 :       case TABFDecimal:
    1852                 :         /*-------------------------------------------------
    1853                 :          * DECIMAL type
    1854                 :          *------------------------------------------------*/
    1855               0 :         poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTReal);
    1856               0 :         poFieldDefn->SetWidth(nWidth);
    1857               0 :         poFieldDefn->SetPrecision(nPrecision);
    1858               0 :         break;
    1859                 :       case TABFFloat:
    1860                 :         /*-------------------------------------------------
    1861                 :          * FLOAT type
    1862                 :          *------------------------------------------------*/
    1863               1 :         poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTReal);
    1864               1 :         break;
    1865                 :       case TABFDate:
    1866                 :         /*-------------------------------------------------
    1867                 :          * DATE type (V450, returned as a string: "DD/MM/YYYY")
    1868                 :          *------------------------------------------------*/
    1869                 :         poFieldDefn = new OGRFieldDefn(szNewFieldName, 
    1870                 : #ifdef MITAB_USE_OFTDATETIME
    1871               0 :                                                    OFTDate);
    1872                 : #else
    1873                 :                                                    OFTString);
    1874                 : #endif
    1875               0 :         poFieldDefn->SetWidth(10);
    1876               0 :         m_nVersion = MAX(m_nVersion, 450);
    1877               0 :         break;
    1878                 :       case TABFTime:
    1879                 :         /*-------------------------------------------------
    1880                 :          * TIME type (V900, returned as a string: "HH:MM:SS")
    1881                 :          *------------------------------------------------*/
    1882                 :         poFieldDefn = new OGRFieldDefn(szNewFieldName, 
    1883                 : #ifdef MITAB_USE_OFTDATETIME
    1884               0 :                                                    OFTTime);
    1885                 : #else
    1886                 :                                                    OFTString);
    1887                 : #endif
    1888               0 :         poFieldDefn->SetWidth(8);
    1889               0 :         m_nVersion = MAX(m_nVersion, 900);
    1890               0 :         break;
    1891                 :       case TABFDateTime:
    1892                 :         /*-------------------------------------------------
    1893                 :          * DATETIME type (V900, returned as a string: "DD/MM/YYYY HH:MM:SS")
    1894                 :          *------------------------------------------------*/
    1895                 :         poFieldDefn = new OGRFieldDefn(szNewFieldName, 
    1896                 : #ifdef MITAB_USE_OFTDATETIME
    1897               0 :                                                    OFTDateTime);
    1898                 : #else
    1899                 :                                                    OFTString);
    1900                 : #endif
    1901               0 :         poFieldDefn->SetWidth(19);
    1902               0 :         m_nVersion = MAX(m_nVersion, 900);
    1903               0 :         break;
    1904                 :       case TABFLogical:
    1905                 :         /*-------------------------------------------------
    1906                 :          * LOGICAL type (value "T" or "F")
    1907                 :          *------------------------------------------------*/
    1908               0 :         poFieldDefn = new OGRFieldDefn(szNewFieldName, OFTString);
    1909               0 :         poFieldDefn->SetWidth(1);
    1910               0 :         break;
    1911                 :       default:
    1912                 :         CPLError(CE_Failure, CPLE_NotSupported,
    1913               0 :                  "Unsupported type for field %s", szNewFieldName);
    1914               0 :         CPLFree(pszCleanName);
    1915               0 :         return -1;
    1916                 :     }
    1917                 : 
    1918                 :     /*-----------------------------------------------------
    1919                 :      * Add the FieldDefn to the FeatureDefn 
    1920                 :      *----------------------------------------------------*/
    1921               4 :     m_poDefn->AddFieldDefn(poFieldDefn);
    1922               4 :     delete poFieldDefn;
    1923                 : 
    1924                 :     /*-----------------------------------------------------
    1925                 :      * ... and pass field info to the .DAT file.
    1926                 :      *----------------------------------------------------*/
    1927                 :     nStatus = m_poDATFile->AddField(szNewFieldName, eMapInfoType, 
    1928               4 :                                     nWidth, nPrecision);
    1929                 : 
    1930                 :     /*-----------------------------------------------------------------
    1931                 :      * Extend the array to keep track of indexed fields (default=NOT indexed)
    1932                 :      *----------------------------------------------------------------*/
    1933                 :     m_panIndexNo = (int *)CPLRealloc(m_panIndexNo,
    1934               4 :                                      m_poDefn->GetFieldCount()*sizeof(int));
    1935               4 :     m_panIndexNo[m_poDefn->GetFieldCount()-1] = 0;
    1936                 : 
    1937                 :      /*-----------------------------------------------------------------
    1938                 :      * Index the field if requested
    1939                 :      *----------------------------------------------------------------*/
    1940               4 :     if (nStatus == 0 && bIndexed)
    1941               0 :         nStatus = SetFieldIndexed(m_poDefn->GetFieldCount()-1);
    1942                 : 
    1943               4 :     CPLFree(pszCleanName);
    1944               4 :     return nStatus;
    1945                 : }
    1946                 : 
    1947                 : 
    1948                 : /**********************************************************************
    1949                 :  *                   TABFile::GetNativeFieldType()
    1950                 :  *
    1951                 :  * Returns the native MapInfo field type for the specified field.
    1952                 :  *
    1953                 :  * Returns TABFUnknown if file is not opened, or if specified field index is
    1954                 :  * invalid.
    1955                 :  *
    1956                 :  * Note that field ids are positive and start at 0.
    1957                 :  **********************************************************************/
    1958               4 : TABFieldType TABFile::GetNativeFieldType(int nFieldId)
    1959                 : {
    1960               4 :     if (m_poDATFile)
    1961                 :     {
    1962               4 :         return m_poDATFile->GetFieldType(nFieldId);
    1963                 :     }
    1964               0 :     return TABFUnknown;
    1965                 : }
    1966                 : 
    1967                 : 
    1968                 : 
    1969                 : /**********************************************************************
    1970                 :  *                   TABFile::GetFieldIndexNumber()
    1971                 :  *
    1972                 :  * Returns the field's index number that was specified in the .TAB header
    1973                 :  * or 0 if the specified field is not indexed.
    1974                 :  *
    1975                 :  * Note that field ids are positive and start at 0
    1976                 :  * and valid index ids are positive and start at 1.
    1977                 :  **********************************************************************/
    1978               8 : int  TABFile::GetFieldIndexNumber(int nFieldId)
    1979                 : {
    1980               8 :     if (m_panIndexNo == NULL || nFieldId < 0 || 
    1981                 :         m_poDATFile== NULL || nFieldId >= m_poDATFile->GetNumFields())
    1982               0 :         return 0;  // no index
    1983                 : 
    1984               8 :     return m_panIndexNo[nFieldId];
    1985                 : }
    1986                 : 
    1987                 : /************************************************************************
    1988                 :  *                       TABFile::SetFieldIndexed()
    1989                 :  *
    1990                 :  * Request that a field be indexed.  This will create the .IND file if
    1991                 :  * necessary, etc.
    1992                 :  *
    1993                 :  * Note that field ids are positive and start at 0.
    1994                 :  *
    1995                 :  * Returns 0 on success, -1 on error.
    1996                 :  ************************************************************************/
    1997               0 : int TABFile::SetFieldIndexed( int nFieldId )
    1998                 : {
    1999                 :     /*-----------------------------------------------------------------
    2000                 :      * Make sure things are OK
    2001                 :      *----------------------------------------------------------------*/
    2002               0 :     if (m_pszFname == NULL || m_eAccessMode != TABWrite || m_poDefn == NULL)
    2003                 :     {
    2004                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2005                 :                  "SetFieldIndexed() must be called after opening a new "
    2006               0 :                  "dataset, but before writing the first feature to it.");
    2007               0 :         return -1;
    2008                 :     }
    2009                 : 
    2010               0 :     if (m_panIndexNo == NULL || nFieldId < 0 || 
    2011                 :         m_poDATFile== NULL || nFieldId >= m_poDATFile->GetNumFields())
    2012                 :     {
    2013                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2014               0 :                  "Invalid field number in SetFieldIndexed().");
    2015               0 :         return -1;
    2016                 :     }
    2017                 : 
    2018                 :     /*-----------------------------------------------------------------
    2019                 :      * If field is already indexed then just return
    2020                 :      *----------------------------------------------------------------*/
    2021               0 :     if (m_panIndexNo[nFieldId] != 0)
    2022               0 :         return 0;  // Nothing to do
    2023                 : 
    2024                 : 
    2025                 :     /*-----------------------------------------------------------------
    2026                 :      * Create .IND file if it's not done yet.
    2027                 :      *
    2028                 :      * Note: We can pass the .TAB's filename directly and the
    2029                 :      * TABINDFile class will automagically adjust the extension.
    2030                 :      *----------------------------------------------------------------*/
    2031               0 :     if (m_poINDFile == NULL)
    2032                 :     {
    2033               0 :         m_poINDFile = new TABINDFile;
    2034                 :    
    2035               0 :         if ( m_poINDFile->Open(m_pszFname, "w", TRUE) != 0)
    2036                 :         {
    2037                 :             // File could not be opened... 
    2038               0 :             delete m_poINDFile;
    2039               0 :             m_poINDFile = NULL;
    2040               0 :             return -1;
    2041                 :         }
    2042                 :     }
    2043                 : 
    2044                 :     /*-----------------------------------------------------------------
    2045                 :      * Init new index.
    2046                 :      *----------------------------------------------------------------*/
    2047                 :     int nNewIndexNo;
    2048               0 :     OGRFieldDefn *poFieldDefn = m_poDefn->GetFieldDefn(nFieldId);
    2049                 : 
    2050               0 :     if (poFieldDefn == NULL ||
    2051               0 :         (nNewIndexNo = m_poINDFile->CreateIndex(GetNativeFieldType(nFieldId),
    2052                 :                                                 poFieldDefn->GetWidth()) ) < 1)
    2053                 :     {
    2054                 :         // Failed... an error has already been reported.
    2055               0 :         return -1;
    2056                 :     }
    2057                 : 
    2058               0 :     m_panIndexNo[nFieldId] = nNewIndexNo;
    2059                 : 
    2060               0 :     return 0;
    2061                 : }
    2062                 : 
    2063                 : /************************************************************************
    2064                 :  *                       TABFile::IsFieldIndexed()
    2065                 :  *
    2066                 :  * Returns TRUE if field is indexed, or FALSE otherwise.
    2067                 :  ************************************************************************/
    2068               0 : GBool TABFile::IsFieldIndexed( int nFieldId )
    2069                 : {
    2070               0 :     return (GetFieldIndexNumber(nFieldId) > 0 ? TRUE:FALSE);
    2071                 : }
    2072                 : 
    2073                 : 
    2074                 : 
    2075                 : /**********************************************************************
    2076                 :  *                   TABFile::GetINDFileRef()
    2077                 :  *
    2078                 :  * Opens the .IND file for this dataset and returns a reference to
    2079                 :  * the handle.  
    2080                 :  * If the .IND file has already been opened then the same handle is 
    2081                 :  * returned directly.
    2082                 :  * If the .IND file does not exist then the function silently returns NULL.
    2083                 :  *
    2084                 :  * Note that the returned TABINDFile handle is only a reference to an
    2085                 :  * object that is owned by this class.  Callers can use it but cannot
    2086                 :  * destroy the object.  The object will remain valid for as long as 
    2087                 :  * the TABFile will remain open.
    2088                 :  **********************************************************************/
    2089               0 : TABINDFile  *TABFile::GetINDFileRef()
    2090                 : {
    2091               0 :     if (m_pszFname == NULL)
    2092               0 :         return NULL;
    2093                 : 
    2094               0 :     if (m_eAccessMode == TABRead && m_poINDFile == NULL)
    2095                 :     {
    2096                 :         /*-------------------------------------------------------------
    2097                 :          * File is not opened yet... do it now.
    2098                 :          *
    2099                 :          * Note: We can pass the .TAB's filename directly and the
    2100                 :          * TABINDFile class will automagically adjust the extension.
    2101                 :          *------------------------------------------------------------*/
    2102               0 :         m_poINDFile = new TABINDFile;
    2103                 :    
    2104               0 :         if ( m_poINDFile->Open(m_pszFname, "r", TRUE) != 0)
    2105                 :         {
    2106                 :             // File could not be opened... probably does not exist
    2107               0 :             delete m_poINDFile;
    2108               0 :             m_poINDFile = NULL;
    2109                 :         }
    2110               0 :         else if (m_panIndexNo && m_poDATFile)
    2111                 :         {
    2112                 :             /*---------------------------------------------------------
    2113                 :              * Pass type information for each indexed field.
    2114                 :              *--------------------------------------------------------*/
    2115               0 :             for(int i=0; i<m_poDATFile->GetNumFields(); i++)
    2116                 :             {
    2117               0 :                 if (m_panIndexNo[i] > 0)
    2118                 :                 {
    2119                 :                     m_poINDFile->SetIndexFieldType(m_panIndexNo[i],
    2120               0 :                                                    GetNativeFieldType(i));
    2121                 :                 }
    2122                 :             }
    2123                 :         }
    2124                 :     }
    2125                 : 
    2126               0 :     return m_poINDFile;
    2127                 : }
    2128                 : 
    2129                 : 
    2130                 : /**********************************************************************
    2131                 :  *                   TABFile::SetBounds()
    2132                 :  *
    2133                 :  * Set projection coordinates bounds of the newly created dataset.
    2134                 :  *
    2135                 :  * This function must be called after creating a new dataset and before any
    2136                 :  * feature can be written to it.
    2137                 :  *
    2138                 :  * Returns 0 on success, -1 on error.
    2139                 :  **********************************************************************/
    2140               2 : int TABFile::SetBounds(double dXMin, double dYMin, 
    2141                 :                        double dXMax, double dYMax)
    2142                 : {
    2143               2 :     if (m_eAccessMode != TABWrite)
    2144                 :     {
    2145                 :         CPLError(CE_Failure, CPLE_NotSupported,
    2146               0 :                  "SetBounds() can be used only with Write access.");
    2147               0 :         return -1;
    2148                 :     }
    2149                 : 
    2150                 :     /*-----------------------------------------------------------------
    2151                 :      * Check that dataset has been created but no feature set yet.
    2152                 :      *----------------------------------------------------------------*/
    2153               2 :     if (m_poMAPFile && m_nLastFeatureId < 1)
    2154                 :     {
    2155               2 :         m_poMAPFile->SetCoordsysBounds(dXMin, dYMin, dXMax, dYMax);
    2156                 : 
    2157               2 :         m_bBoundsSet = TRUE;
    2158                 :     }
    2159                 :     else
    2160                 :     {
    2161                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2162                 :                  "SetBounds() can be called only after dataset has been "
    2163               0 :                  "created and before any feature is set.");
    2164               0 :         return -1;
    2165                 :     }
    2166                 : 
    2167               2 :     return 0;
    2168                 : }
    2169                 : 
    2170                 : 
    2171                 : /**********************************************************************
    2172                 :  *                   TABFile::GetBounds()
    2173                 :  *
    2174                 :  * Fetch projection coordinates bounds of a dataset.
    2175                 :  *
    2176                 :  * The bForce flag has no effect on TAB files since the bounds are
    2177                 :  * always in the header.
    2178                 :  *
    2179                 :  * Returns 0 on success, -1 on error.
    2180                 :  **********************************************************************/
    2181               0 : int TABFile::GetBounds(double &dXMin, double &dYMin, 
    2182                 :                        double &dXMax, double &dYMax,
    2183                 :                        GBool /*bForce = TRUE*/)
    2184                 : {
    2185                 :     TABMAPHeaderBlock *poHeader;
    2186                 : 
    2187               0 :     if (m_poMAPFile && (poHeader=m_poMAPFile->GetHeaderBlock()) != NULL)
    2188                 :     {
    2189                 :         /*-------------------------------------------------------------
    2190                 :          * Projection bounds correspond to the +/- 1e9 integer coord. limits
    2191                 :          *------------------------------------------------------------*/
    2192                 :         double dX0, dX1, dY0, dY1;
    2193                 :         m_poMAPFile->Int2Coordsys(-1000000000, -1000000000,  
    2194               0 :                                   dX0, dY0);
    2195                 :         m_poMAPFile->Int2Coordsys(1000000000, 1000000000, 
    2196               0 :                                   dX1, dY1);
    2197                 :         /*-------------------------------------------------------------
    2198                 :          * ... and make sure that Min < Max
    2199                 :          *------------------------------------------------------------*/
    2200               0 :         dXMin = MIN(dX0, dX1);
    2201               0 :         dXMax = MAX(dX0, dX1);
    2202               0 :         dYMin = MIN(dY0, dY1);
    2203               0 :         dYMax = MAX(dY0, dY1);
    2204                 :     }
    2205                 :     else
    2206                 :     {
    2207                 :         CPLError(CE_Failure, CPLE_AppDefined,
    2208               0 :              "GetBounds() can be called only after dataset has been opened.");
    2209               0 :         return -1;
    2210                 :     }
    2211                 : 
    2212               0 :     return 0;
    2213                 : }
    2214                 : 
    2215                 : 
    2216                 : /**********************************************************************
    2217                 :  *                   TABFile::GetExtent()
    2218                 :  *
    2219                 :  * Fetch extent of the data currently stored in the dataset.
    2220                 :  *
    2221                 :  * The bForce flag has no effect on TAB files since that value is
    2222                 :  * always in the header.
    2223                 :  *
    2224                 :  * Returns OGRERR_NONE/OGRRERR_FAILURE.
    2225                 :  **********************************************************************/
    2226               0 : OGRErr TABFile::GetExtent (OGREnvelope *psExtent, int bForce)
    2227                 : {
    2228                 :     TABMAPHeaderBlock *poHeader;
    2229                 : 
    2230               0 :     if (m_poMAPFile && (poHeader=m_poMAPFile->GetHeaderBlock()) != NULL)
    2231                 :     {
    2232                 :         double dX0, dX1, dY0, dY1;
    2233                 :         /*-------------------------------------------------------------
    2234                 :          * Fetch extent of the data from the .map header block
    2235                 :          * this value is different from the projection bounds.
    2236                 :          *------------------------------------------------------------*/
    2237                 :         m_poMAPFile->Int2Coordsys(poHeader->m_nXMin, poHeader->m_nYMin,  
    2238               0 :                                   dX0, dY0);
    2239                 :         m_poMAPFile->Int2Coordsys(poHeader->m_nXMax, poHeader->m_nYMax, 
    2240               0 :                                   dX1, dY1);
    2241                 : 
    2242                 :        /*-------------------------------------------------------------
    2243                 :          * ... and make sure that Min < Max
    2244                 :          *------------------------------------------------------------*/
    2245               0 :         psExtent->MinX = MIN(dX0, dX1);
    2246               0 :         psExtent->MaxX = MAX(dX0, dX1);
    2247               0 :         psExtent->MinY = MIN(dY0, dY1);
    2248               0 :         psExtent->MaxY = MAX(dY0, dY1);
    2249                 : 
    2250               0 :         return OGRERR_NONE;
    2251                 :     }
    2252                 : 
    2253               0 :     return OGRERR_FAILURE;
    2254                 : }
    2255                 : 
    2256                 : /**********************************************************************
    2257                 :  *                   TABFile::GetFeatureCountByType()
    2258                 :  *
    2259                 :  * Return number of features of each type.
    2260                 :  *
    2261                 :  * Note that the sum of the 4 returned values may be different from
    2262                 :  * the total number of features since features with NONE geometry
    2263                 :  * are not taken into account here.
    2264                 :  *
    2265                 :  * Note: the bForce flag has nmo effect on .TAB files since the info
    2266                 :  * is always in the header.
    2267                 :  *
    2268                 :  * Returns 0 on success, or silently returns -1 (with no error) if this
    2269                 :  * information is not available.
    2270                 :  **********************************************************************/
    2271               2 : int TABFile::GetFeatureCountByType(int &numPoints, int &numLines,
    2272                 :                                    int &numRegions, int &numTexts,
    2273                 :                                    GBool /* bForce = TRUE*/ )
    2274                 : {
    2275                 :     TABMAPHeaderBlock *poHeader;
    2276                 : 
    2277               2 :     if (m_poMAPFile && (poHeader=m_poMAPFile->GetHeaderBlock()) != NULL)
    2278                 :     {
    2279               2 :         numPoints  = poHeader->m_numPointObjects;
    2280               2 :         numLines   = poHeader->m_numLineObjects;
    2281               2 :         numRegions = poHeader->m_numRegionObjects;
    2282               2 :         numTexts   = poHeader->m_numTextObjects;
    2283                 :     }
    2284                 :     else
    2285                 :     {
    2286               0 :         numPoints = numLines = numRegions = numTexts = 0;
    2287               0 :         return -1;
    2288                 :     }
    2289                 : 
    2290               2 :     return 0;
    2291                 : }
    2292                 : 
    2293                 : 
    2294                 : /**********************************************************************
    2295                 :  *                   TABFile::SetMIFCoordSys()
    2296                 :  *
    2297                 :  * Set projection for a new file using a MIF coordsys string.
    2298                 :  *
    2299                 :  * This function must be called after creating a new dataset and before any
    2300                 :  * feature can be written to it.
    2301                 :  *
    2302                 :  * Returns 0 on success, -1 on error.
    2303                 :  **********************************************************************/
    2304               0 : int TABFile::SetMIFCoordSys(const char *pszMIFCoordSys)
    2305                 : {
    2306               0 :     if (m_eAccessMode != TABWrite)
    2307                 :     {
    2308                 :         CPLError(CE_Failure, CPLE_NotSupported,
    2309               0 :                  "SetMIFCoordSys() can be used only with Write access.");
    2310               0 :         return -1;
    2311                 :     }
    2312                 : 
    2313                 :     /*-----------------------------------------------------------------
    2314                 :      * Check that dataset has been created but no feature set yet.
    2315                 :      *----------------------------------------------------------------*/
    2316               0 :     if (m_poMAPFile && m_nLastFeatureId < 1)
    2317                 :     {
    2318                 :         OGRSpatialReference *poSpatialRef;
    2319                 : 
    2320               0 :         poSpatialRef = MITABCoordSys2SpatialRef( pszMIFCoordSys );
    2321                 : 
    2322               0 :         if (poSpatialRef)
    2323                 :         {
    2324                 :             double dXMin, dYMin, dXMax, dYMax;
    2325               0 :             if (SetSpatialRef(poSpatialRef) == 0)
    2326                 :             {
    2327               0 :                 if (MITABExtractCoordSysBounds(pszMIFCoordSys,
    2328                 :                                                dXMin, dYMin, 
    2329                 :                                                dXMax, dYMax) == TRUE)
    2330                 :                 {
    2331                 :                     // If the coordsys string contains bounds, then use them
    2332               0 :                     if (SetBounds(dXMin, dYMin, dXMax, dYMax) != 0)
    2333                 :                     {
    2334                 :                         // Failed Setting Bounds... an error should have
    2335                 :                         // been already reported.
    2336               0 :                         return -1;
    2337                 :                     }
    2338                 :                 }
    2339                 :             }
    2340                 :             else
    2341                 :             {
    2342                 :                 // Failed setting poSpatialRef... and error should have 
    2343                 :                 // been reported.
    2344               0 :                 return -1;
    2345                 :             }
    2346                 : 
    2347                 :             // Release our handle on poSpatialRef
    2348               0 :             if( poSpatialRef->Dereference() == 0 )
    2349               0 :                 delete poSpatialRef;
    2350                 :         }
    2351                 :     }
    2352                 :     else
    2353                 :     {
    2354                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2355                 :                  "SetMIFCoordSys() can be called only after dataset has been "
    2356               0 :                  "created and before any feature is set.");
    2357               0 :         return -1;
    2358                 :     }
    2359                 : 
    2360               0 :     return 0;
    2361                 : }
    2362                 : 
    2363                 : /**********************************************************************
    2364                 :  *                   TABFile::SetProjInfo()
    2365                 :  *
    2366                 :  * Set projection for a new file using a TABProjInfo structure.
    2367                 :  *
    2368                 :  * This function must be called after creating a new dataset and before any
    2369                 :  * feature can be written to it.
    2370                 :  *
    2371                 :  * This call will also trigger a lookup of default bounds for the specified
    2372                 :  * projection (except nonearth), and reset the m_bBoundsValid flag.
    2373                 :  *
    2374                 :  * Returns 0 on success, -1 on error.
    2375                 :  **********************************************************************/
    2376               1 : int TABFile::SetProjInfo(TABProjInfo *poPI)
    2377                 : {
    2378               1 :     if (m_eAccessMode != TABWrite)
    2379                 :     {
    2380                 :         CPLError(CE_Failure, CPLE_NotSupported,
    2381               0 :                  "SetProjInfo() can be used only with Write access.");
    2382               0 :         return -1;
    2383                 :     }
    2384                 : 
    2385                 :     /*-----------------------------------------------------------------
    2386                 :      * Check that dataset has been created but no feature set yet.
    2387                 :      *----------------------------------------------------------------*/
    2388               1 :     if (m_poMAPFile && m_nLastFeatureId < 1)
    2389                 :     {
    2390               1 :         if (m_poMAPFile->GetHeaderBlock()->SetProjInfo( poPI ) != 0)
    2391               0 :             return -1;
    2392                 :     }
    2393                 :     else
    2394                 :     {
    2395                 :         CPLError(CE_Failure, CPLE_AssertionFailed,
    2396                 :                  "SetProjInfo() can be called only after dataset has been "
    2397               0 :                  "created and before any feature is set.");
    2398               0 :         return -1;
    2399                 :     }
    2400                 : 
    2401                 :     /*-----------------------------------------------------------------
    2402                 :      * Lookup default bounds and reset m_bBoundsSet flag
    2403                 :      *----------------------------------------------------------------*/
    2404                 :     double dXMin, dYMin, dXMax, dYMax;
    2405                 : 
    2406               1 :     m_bBoundsSet = FALSE;
    2407               1 :     if (MITABLookupCoordSysBounds(poPI, dXMin, dYMin, dXMax, dYMax) == TRUE)
    2408                 :     {
    2409               1 :         SetBounds(dXMin, dYMin, dXMax, dYMax);
    2410                 :     }
    2411                 : 
    2412               1 :     return 0;
    2413                 : }
    2414                 : 
    2415                 : 
    2416                 : /************************************************************************/
    2417                 : /*                           TestCapability()                           */
    2418                 : /************************************************************************/
    2419                 : 
    2420               0 : int TABFile::TestCapability( const char * pszCap )
    2421                 : 
    2422                 : {
    2423               0 :     if( EQUAL(pszCap,OLCRandomRead) )
    2424               0 :         return TRUE;
    2425                 : 
    2426               0 :     else if( EQUAL(pszCap,OLCSequentialWrite) )
    2427               0 :         return m_eAccessMode == TABWrite;
    2428                 : 
    2429               0 :     else if( EQUAL(pszCap,OLCRandomWrite) )
    2430               0 :         return FALSE;
    2431                 : 
    2432               0 :     else if( EQUAL(pszCap,OLCFastFeatureCount) )
    2433                 :         return m_poFilterGeom == NULL
    2434               0 :             && m_poAttrQuery == NULL;
    2435                 : 
    2436               0 :     else if( EQUAL(pszCap,OLCFastSpatialFilter) )
    2437               0 :         return TRUE;
    2438                 : 
    2439               0 :     else if( EQUAL(pszCap,OLCFastGetExtent) )
    2440               0 :         return TRUE;
    2441                 : 
    2442               0 :     else if( EQUAL(pszCap,OLCCreateField) )
    2443               0 :         return TRUE;
    2444                 : 
    2445                 :     else 
    2446               0 :         return FALSE;
    2447                 : }
    2448                 : 
    2449                 : /**********************************************************************
    2450                 :  *                   TABFile::Dump()
    2451                 :  *
    2452                 :  * Dump block contents... available only in DEBUG mode.
    2453                 :  **********************************************************************/
    2454                 : #ifdef DEBUG
    2455                 : 
    2456               0 : void TABFile::Dump(FILE *fpOut /*=NULL*/)
    2457                 : {
    2458               0 :     if (fpOut == NULL)
    2459               0 :         fpOut = stdout;
    2460                 : 
    2461               0 :     fprintf(fpOut, "----- TABFile::Dump() -----\n");
    2462                 : 
    2463               0 :     if (m_poMAPFile == NULL)
    2464                 :     {
    2465               0 :         fprintf(fpOut, "File is not opened.\n");
    2466                 :     }
    2467                 :     else
    2468                 :     {
    2469               0 :         fprintf(fpOut, "File is opened: %s\n", m_pszFname);
    2470               0 :         fprintf(fpOut, "Associated TABLE file ...\n\n");
    2471               0 :         m_poDATFile->Dump(fpOut);
    2472               0 :         fprintf(fpOut, "... end of TABLE file dump.\n\n");
    2473               0 :         if( GetSpatialRef() != NULL )
    2474                 :         {
    2475                 :             char        *pszWKT;
    2476                 : 
    2477               0 :             GetSpatialRef()->exportToWkt( &pszWKT );
    2478               0 :             fprintf( fpOut, "SRS = %s\n", pszWKT );
    2479               0 :             OGRFree( pszWKT );                                          
    2480                 :         }
    2481               0 :         fprintf(fpOut, "Associated .MAP file ...\n\n");
    2482               0 :         m_poMAPFile->Dump(fpOut);
    2483               0 :         fprintf(fpOut, "... end of .MAP file dump.\n\n");
    2484                 : 
    2485                 :     }
    2486                 : 
    2487               0 :     fflush(fpOut);
    2488               0 : }
    2489                 : 
    2490                 : #endif // DEBUG

Generated by: LCOV version 1.7