svn-gvsig-desktop / tags / v1_0_2_RELEASE / libraries / libFMap / src / com / iver / cit / gvsig / fmap / drivers / dbf / DbaseFileHeader.java @ 11432
History | View | Annotate | Download (17.3 KB)
1 | 2462 | fjp | /*
|
---|---|---|---|
2 | * Created on 16-feb-2004
|
||
3 | *
|
||
4 | * To change the template for this generated file go to
|
||
5 | * Window>Preferences>Java>Code Generation>Code and Comments
|
||
6 | */
|
||
7 | package com.iver.cit.gvsig.fmap.drivers.dbf; |
||
8 | |||
9 | import java.io.IOException; |
||
10 | import java.nio.ByteOrder; |
||
11 | import java.util.Calendar; |
||
12 | import java.util.Date; |
||
13 | |||
14 | 4564 | fjp | import com.iver.utiles.bigfile.BigByteBuffer2; |
15 | 2462 | fjp | |
16 | |||
17 | /**
|
||
18 | * Class to represent the header of a Dbase III file. Creation date: (5/15/2001
|
||
19 | * 5:15:30 PM)
|
||
20 | */
|
||
21 | public class DbaseFileHeader { |
||
22 | // Constant for the size of a record
|
||
23 | private int FILE_DESCRIPTOR_SIZE = 32; |
||
24 | |||
25 | // type of the file, must be 03h
|
||
26 | private int myFileType = 0x03; |
||
27 | |||
28 | // Date the file was last updated.
|
||
29 | private Date myUpdateDate = new Date(); |
||
30 | |||
31 | // Number of records in the datafile
|
||
32 | private int myNumRecords = 0; |
||
33 | |||
34 | // Length of the header structure
|
||
35 | private int myHeaderLength; |
||
36 | |||
37 | // Length of the records
|
||
38 | private int myRecordLength; |
||
39 | |||
40 | // Number of fields in the record.
|
||
41 | private int myNumFields; |
||
42 | |||
43 | // collection of header records.
|
||
44 | private DbaseFieldDescriptor[] myFieldDescriptions; |
||
45 | |||
46 | 6990 | fjp | private byte myLanguageID; |
47 | |||
48 | 2462 | fjp | /**
|
49 | * DbaseFileHreader constructor comment.
|
||
50 | */
|
||
51 | public DbaseFileHeader() {
|
||
52 | super();
|
||
53 | } |
||
54 | |||
55 | /**
|
||
56 | * Add a column to this DbaseFileHeader. The type is one of (C N L or D)
|
||
57 | * character, number, logical(true/false), or date. The Field length is
|
||
58 | * the total length in bytes reserved for this column. The decimal count
|
||
59 | * only applies to numbers(N), and floating point values (F), and refers
|
||
60 | * to the number of characters to reserve after the decimal point.
|
||
61 | *
|
||
62 | * @param inFieldName DOCUMENT ME!
|
||
63 | * @param inFieldType DOCUMENT ME!
|
||
64 | * @param inFieldLength DOCUMENT ME!
|
||
65 | * @param inDecimalCount DOCUMENT ME!
|
||
66 | *
|
||
67 | * @throws Exception DOCUMENT ME!
|
||
68 | */
|
||
69 | public void addColumn(String inFieldName, char inFieldType, |
||
70 | int inFieldLength, int inDecimalCount) throws Exception { |
||
71 | if (inFieldLength <= 0) { |
||
72 | inFieldLength = 1;
|
||
73 | } |
||
74 | |||
75 | if (myFieldDescriptions == null) { |
||
76 | myFieldDescriptions = new DbaseFieldDescriptor[0]; |
||
77 | } |
||
78 | |||
79 | int tempLength = 1; // the length is used for the offset, and there is a * for deleted as the first byte |
||
80 | DbaseFieldDescriptor[] tempFieldDescriptors = new DbaseFieldDescriptor[myFieldDescriptions.length + |
||
81 | 1];
|
||
82 | |||
83 | for (int i = 0; i < myFieldDescriptions.length; i++) { |
||
84 | myFieldDescriptions[i].myFieldDataAddress = tempLength; |
||
85 | tempLength = tempLength + myFieldDescriptions[i].myFieldLength; |
||
86 | tempFieldDescriptors[i] = myFieldDescriptions[i]; |
||
87 | } |
||
88 | |||
89 | tempFieldDescriptors[myFieldDescriptions.length] = new DbaseFieldDescriptor();
|
||
90 | tempFieldDescriptors[myFieldDescriptions.length].myFieldLength = inFieldLength; |
||
91 | tempFieldDescriptors[myFieldDescriptions.length].myDecimalCount = inDecimalCount; |
||
92 | tempFieldDescriptors[myFieldDescriptions.length].myFieldDataAddress = tempLength; |
||
93 | |||
94 | // set the field name
|
||
95 | String tempFieldName = inFieldName;
|
||
96 | |||
97 | if (tempFieldName == null) { |
||
98 | tempFieldName = "NoName";
|
||
99 | } |
||
100 | |||
101 | if (tempFieldName.length() > 11) { |
||
102 | tempFieldName = tempFieldName.substring(0, 11); |
||
103 | warn("FieldName " + inFieldName +
|
||
104 | " is longer than 11 characters, truncating to " +
|
||
105 | tempFieldName); |
||
106 | } |
||
107 | |||
108 | tempFieldDescriptors[myFieldDescriptions.length].myFieldName = tempFieldName; |
||
109 | |||
110 | // the field type
|
||
111 | if ((inFieldType == 'C') || (inFieldType == 'c')) { |
||
112 | tempFieldDescriptors[myFieldDescriptions.length].myFieldType = 'C';
|
||
113 | |||
114 | if (inFieldLength > 254) { |
||
115 | warn("Field Length for " + inFieldName + " set to " + |
||
116 | inFieldLength + |
||
117 | " Which is longer than 254, not consistent with dbase III");
|
||
118 | } |
||
119 | } else if ((inFieldType == 'S') || (inFieldType == 's')) { |
||
120 | tempFieldDescriptors[myFieldDescriptions.length].myFieldType = 'C';
|
||
121 | warn("Field type for " + inFieldName +
|
||
122 | " set to S which is flat out wrong people!, I am setting this to C, in the hopes you meant character.");
|
||
123 | |||
124 | if (inFieldLength > 254) { |
||
125 | warn("Field Length for " + inFieldName + " set to " + |
||
126 | inFieldLength + |
||
127 | " Which is longer than 254, not consistent with dbase III");
|
||
128 | } |
||
129 | |||
130 | tempFieldDescriptors[myFieldDescriptions.length].myFieldLength = 8;
|
||
131 | } else if ((inFieldType == 'D') || (inFieldType == 'd')) { |
||
132 | tempFieldDescriptors[myFieldDescriptions.length].myFieldType = 'D';
|
||
133 | |||
134 | if (inFieldLength != 8) { |
||
135 | warn("Field Length for " + inFieldName + " set to " + |
||
136 | inFieldLength + " Setting to 8 digets YYYYMMDD");
|
||
137 | } |
||
138 | |||
139 | tempFieldDescriptors[myFieldDescriptions.length].myFieldLength = 8;
|
||
140 | } else if ((inFieldType == 'F') || (inFieldType == 'f')) { |
||
141 | tempFieldDescriptors[myFieldDescriptions.length].myFieldType = 'F';
|
||
142 | |||
143 | if (inFieldLength > 20) { |
||
144 | warn("Field Length for " + inFieldName + " set to " + |
||
145 | inFieldLength + |
||
146 | " Preserving length, but should be set to Max of 20 not valid for dbase IV, and UP specification, not present in dbaseIII.");
|
||
147 | } |
||
148 | } else if ((inFieldType == 'N') || (inFieldType == 'n')) { |
||
149 | tempFieldDescriptors[myFieldDescriptions.length].myFieldType = 'N';
|
||
150 | |||
151 | if (inFieldLength > 18) { |
||
152 | warn("Field Length for " + inFieldName + " set to " + |
||
153 | inFieldLength + |
||
154 | " Preserving length, but should be set to Max of 18 for dbase III specification.");
|
||
155 | } |
||
156 | |||
157 | if (inDecimalCount < 0) { |
||
158 | warn("Field Decimal Position for " + inFieldName + " set to " + |
||
159 | inDecimalCount + |
||
160 | " Setting to 0 no decimal data will be saved.");
|
||
161 | tempFieldDescriptors[myFieldDescriptions.length].myDecimalCount = 0;
|
||
162 | } |
||
163 | |||
164 | if (inDecimalCount > (inFieldLength - 1)) { |
||
165 | warn("Field Decimal Position for " + inFieldName + " set to " + |
||
166 | inDecimalCount + " Setting to " + (inFieldLength - 1) + |
||
167 | " no non decimal data will be saved.");
|
||
168 | tempFieldDescriptors[myFieldDescriptions.length].myDecimalCount = inFieldLength - |
||
169 | 1;
|
||
170 | } |
||
171 | } else if ((inFieldType == 'L') || (inFieldType == 'l')) { |
||
172 | tempFieldDescriptors[myFieldDescriptions.length].myFieldType = 'L';
|
||
173 | |||
174 | if (inFieldLength != 1) { |
||
175 | warn("Field Length for " + inFieldName + " set to " + |
||
176 | inFieldLength + |
||
177 | " Setting to length of 1 for logical fields.");
|
||
178 | } |
||
179 | |||
180 | tempFieldDescriptors[myFieldDescriptions.length].myFieldLength = 1;
|
||
181 | } else {
|
||
182 | throw new Exception("Undefined field type " + inFieldType + |
||
183 | " For column " + inFieldName);
|
||
184 | } |
||
185 | |||
186 | // the length of a record
|
||
187 | tempLength = tempLength + |
||
188 | tempFieldDescriptors[myFieldDescriptions.length].myFieldLength; |
||
189 | |||
190 | // set the new fields.
|
||
191 | myFieldDescriptions = tempFieldDescriptors; |
||
192 | myHeaderLength = 33 + (32 * myFieldDescriptions.length); |
||
193 | myNumFields = myFieldDescriptions.length; |
||
194 | myRecordLength = tempLength; |
||
195 | } |
||
196 | |||
197 | /**
|
||
198 | * Remove a column from this DbaseFileHeader.
|
||
199 | *
|
||
200 | * @param inFieldName DOCUMENT ME!
|
||
201 | *
|
||
202 | * @return index of the removed column, -1 if no found
|
||
203 | */
|
||
204 | public int removeColumn(String inFieldName) { |
||
205 | int retCol = -1; |
||
206 | int tempLength = 1; |
||
207 | DbaseFieldDescriptor[] tempFieldDescriptors = new DbaseFieldDescriptor[myFieldDescriptions.length - |
||
208 | 1];
|
||
209 | |||
210 | for (int i = 0, j = 0; i < myFieldDescriptions.length; i++) { |
||
211 | if (!inFieldName.equalsIgnoreCase(
|
||
212 | myFieldDescriptions[i].myFieldName.trim())) { |
||
213 | // if this is the last field and we still haven't found the
|
||
214 | // named field
|
||
215 | if ((i == j) && (i == (myFieldDescriptions.length - 1))) { |
||
216 | System.err.println("Could not find a field named '" + |
||
217 | inFieldName + "' for removal");
|
||
218 | |||
219 | return retCol;
|
||
220 | } |
||
221 | |||
222 | tempFieldDescriptors[j] = myFieldDescriptions[i]; |
||
223 | tempFieldDescriptors[j].myFieldDataAddress = tempLength; |
||
224 | tempLength += tempFieldDescriptors[j].myFieldLength; |
||
225 | |||
226 | // only increment j on non-matching fields
|
||
227 | j++; |
||
228 | } else {
|
||
229 | retCol = i; |
||
230 | } |
||
231 | } |
||
232 | |||
233 | // set the new fields.
|
||
234 | myFieldDescriptions = tempFieldDescriptors; |
||
235 | myHeaderLength = 33 + (32 * myFieldDescriptions.length); |
||
236 | myNumFields = myFieldDescriptions.length; |
||
237 | myRecordLength = tempLength; |
||
238 | |||
239 | return retCol;
|
||
240 | } |
||
241 | |||
242 | /**
|
||
243 | * DOCUMENT ME!
|
||
244 | *
|
||
245 | * @param inWarn DOCUMENT ME!
|
||
246 | */
|
||
247 | private void warn(String inWarn) { |
||
248 | //TODO Descomentar esto cuando tenga la clase warning support
|
||
249 | // warnings.warn(inWarn);
|
||
250 | } |
||
251 | |||
252 | /**
|
||
253 | * Return the Field Descriptor for the given field.
|
||
254 | *
|
||
255 | * @param inIndex DOCUMENT ME!
|
||
256 | *
|
||
257 | * @return DOCUMENT ME!
|
||
258 | */
|
||
259 | public DbaseFieldDescriptor getFieldDescription(int inIndex) { |
||
260 | return myFieldDescriptions[inIndex];
|
||
261 | } |
||
262 | |||
263 | // Retrieve the length of the field at the given index
|
||
264 | public int getFieldLength(int inIndex) { |
||
265 | return myFieldDescriptions[inIndex].myFieldLength;
|
||
266 | } |
||
267 | |||
268 | // Retrieve the location of the decimal point within the field.
|
||
269 | public int getFieldDecimalCount(int inIndex) { |
||
270 | return myFieldDescriptions[inIndex].myDecimalCount;
|
||
271 | } |
||
272 | |||
273 | // Retrieve the Name of the field at the given index
|
||
274 | public String getFieldName(int inIndex) { |
||
275 | return myFieldDescriptions[inIndex].myFieldName;
|
||
276 | } |
||
277 | |||
278 | // Retrieve the type of field at the given index
|
||
279 | public char getFieldType(int inIndex) { |
||
280 | return myFieldDescriptions[inIndex].myFieldType;
|
||
281 | } |
||
282 | |||
283 | /**
|
||
284 | * Return the date this file was last updated.
|
||
285 | *
|
||
286 | * @return DOCUMENT ME!
|
||
287 | */
|
||
288 | public Date getLastUpdateDate() { |
||
289 | return myUpdateDate;
|
||
290 | } |
||
291 | |||
292 | /**
|
||
293 | * Return the number of fields in the records.
|
||
294 | *
|
||
295 | * @return DOCUMENT ME!
|
||
296 | */
|
||
297 | public int getNumFields() { |
||
298 | return myNumFields;
|
||
299 | } |
||
300 | |||
301 | /**
|
||
302 | * Return the number of records in the file
|
||
303 | *
|
||
304 | * @return DOCUMENT ME!
|
||
305 | */
|
||
306 | public int getNumRecords() { |
||
307 | return myNumRecords;
|
||
308 | } |
||
309 | |||
310 | /**
|
||
311 | * Return the length of the records in bytes.
|
||
312 | *
|
||
313 | * @return DOCUMENT ME!
|
||
314 | */
|
||
315 | public int getRecordLength() { |
||
316 | return myRecordLength;
|
||
317 | } |
||
318 | |||
319 | /**
|
||
320 | * Return the length of the header
|
||
321 | *
|
||
322 | * @return DOCUMENT ME!
|
||
323 | */
|
||
324 | public int getHeaderLength() { |
||
325 | return myHeaderLength;
|
||
326 | } |
||
327 | |||
328 | /**
|
||
329 | * Read the header data from the DBF file.
|
||
330 | *
|
||
331 | * @param in DOCUMENT ME!
|
||
332 | *
|
||
333 | * @throws IOException DOCUMENT ME!
|
||
334 | */
|
||
335 | 4564 | fjp | public void readHeader(BigByteBuffer2 in) throws IOException { |
336 | 2462 | fjp | // type of file.
|
337 | myFileType = in.get(); |
||
338 | |||
339 | if (myFileType != 0x03) { |
||
340 | throw new IOException("Unsupported DBF file Type " + |
||
341 | Integer.toHexString(myFileType));
|
||
342 | } |
||
343 | |||
344 | // parse the update date information.
|
||
345 | int tempUpdateYear = (int) in.get(); |
||
346 | int tempUpdateMonth = (int) in.get(); |
||
347 | int tempUpdateDay = (int) in.get(); |
||
348 | tempUpdateYear = tempUpdateYear + 1900;
|
||
349 | |||
350 | Calendar c = Calendar.getInstance(); |
||
351 | c.set(Calendar.YEAR, tempUpdateYear);
|
||
352 | c.set(Calendar.MONTH, tempUpdateMonth - 1); |
||
353 | c.set(Calendar.DATE, tempUpdateDay);
|
||
354 | myUpdateDate = c.getTime(); |
||
355 | |||
356 | // read the number of records.
|
||
357 | in.order(ByteOrder.LITTLE_ENDIAN);
|
||
358 | myNumRecords = in.getInt(); |
||
359 | |||
360 | // read the length of the header structure.
|
||
361 | myHeaderLength = in.getShort(); |
||
362 | |||
363 | // read the length of a record
|
||
364 | 6990 | fjp | myRecordLength = in.getShort(); //posicon 0h
|
365 | 2462 | fjp | |
366 | in.order(ByteOrder.BIG_ENDIAN);
|
||
367 | |||
368 | // skip the reserved bytes in the header.
|
||
369 | 6990 | fjp | // in.position(in.position() + 20);
|
370 | |||
371 | // Leemos el byte de language
|
||
372 | in.position(29);
|
||
373 | myLanguageID = in.get(); |
||
374 | |||
375 | // Posicionamos para empezar a leer los campos.
|
||
376 | in.position(32);
|
||
377 | 2462 | fjp | |
378 | // calculate the number of Fields in the header
|
||
379 | myNumFields = (myHeaderLength - FILE_DESCRIPTOR_SIZE - 1) / FILE_DESCRIPTOR_SIZE;
|
||
380 | |||
381 | // read all of the header records
|
||
382 | myFieldDescriptions = new DbaseFieldDescriptor[myNumFields];
|
||
383 | 3217 | fjp | int fieldOffset = 0; |
384 | 2462 | fjp | |
385 | for (int i = 0; i < myNumFields; i++) { |
||
386 | myFieldDescriptions[i] = new DbaseFieldDescriptor();
|
||
387 | |||
388 | // read the field name
|
||
389 | byte[] buffer = new byte[11]; |
||
390 | in.get(buffer); |
||
391 | myFieldDescriptions[i].myFieldName = new String(buffer); |
||
392 | |||
393 | // read the field type
|
||
394 | myFieldDescriptions[i].myFieldType = (char) in.get();
|
||
395 | |||
396 | // read the field data address, offset from the start of the record.
|
||
397 | myFieldDescriptions[i].myFieldDataAddress = in.getInt(); |
||
398 | |||
399 | // read the field length in bytes
|
||
400 | int tempLength = (int) in.get(); |
||
401 | |||
402 | if (tempLength < 0) { |
||
403 | tempLength = tempLength + 256;
|
||
404 | } |
||
405 | |||
406 | myFieldDescriptions[i].myFieldLength = tempLength; |
||
407 | |||
408 | // read the field decimal count in bytes
|
||
409 | myFieldDescriptions[i].myDecimalCount = (int) in.get();
|
||
410 | |||
411 | 3217 | fjp | // NUEVO: Calculamos los offsets aqu? para no
|
412 | // tener que recalcular cada vez que nos piden
|
||
413 | // algo.
|
||
414 | myFieldDescriptions[i].myFieldDataAddress = fieldOffset; |
||
415 | fieldOffset += tempLength; |
||
416 | // Fin NUEVO
|
||
417 | 2462 | fjp | // read the reserved bytes.
|
418 | in.position(in.position() + 14);
|
||
419 | } |
||
420 | |||
421 | // Last byte is a marker for the end of the field definitions.
|
||
422 | in.get(); |
||
423 | } |
||
424 | |||
425 | /**
|
||
426 | * Set the number of records in the file
|
||
427 | *
|
||
428 | * @param inNumRecords DOCUMENT ME!
|
||
429 | */
|
||
430 | protected void setNumRecords(int inNumRecords) { |
||
431 | myNumRecords = inNumRecords; |
||
432 | } |
||
433 | |||
434 | /*
|
||
435 | * Write the header data to the DBF file.
|
||
436 | *
|
||
437 | * @param out DOCUMENT ME!
|
||
438 | *
|
||
439 | * @throws Exception DOCUMENT ME!
|
||
440 | *
|
||
441 | public void writeHeader(LEDataOutputStream out) throws Exception {
|
||
442 | // write the output file type.
|
||
443 | out.writeByte(myFileType);
|
||
444 | // write the date stuff
|
||
445 | Calendar c = Calendar.getInstance();
|
||
446 | c.setTime(new Date());
|
||
447 | out.writeByte(c.get(Calendar.YEAR) - 1900);
|
||
448 | out.writeByte(c.get(Calendar.MONTH) + 1);
|
||
449 | out.writeByte(c.get(Calendar.DAY_OF_MONTH));
|
||
450 | // write the number of records in the datafile.
|
||
451 | out.writeInt(myNumRecords);
|
||
452 | // write the length of the header structure.
|
||
453 | out.writeShort(myHeaderLength);
|
||
454 | // write the length of a record
|
||
455 | out.writeShort(myRecordLength);
|
||
456 | // write the reserved bytes in the header
|
||
457 | for (int i = 0; i < 20; i++)
|
||
458 | out.writeByte(0);
|
||
459 | // write all of the header records
|
||
460 | int tempOffset = 0;
|
||
461 | for (int i = 0; i < myFieldDescriptions.length; i++) {
|
||
462 | // write the field name
|
||
463 | for (int j = 0; j < 11; j++) {
|
||
464 | if (myFieldDescriptions[i].myFieldName.length() > j) {
|
||
465 | out.writeByte((int) myFieldDescriptions[i].myFieldName.charAt(
|
||
466 | j));
|
||
467 | } else {
|
||
468 | out.writeByte(0);
|
||
469 | }
|
||
470 | }
|
||
471 | // write the field type
|
||
472 | out.writeByte(myFieldDescriptions[i].myFieldType);
|
||
473 | // write the field data address, offset from the start of the record.
|
||
474 | out.writeInt(tempOffset);
|
||
475 | tempOffset += myFieldDescriptions[i].myFieldLength;
|
||
476 | // write the length of the field.
|
||
477 | out.writeByte(myFieldDescriptions[i].myFieldLength);
|
||
478 | // write the decimal count.
|
||
479 | out.writeByte(myFieldDescriptions[i].myDecimalCount);
|
||
480 | // write the reserved bytes.
|
||
481 | for (int j = 0; j < 14; j++)
|
||
482 | out.writeByte(0);
|
||
483 | }
|
||
484 | // write the end of the field definitions marker
|
||
485 | out.writeByte(0x0D);
|
||
486 | }
|
||
487 | */
|
||
488 | |||
489 | /**
|
||
490 | * Class for holding the information assicated with a record.
|
||
491 | */
|
||
492 | class DbaseFieldDescriptor { |
||
493 | // Field Name
|
||
494 | String myFieldName;
|
||
495 | |||
496 | // Field Type (C N L D F or M)
|
||
497 | char myFieldType;
|
||
498 | |||
499 | // Field Data Address offset from the start of the record.
|
||
500 | int myFieldDataAddress;
|
||
501 | |||
502 | // Length of the data in bytes
|
||
503 | int myFieldLength;
|
||
504 | |||
505 | // Field decimal count in Binary, indicating where the decimal is
|
||
506 | int myDecimalCount;
|
||
507 | } |
||
508 | 6990 | fjp | |
509 | public byte getLanguageID() { |
||
510 | return myLanguageID;
|
||
511 | } |
||
512 | 2462 | fjp | } |