KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sqlmagic > tinysql > DBFHeader


1 /*
2  * DBFHeader.java
3  * tinySQL, manipulation of the first 32 bytes of a dBase III header
4  *
5  * $Author: davis $
6  * $Date: 2004/12/18 21:31:13 $
7  * $Revision: 1.1 $
8  *
9  */

10 package com.sqlmagic.tinysql;
11
12 import java.util.*;
13 import java.lang.*;
14 import java.io.*;
15
16
17 /**
18 dBase III header read/write access (bytes 0 - 31) <br>
19 The column definitions are not read
20 @author Brian Jepson <bjepson@home.com>
21 @author Marcel Ruff <ruff@swand.lake.de> Added write access to dBase and JDK 2 support
22 */

23 public class DBFHeader
24 {
25   String JavaDoc tableName = null;
26   short file_type = 0; // = 0x03 without .DBT, 0x83 with .DBT (memo file)
27
short file_update_year = 0;
28   short file_update_month = 0;
29   short file_update_day = 0;
30   int numFields = 0; // number of column definitions
31
int numRecords = 0; // number of data records
32
int headerLength = 0; // in bytes
33
int recordLength = 0; // length in bytes of one data row, including the beginning delete flag-byte
34

35   /*
36      The dBase III header consists of 32 byte bulks:
37      0-32 primary header info
38      32 bytes for each column info (n times)
39      The number of columns is calculated from the headerLength
40   */

41   final static int BULK_SIZE = 32; //
42
final static int FLAG_INDEX = 0; // = 0x03 without .DBT, 0x83 with .DBT (memo file)
43
final static int DATE_INDEX = 1; // 1=YY 2=MM 3=DD (last update)
44
final static int NUMBER_OF_REC_INDEX = 4; // 4-7
45
final static int LENGTH_OF_HEADER_INDEX = 8; // 8-9
46
final static int LENGTH_OF_REC_INDEX = 10; // 8-11
47
final static int RESERVED_INDEX = 12; // 12-31
48

49
50   /**
51    * Constructs a DBFHeader, read the data from file <br>
52    * You need to supply an open file handle to read from
53    * @param ff open file handle for read access
54    */

55   DBFHeader(RandomAccessFile ff) throws tinySQLException
56   {
57     try {
58       ff.seek(FLAG_INDEX);
59       file_type = Utils.fixByte(ff.readByte());
60
61       // get the last update date
62
file_update_year = Utils.fixByte(ff.readByte());
63       file_update_month = Utils.fixByte(ff.readByte());
64       file_update_day = Utils.fixByte(ff.readByte());
65
66       // a byte array to hold little-endian long data
67
//
68
byte[] b = new byte[4];
69
70       // read that baby in...
71
//
72
ff.readFully(b);
73
74       // convert the byte array into a long (really a double)
75
// 4-7 number of records
76
numRecords = (int)Utils.vax_to_long(b);
77
78       // a byte array to hold little-endian short data
79
//
80
b = new byte[2];
81
82       // get the data position (where it starts in the file)
83
// 8-9 Length of header
84
ff.readFully(b);
85       headerLength = Utils.vax_to_short(b);
86
87       // find out the length of the data portion
88
// 10-11 Length of Record
89
ff.readFully(b);
90       recordLength = Utils.vax_to_short(b);
91
92       // calculate the number of fields
93
//
94
numFields = (int) (headerLength - 33)/32;
95
96       // skip the next 20 bytes - looks like this is not needed...
97
//ff.skipBytes(20);
98
// 12-31 reserved
99

100       Utils.log("HEADER=" + this.toString());
101
102     } catch (Exception JavaDoc e) {
103       throw new tinySQLException(e.getMessage());
104     }
105   }
106
107
108   /**
109    * Constructs a DBFHeader, read the data from file <br>
110    * You need to supply an open file handle to read from
111    * @param numFields number of Columns
112    * @param recordLength sum of all column.size plus 1 byte (delete flag)
113    */

114   DBFHeader(int numFields, int recordLength) throws tinySQLException
115   {
116     this.numFields = numFields;
117     this.recordLength = recordLength;
118     Utils.log("DBFHeader", "numFields=" + numFields + " recordLength=" + recordLength);
119   }
120
121
122   /**
123   Create new dBase file and write the first 32 bytes<br>
124   the file remains opened
125   @return file handle with read/write access
126   */

127   public RandomAccessFile create(String JavaDoc dataDir, String JavaDoc tableName
128                                  ) throws tinySQLException
129   {
130     this.tableName = tableName;
131
132     try {
133       // make the data directory, if it needs to be make
134
//
135
mkDataDirectory(dataDir);
136
137       // perform an implicit drop table.
138
//
139
dropTable(dataDir, tableName);
140
141       String JavaDoc fullPath = dataDir + File.separator + tableName + dbfFileTable.dbfExtension;
142       RandomAccessFile ff = new RandomAccessFile(fullPath, "rw");
143
144       write(ff);
145
146       // ftbl.close();
147

148       return ff;
149
150     } catch (Exception JavaDoc e) {
151       throw new tinySQLException(e.getMessage());
152     }
153   }
154
155
156   /**
157   write the first 32 bytes to file
158   */

159   public void write(RandomAccessFile ff) throws tinySQLException
160   {
161     try {
162       //-----------------------------
163
// write out the primary header
164
ff.seek(FLAG_INDEX);
165       ff.writeByte((byte)0x03);
166
167       setTimestamp(ff); // set current date YY MM DD (dBase is not Y2K save)
168

169       setNumRecords(ff, 0);
170
171       setHeaderLength(ff, numFields);
172
173       setRecordLength(ff, recordLength);
174
175       setReserved(ff);
176
177     } catch (Exception JavaDoc e) {
178       throw new tinySQLException(e.getMessage());
179     }
180   }
181
182
183   /*
184    * Make the data directory unless it already exists
185    */

186   void mkDataDirectory(String JavaDoc dataDir) throws NullPointerException JavaDoc
187   {
188     File dd = new File( dataDir );
189
190     if (!dd.exists()) {
191       dd.mkdir();
192     }
193   }
194
195   public void setTimestamp(RandomAccessFile ff) throws tinySQLException
196   {
197     try {
198       java.util.Calendar JavaDoc cal = java.util.Calendar.getInstance();
199       cal.setTime(new java.util.Date JavaDoc());
200       int dd = cal.get(java.util.Calendar.DAY_OF_MONTH);
201       int mm = cal.get(java.util.Calendar.MONTH) + 1;
202       int yy = cal.get(java.util.Calendar.YEAR);
203       yy = yy % 100; // Y2K problem: only 2 digits
204
ff.seek(DATE_INDEX);
205       ff.write(yy);
206       ff.write(mm);
207       ff.write(dd);
208     } catch (Exception JavaDoc e) {
209       throw new tinySQLException(e.getMessage());
210     }
211   }
212
213
214   /**
215   Update the header (index 4-7) with the new number of records
216   @param New number of records
217   */

218   public void setNumRecords(RandomAccessFile ff, int numRecords) throws tinySQLException
219   {
220     this.numRecords = numRecords;
221     writeNumRecords(ff, numRecords);
222   }
223
224
225   /**
226   Update the header (index 4-7) with the new number of records <br>
227   This is the static variant (use it if you don't want to obtain
228   a DBFHeader instance
229   @param New number of records
230   */

231   public static void writeNumRecords(RandomAccessFile ff, int numRecords) throws tinySQLException
232   {
233     try {
234       byte[] b = Utils.intToLittleEndian(numRecords);
235       ff.seek(NUMBER_OF_REC_INDEX);
236       ff.write(b);
237     } catch (Exception JavaDoc e) {
238       throw new tinySQLException(e.getMessage());
239     }
240   }
241
242
243   /**
244   Update the header (index 8-9) with the new number of records
245   @param numFields number of columns (used to calculate header length)
246   */

247   public void setHeaderLength(RandomAccessFile ff, int numFields) throws tinySQLException
248   {
249     this.numFields = numFields;
250     try {
251       int headerLength = (DBFHeader.BULK_SIZE+1) + numFields * DBFHeader.BULK_SIZE;
252       ff.seek(DBFHeader.LENGTH_OF_HEADER_INDEX);
253       ff.write(Utils.shortToLittleEndian((short)headerLength));
254     } catch (Exception JavaDoc e) {
255       throw new tinySQLException(e.getMessage());
256     }
257   }
258
259
260   /**
261   Update the header (index 10-11) with the length of one record
262   @param recordLength Length of one data record (row)
263   */

264   public void setRecordLength(RandomAccessFile ff, int recordLength) throws tinySQLException
265   {
266     this.recordLength = recordLength;
267     try {
268       ff.seek(DBFHeader.LENGTH_OF_REC_INDEX);
269       ff.write(Utils.shortToLittleEndian((short)recordLength));
270     } catch (Exception JavaDoc e) {
271       throw new tinySQLException(e.getMessage());
272     }
273   }
274
275
276   /**
277   Update the header (index 10-11) with the length of one record
278   @param recordLength Length of one data record (row)
279   */

280   public void setReserved(RandomAccessFile ff) throws tinySQLException
281   {
282     try {
283       ff.seek(DBFHeader.RESERVED_INDEX);
284       byte[] reserved = Utils.forceToSize(null,
285              DBFHeader.BULK_SIZE - DBFHeader.RESERVED_INDEX,
286              (byte)0);
287       ff.write(reserved); // padding with \0!
288
} catch (Exception JavaDoc e) {
289       throw new tinySQLException(e.getMessage());
290     }
291   }
292
293
294   static void dropTable (String JavaDoc dataDir, String JavaDoc fname) throws tinySQLException {
295     try {
296
297       // delFile(fname);
298
Utils.delFile(dataDir, fname + dbfFileTable.dbfExtension);
299
300     } catch (Exception JavaDoc e) {
301       throw new tinySQLException(e.getMessage());
302     }
303   }
304
305
306 }
307
308
Popular Tags