dbfFileTable.java :  » Database-DBMS » TinySQL » com » sqlmagic » tinysql » Java Open Source

Java Open Source » Database DBMS » TinySQL 
TinySQL » com » sqlmagic » tinysql » dbfFileTable.java
/*
 *
 * Extension of tinySQLTable which manipulates dbf files.
 *
 * Copyright 1996 John Wiley & Sons, Inc. 
 * See the COPYING file for redistribution details.
 *
 * $Author: davis $
 * $Date: 2004/12/18 21:29:47 $
 * $Revision: 1.1 $
 *
 */
package com.sqlmagic.tinysql;

import java.util.*;
import java.lang.*;
import java.io.*;


/**
dBase read/write access <br> 
@author Brian Jepson <bjepson@home.com>
@author Marcel Ruff <ruff@swand.lake.de> Added write access to dBase and JDK 2 support
@author Thomas Morgner <mgs@sherito.org> Added caching for the current read row. A row
is now read only once and substrings are generated by each call to GetCol. Incredibly 
increased read speed when little memory is available and disks are slow.
*/
public class dbfFileTable extends tinySQLTable
{
   private String fullPath,fileName;   
   private DBFHeader dbfHeader = null; // the first 32 bytes of the dBase file
   private RandomAccessFile ftbl;  // access handle to the dBase file
   public boolean fileOpen=false;
   final static String dbfExtension = ".DBF";

   // dBase III column info offsets (one for each column):
   final static int FIELD_NAME_INDEX = 0;       // 0-10 column name, ASCIIZ - null padded
   final static int FIELD_TYPE_INDEX = 11;      // 11-11
   final static int IMU_INDEX = 12;             // 12-15 (in memory use)
   final static int FIELD_LENGTH_INDEX = 16;    // 16-16 (max field length = 254)
   final static int DECIMAL_COUNT_INDEX = 17;   // 17-17
   final static int FIELD_RESERVED_INDEX = 18;  // 18-31

/* 
 * The header section ends with carriage return CR.
 *
 * The data records have fixed length (from LENGTH_OF_REC_INDEX)
 * the field entries in a record have fixed length (from FIELD_LENGTH_INDEX)
 * all number and dates are stored as ASCII characters
 */
   final static int IS_DELETED_INDEX = 0;    // '*': is deleted
                                      // ' ': is not deleted
   final static char RECORD_IS_DELETED = '*';
   final static char RECORD_IS_NOT_DELETED = ' ';
/*
 * Current record
 */
   int currentRecordNumber = 0;      // current record, starts with 1!
/*
 * The cache holds a single row as string and is read only once,
 * and discarded when the cursor moves
 */
   private String currentRowCache = null;
/*
 * End of file flag
 */
   boolean eof = false;
/*
 *
 * Constructs a dbfFileTable. This is only called by getTable()
 * in dbfFile.java.
 *
 * @param dDir data directory
 * @param table_name the name of the table
 *
 */
   dbfFileTable( String dDir, String table_name ) throws tinySQLException 
   {
      int aliasAt;
      aliasAt = table_name.indexOf("->");
      if ( aliasAt > -1 ) 
      {
         table = table_name.substring(0,aliasAt);
         tableAlias = table_name.substring(aliasAt + 2);
      } else {
         table = table_name;
         tableAlias = table_name;
      }
/*
 *    The full path to the file
 */
      fileName = table;
      if (!fileName.toUpperCase().endsWith(dbfExtension) )
        fileName = fileName + dbfExtension;
      fullPath = dDir + File.separator + fileName;  
      if ( tinySQLGlobals.DEBUG ) 
         System.out.println("dbfFileTable: fileName=" + fileName + "\nTable="
         + table + "\nfullPath=" + fullPath);
/*
 *    Open the DBF file
 */
      column_info = open_dbf();
   }
/*
 * Check if the file is open.
 */
   public boolean isOpen()
   {
      return fileOpen;
   }
/*
 * Close method. Try not to call this until you are sure 
 * the object is about to go out of scope.
 */
   public void close() throws tinySQLException 
   {
      try 
      {
         if ( tinySQLGlobals.DEBUG )
            System.out.println("Closing " + toString());
         ftbl.close();
         fileOpen = false;
      } catch (IOException e) {
         throw new tinySQLException(e.getMessage());
      }
   }
/*
 * Returns the size of a column
 *
 * @param column name of the column
 * @see tinySQLTable#ColSize
 */
   public int ColSize(String colName) throws tinySQLException 
   {
      tsColumn coldef = getColumn(colName);
      return coldef.size;
   }
/*
 * Returns the number of rows in the table
 */
   public int GetRowCount()
   {
      return dbfHeader.numRecords;
   }
/*
 * Returns the decimal places for a column
 */
   public int ColDec(String colName) throws tinySQLException
   {
      tsColumn coldef = getColumn(colName);
      return coldef.decimalPlaces;
   }
/*
 * Returns the datatype of a column.
 *
 * @param column name of the column.
 * @see tinySQLTable#ColType
 *
 * @changed to return java.sql.Types
 */
   public int ColType(String colName) throws tinySQLException 
   {
      tsColumn coldef = getColumn(colName);
      return coldef.type;
   }
/*
 *  Get a column object for the named column.
 */
   public tsColumn getColumn(String colName) throws tinySQLException
   {
     int foundDot;
     String columnName;
     columnName = colName;
     foundDot = columnName.indexOf(".");
     if ( foundDot > -1 )
        columnName = columnName.substring(foundDot+1);
     columnName = tinySQLGlobals.getShortName(columnName);
     tsColumn coldef = (tsColumn) column_info.get(columnName);
     if ( coldef == (tsColumn)null )
        throw new tinySQLException("Column " + columnName + " does not"
        + " exist in table " + table);
     return coldef;
   }
/*
 * Updates the current row in the table.
 *
 * @param c Ordered Vector of column names
 * @param v Ordered Vector (must match order of c) of values
 * @see tinySQLTable#UpdateCurrentRow
 */
   public void UpdateCurrentRow(Vector c, Vector v) throws tinySQLException 
   {
/*
 *    The Vectors v and c are expected to have the 
 *    same number of elements. It is also expected
 *    that the elements correspond to each other,
 *    such that value 1 of Vector v corresponds to
 *    column 1 of Vector c, and so forth.
 */
      for (int i = 0; i < v.size(); i++) 
      {
/*
 *       Get the column name and the value, and
 *       invoke UpdateCol() to update it.
 */
         String column = ((String)c.elementAt(i)).toUpperCase();
         String value =  (String)v.elementAt(i);
         UpdateCol(column, value);
      }
   }
/*
 * Position the record pointer at the top of the table.
 *
 * @see tinySQLTable#GoTop
 */
   public void GoTop() throws tinySQLException 
   {
      currentRowCache = null;
      currentRecordNumber = 0;
      eof           = false;
   }
/*
 * Advance the record pointer to the next record.
 *
 * @see tinySQLTable#NextRecord
 */
   public boolean NextRecord() throws tinySQLException 
   {
      currentRowCache = null;
      if (currentRecordNumber < dbfHeader.numRecords) 
      {
         currentRecordNumber++;
         eof = false;
         return true;
      } else {
         eof = true;
         return false;
      }
   }
/*
 * Insert a row. If c or v == null, insert a blank row
 *
 * @param c Ordered Vector of column names
 * @param v Ordered Vector (must match order of c) of values
 * @see tinySQLTable#InsertRow()
 *
 */
   public void InsertRow(Vector c, Vector v) throws tinySQLException 
   {
      try 
      {
/*
 *       Go to the end of the file, then write out the not deleted indicator
 */
         ftbl.seek( ftbl.length() );
         ftbl.write(RECORD_IS_NOT_DELETED);
/*
 *       Write out a blank record
 */
         for (int i = 1; i < dbfHeader.recordLength; i++) 
         {
            ftbl.write(' ');
         }
         int numRec = (int)dbfHeader.numRecords + 1;
         currentRecordNumber = numRec;
         dbfHeader.setNumRecords(ftbl, numRec);
      } catch (Exception e) {
         if ( tinySQLGlobals.DEBUG ) e.printStackTrace();
         throw new tinySQLException(e.getMessage());
      }
      if (c != null && v != null)
         UpdateCurrentRow(c, v);
      else
         dbfHeader.setTimestamp(ftbl);
   }
/*
 * Retrieve a column's string value from the current row.
 *
 * @param column the column name
 * @see tinySQLTable#GetCol
 */
   public String GetCol(String colName) throws tinySQLException
   {
      int foundDot;
      String columnName;
      columnName = colName;
      foundDot = columnName.indexOf(".");
      if ( foundDot > -1 ) 
         columnName = columnName.substring(foundDot + 1);
      tsColumn coldef = (tsColumn) column_info.get(columnName);
      if (currentRowCache == null)
        currentRowCache = _GetCol(ftbl, dbfHeader, currentRecordNumber);

      return getColumn (coldef, currentRowCache);
   }
/*
 * Extracts a column from the given row. The row is given as a string.
 * If coldef is null, the special delete-flag is returned (Position 0 of a row).
 *
 * @param coldef the column definition, which tells what content to extract from the row
 * @param row the row as an string contains all column data
 * @returns a substring of row.
 */
   public static String getColumn (tsColumn coldef, String row)
   {
      if ( row == (String)null ) System.out.println("Row is null");
      else if ( row.length() == 0 ) System.out.println("Row has 0 length");
      if (coldef == null)
         return row.substring (0,1);
      return row.substring(coldef.position, coldef.position + coldef.size);
   }
/*
 * Retrieve a column's string value from the given row and given colName
 * @param ff the file handle
 * @param colName the column name
 * @param the wanted record (starts with 1)
 * @see tinySQLTable#GetCol
 *
 * @author Thomas Morgner <mgs@sherito.org> This function retrieves a
 * row, perhaps the name should changed to reflect the new function.
 */
   public static String _GetCol(RandomAccessFile ff, DBFHeader dbfHeader,
      int currentRow) throws tinySQLException 
   {
      try
      {
/*
 *       Seek the starting offset of the current record,
 *       as indicated by currentRow
 */
         ff.seek(dbfHeader.headerLength + (currentRow - 1) * dbfHeader.recordLength);
/*
 *       Fully read a byte array out to the length of the record and convert
 *       it into a String.
 */
         byte[] b = new byte[dbfHeader.recordLength];
         ff.readFully(b);
         return new String(b, Utils.encode); // "Cp437"
      } catch (Exception e) {
         throw new tinySQLException(e.getMessage());
      }
   }
/*
 * Update a single column.
 *
 * @param column the column name
 * @param value the String value with which update the column
 * @see tinySQLTable#UpdateCol
 *
 */
   public void UpdateCol( String colName, String value ) throws tinySQLException
   {
      String shortColumnName;
      try 
      {
/*
 *       If it's the pseudo column _DELETED, return
 */
         if (colName.equals("_DELETED")) return;
/*
 *       Retrieve the tsColumn object which corresponds to this column.
 */
         shortColumnName = tinySQLGlobals.getShortName(colName);
         tsColumn column = (tsColumn) column_info.get(shortColumnName);
         if (column == null)
            throw new tinySQLException("Can't update field=" + colName);
         if ( Utils.isDateColumn(column.type) )
         {
/*
 *          Convert non-blank dates to the standard YYYYMMDD format.
 */
            if ( value.trim().length() > 0 ) 
               value = UtilString.dateValue(value);
         }
/*
 *       Seek the starting offset of the current record,
 *       as indicated by currentRecordNumber
 */
         ftbl.seek(dbfHeader.headerLength + (currentRecordNumber - 1) * dbfHeader.recordLength + column.position);
/*
 *       Enforce the correct column length, transform to byte and write to file
 */
         value = Utils.forceToSize(value, column.size, " ");
         byte[] b = value.getBytes(Utils.encode);
         ftbl.write(b);
         dbfHeader.setTimestamp(ftbl);
      } catch (Exception e) {
         throw new tinySQLException(e.getMessage());
      }
   }
/*
 * Delete the current row.
 *
 * @see tinySQLTable#DeleteRow
 *
 */
   public void DeleteRow() throws tinySQLException
   {
      try
      {
         ftbl.seek(dbfHeader.headerLength + (currentRecordNumber - 1) * dbfHeader.recordLength);
         ftbl.write(RECORD_IS_DELETED);
      } catch (Exception e) {
         throw new tinySQLException(e.getMessage());
      }
   }
/* 
 * Is the current row deleted?
 *
 * @see tinySQLTable#isDeleted()
 */
   public boolean isDeleted() throws tinySQLException
   {
      return ((GetCol("_DELETED")).charAt(0) == RECORD_IS_DELETED); // "*";
   }
/*
 * Checks whether the row is deleted. 
 */
   public static boolean isDeleted(RandomAccessFile ff, DBFHeader dbfHeader, int currentRow) throws tinySQLException
   {
      char del = _GetCol(ff, dbfHeader, currentRow).charAt(0); // "_DELETED"
      return del == RECORD_IS_DELETED;
   }
/*
 * Check if record is marked as deleted
 * @param record the record string (the first byte '*' marks a deleted record)
 */
   public static boolean isDeleted(String record)
   {
      if (record.charAt(IS_DELETED_INDEX) == RECORD_IS_DELETED)
         return true;  // '*'
      return false;   // ' '
   }
/***************************************************************************
 *
 * End methods implemented from tinySQLTable.java
 * the rest of this stuff is private methods
 * for dbfFileTable.
 *
 * @return Length in bytes of one row or 0 if not known
 */
   public int getRecordLength() 
   {
      return dbfHeader.recordLength;
   }
   public String toString()
   {
      StringBuffer outputBuffer;
      outputBuffer = new StringBuffer();
      outputBuffer.append("Table " + table + ", path " + fullPath 
      + ", file " + ftbl.toString()); 
      return outputBuffer.toString();
   }
/*
 * opens a DBF file. This is based on Pratap Pereira's 
 * Xbase.pm perl module
 * @return column definition list (HashTable)
 *
 * @author Thomas Morgner <mgs@sherito.org> added check for
 * file exists, before the file is opened. Opening a non existing
 * file will create a new file, and we get errors while trying
 * to read the non-existend headers
 */
   Hashtable open_dbf() throws tinySQLException 
   {
      try
      {
         File f = new File (fullPath);
         if ( tinySQLGlobals.DEBUG ) 
            System.out.println("Try to open  " + f.getAbsolutePath());
         if (!f.exists() ) 
         {
             throw new tinySQLException ("Unable to open " + f.getAbsolutePath() 
            + " - does not exist. or can't be read.");
         } else if (!f.canRead () ) {
             throw new tinySQLException ("Unable to open " + f.getAbsolutePath()
            + " - file can't be read (permissions?).");
         }
         if (f.canWrite ())
         {
            ftbl = new RandomAccessFile(f, "rw");
         } else {
/*
 *          Open readonly if the file is not writeable. Needed for
 *          databases on CD-Rom
 */
            ftbl = new RandomAccessFile(f, "r");
         }
/*
 *       Read the first 32 bytes ...
 */
         dbfHeader = new DBFHeader(ftbl);
/*
 *       read the column info (each is a 32 byte bulk) ...
 */
         Hashtable coldef_list = new Hashtable();
         columnNameKeys = new Vector();
         int locn = 0; // offset of the current column
         for (int i = 1; i <= dbfHeader.numFields; i++) 
         {
            tsColumn coldef = dbfFile.readColdef(ftbl, table, i, locn);
            locn += coldef.size; // increment locn by the length of this field.
            coldef_list.put(coldef.name, coldef);
            columnNameKeys.addElement(coldef.name);
         }
         fileOpen = true;
         return coldef_list;
      } catch (Exception e) {
         if ( tinySQLGlobals.DEBUG ) e.printStackTrace();
         throw new tinySQLException(e.getMessage());
      }
   }
}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.