📄 mitab_datfile.cpp
字号:
for(i=0; i<m_numFields; i++)
{
m_nRecordSize += m_pasFieldDef[i].byLength;
}
/*-------------------------------------------------------------
* Create m_poRecordBlock the size of a data record.
*------------------------------------------------------------*/
m_nBlockSize = m_nRecordSize;
CPLAssert( m_poRecordBlock == NULL );
m_poRecordBlock = new TABRawBinBlock(m_eAccessMode, FALSE);
m_poRecordBlock->InitNewBlock(m_fp, m_nBlockSize);
m_poRecordBlock->SetFirstBlockPtr(m_nFirstRecordPtr);
/*-------------------------------------------------------------
* Make sure this init. will be performed only once
*------------------------------------------------------------*/
m_bWriteHeaderInitialized = TRUE;
return 0;
}
/**********************************************************************
* TABDATFile::WriteHeader()
*
* Init the header members to be ready to write the header and data records
* to a newly created data file.
*
* Returns 0 on success, -1 on error.
**********************************************************************/
int TABDATFile::WriteHeader()
{
int i;
if (m_eAccessMode != TABWrite)
{
CPLError(CE_Failure, CPLE_NotSupported,
"WriteHeader() can be used only with Write access.");
return -1;
}
if (!m_bWriteHeaderInitialized)
InitWriteHeader();
/*------------------------------------------------------------
* Create a single block that will be used to generate the whole header.
*-----------------------------------------------------------*/
if (m_poHeaderBlock == NULL)
m_poHeaderBlock = new TABRawBinBlock(m_eAccessMode, TRUE);
m_poHeaderBlock->InitNewBlock(m_fp, m_nFirstRecordPtr, 0);
/*------------------------------------------------------------
* First 32 bytes: main header block
*-----------------------------------------------------------*/
m_poHeaderBlock->WriteByte(0x03); // Table type ??? 0x03
// __TODO__ Write the correct update date value
m_poHeaderBlock->WriteByte(99); // Last update year
m_poHeaderBlock->WriteByte(9); // Last update month
m_poHeaderBlock->WriteByte(9); // Last update day
m_poHeaderBlock->WriteInt32(m_numRecords);
m_poHeaderBlock->WriteInt16(m_nFirstRecordPtr);
m_poHeaderBlock->WriteInt16(m_nRecordSize);
m_poHeaderBlock->WriteZeros(20); // Pad rest with zeros
/*-------------------------------------------------------------
* Field definitions follow. Each field def is 32 bytes.
*------------------------------------------------------------*/
for(i=0; i<m_numFields; i++)
{
m_poHeaderBlock->WriteBytes(11, (GByte*)m_pasFieldDef[i].szName);
m_poHeaderBlock->WriteByte(m_pasFieldDef[i].cType);
m_poHeaderBlock->WriteInt32(0); // Skip Bytes 12-15
m_poHeaderBlock->WriteByte(m_pasFieldDef[i].byLength);
m_poHeaderBlock->WriteByte(m_pasFieldDef[i].byDecimals);
m_poHeaderBlock->WriteZeros(14); // Pad rest with zeros
}
/*-------------------------------------------------------------
* Header ends with a 0x0d character.
*------------------------------------------------------------*/
m_poHeaderBlock->WriteByte(0x0d);
/*-------------------------------------------------------------
* Write the block to the file and return.
*------------------------------------------------------------*/
return m_poHeaderBlock->CommitToFile();
}
/**********************************************************************
* TABDATFile::GetNumFields()
*
* Return the number of fields in this table.
*
* Returns a value >= 0 on success, -1 on error.
**********************************************************************/
int TABDATFile::GetNumFields()
{
return m_numFields;
}
/**********************************************************************
* TABDATFile::GetNumRecords()
*
* Return the number of records in this table.
*
* Returns a value >= 0 on success, -1 on error.
**********************************************************************/
int TABDATFile::GetNumRecords()
{
return m_numRecords;
}
/**********************************************************************
* TABDATFile::GetRecordBlock()
*
* Return a TABRawBinBlock reference positioned at the beginning of the
* specified record and ready to read (or write) field values from/to it.
* In read access, the returned block is guaranteed to contain at least one
* full record of data, and in write access, it is at least big enough to
* hold one full record.
*
* Note that record ids are positive and start at 1.
*
* In Write access, CommitRecordToFile() MUST be called after the
* data items have been written to the record, otherwise the record
* will never make it to the file.
*
* Returns a reference to the TABRawBinBlock on success or NULL on error.
* The returned pointer is a reference to a block object owned by this
* TABDATFile object and should not be freed by the caller.
**********************************************************************/
TABRawBinBlock *TABDATFile::GetRecordBlock(int nRecordId)
{
m_bCurRecordDeletedFlag = FALSE;
if (m_eAccessMode == TABRead)
{
/*-------------------------------------------------------------
* READ ACCESS
*------------------------------------------------------------*/
int nFileOffset;
nFileOffset = m_nFirstRecordPtr+(nRecordId-1)*m_nRecordSize;
/*-------------------------------------------------------------
* Move record block pointer to the right location
*------------------------------------------------------------*/
if ( m_poRecordBlock == NULL ||
nRecordId < 1 || nRecordId > m_numRecords ||
m_poRecordBlock->GotoByteInFile(nFileOffset) != 0 )
{
CPLError(CE_Failure, CPLE_FileIO,
"Failed reading .DAT record block for record #%d in %s",
nRecordId, m_pszFname);
return NULL;
}
/*-------------------------------------------------------------
* The first char of the record is a ' ' for an active record, or
* '*' for a deleted one.
* In the case of a deleted record, we simply return default
* values for each attribute... this is what MapInfo seems to do
* when it takes a .TAB with deleted records and exports it to .MIF
*------------------------------------------------------------*/
if (m_poRecordBlock->ReadByte() != ' ')
{
m_bCurRecordDeletedFlag = TRUE;
}
}
else if (m_eAccessMode == TABWrite && nRecordId > 0)
{
/*-------------------------------------------------------------
* WRITE ACCESS
*------------------------------------------------------------*/
int nFileOffset;
/*-------------------------------------------------------------
* Before writing the first record, we must generate the file
* header. We will also initialize class members such as record
* size, etc. and will create m_poRecordBlock.
*------------------------------------------------------------*/
if (!m_bWriteHeaderInitialized)
{
WriteHeader();
}
m_numRecords = MAX(nRecordId, m_numRecords);
nFileOffset = m_nFirstRecordPtr+(nRecordId-1)*m_nRecordSize;
m_poRecordBlock->InitNewBlock(m_fp, m_nRecordSize, nFileOffset);
/*-------------------------------------------------------------
* The first char of the record is the active/deleted flag.
* Automatically set it to ' ' (active).
*------------------------------------------------------------*/
m_poRecordBlock->WriteByte(' ');
}
m_nCurRecordId = nRecordId;
return m_poRecordBlock;
}
/**********************************************************************
* TABDATFile::CommitRecordToFile()
*
* Commit the data record previously initialized with GetRecordBlock()
* to the file. This function must be called after writing the data
* values to a record otherwise the record will never make it to the
* file.
*
* Returns 0 on success, -1 on error.
**********************************************************************/
int TABDATFile::CommitRecordToFile()
{
if (m_eAccessMode != TABWrite || m_poRecordBlock == NULL)
return -1;
return m_poRecordBlock->CommitToFile();
}
/**********************************************************************
* TABDATFile::ValidateFieldInfoFromTAB()
*
* Check that the value read from the .TAB file by the caller are
* consistent with what is found in the .DAT header.
*
* Note that field ids are positive and start at 0.
*
* We have to use this function when opening a file for reading since
* the .DAT file does not contain the full field types information...
* a .DAT file is actually a .DBF file in which the .DBF types are
* handled in a special way... type 'C' fields are used to store binary
* values for most MapInfo types.
*
* For TABTableDBF, we actually have no validation to do since all types
* are stored as strings internally, so we'll just convert from string.
*
* Returns a value >= 0 if OK, -1 on error.
**********************************************************************/
int TABDATFile::ValidateFieldInfoFromTAB(int iField, const char *pszName,
TABFieldType eType,
int nWidth, int nPrecision)
{
int i = iField; // Just to make things shorter
CPLAssert(m_pasFieldDef);
if (m_pasFieldDef == NULL || iField < 0 || iField >= m_numFields)
{
CPLError(CE_Failure, CPLE_FileIO,
"Invalid field %d (%s) in .TAB header. %s contains only %d fields.",
iField+1, pszName, m_pszFname, m_pasFieldDef? m_numFields:0);
return -1;
}
/*-----------------------------------------------------------------
* We used to check that the .TAB field name matched the .DAT
* name stored internally, but apparently some tools that rename table
* field names only update the .TAB file and not the .DAT, so we won't
* do that name validation any more... we'll just check the type.
*
* With TABTableNative, we have to validate the field sizes as well
* because .DAT files use char fields to store binary values.
* With TABTableDBF, no need to validate field type since all
* fields are stored as strings internally.
*----------------------------------------------------------------*/
if ((m_eTableType == TABTableNative &&
((eType == TABFChar && (m_pasFieldDef[i].cType != 'C' ||
m_pasFieldDef[i].byLength != nWidth )) ||
(eType == TABFDecimal && (m_pasFieldDef[i].cType != 'N' ||
m_pasFieldDef[i].byLength != nWidth||
m_pasFieldDef[i].byDecimals!=nPrecision)) ||
(eType == TABFInteger && (m_pasFieldDef[i].cType != 'C' ||
m_pasFieldDef[i].byLength != 4 )) ||
(eType == TABFSmallInt && (m_pasFieldDef[i].cType != 'C' ||
m_pasFieldDef[i].byLength != 2 )) ||
(eType == TABFFloat && (m_pasFieldDef[i].cType != 'C' ||
m_pasFieldDef[i].byLength != 8 )) ||
(eType == TABFDate && (m_pasFieldDef[i].cType != 'C' ||
m_pasFieldDef[i].byLength != 4 )) ||
(eType == TABFTime && (m_pasFieldDef[i].cType != 'C' ||
m_pasFieldDef[i].byLength != 4 )) ||
(eType == TABFDateTime && (m_pasFieldDef[i].cType != 'C' ||
m_pasFieldDef[i].byLength != 8 )) ||
(eType == TABFLogical && (m_pasFieldDef[i].cType != 'L' ||
m_pasFieldDef[i].byLength != 1 )) ) ))
{
CPLError(CE_Failure, CPLE_FileIO,
"Definition of field %d (%s) from .TAB file does not match "
"what is found in %s (name=%s, type=%c, width=%d, prec=%d)",
iField+1, pszName, m_pszFname,
m_pasFieldDef[i].szName, m_pasFieldDef[i].cType,
m_pasFieldDef[i].byLength, m_pasFieldDef[i].byDecimals);
return -1;
}
m_pasFieldDef[i].eTABType = eType;
return 0;
}
/**********************************************************************
* TABDATFile::AddField()
*
* Create a new field (column) in a newly created table. This function
* must be called after the file has been opened, but before writing the
* first record.
*
* Returns the new field index (a value >= 0) if OK, -1 on error.
**********************************************************************/
int TABDATFile::AddField(const char *pszName, TABFieldType eType,
int nWidth, int nPrecision /*=0*/)
{
if (m_eAccessMode != TABWrite || m_bWriteHeaderInitialized ||
m_eTableType != TABTableNative)
{
CPLError(CE_Failure, CPLE_NotSupported,
"Addition of new table fields is not supported after the "
"first data item has been written.");
return -1;
}
/*-----------------------------------------------------------------
* Validate field width... must be <= 254
*----------------------------------------------------------------*/
if (nWidth > 254)
{
CPLError(CE_Failure, CPLE_IllegalArg,
"Invalid size (%d) for field '%s'. "
"Size must be 254 or less.", nWidth, pszName);
return -1;
}
/*-----------------------------------------------------------------
* Map fields with width=0 (variable length in OGR) to a valid default
*----------------------------------------------------------------*/
if (eType == TABFDecimal && nWidth == 0)
nWidth=20;
else if (nWidth == 0)
nWidth=254; /* char fields */
if (m_numFields < 0)
m_numFields = 0;
m_numFields++;
m_pasFieldDef = (TABDATFieldDef*)CPLRealloc(m_pasFieldDef,
m_numFields*sizeof(TABDATFieldDef));
strncpy(m_pasFieldDef[m_numFields-1].szName, pszName, 10);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -