LTP GCOV extension - code coverage report
Current view: directory - ogr/ogrsf_frmts/mitab - mitab_tabfile.cpp
Test: gdal_filtered.info
Date: 2010-07-12 Instrumented lines: 731
Code covered: 46.8 % Executed lines: 342

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

Generated by: LTP GCOV extension version 1.5