LCOV - code coverage report
Current view: directory - ogr/ogrsf_frmts/mitab - mitab_tabfile.cpp (source / functions) Found Hit Coverage
Test: gdal_filtered.info Lines: 815 403 49.4 %
Date: 2012-04-28 Functions: 35 19 54.3 %

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

Generated by: LCOV version 1.7