Statistics
| Revision:

svn-gvsig-desktop / trunk / libraries / libFMap / src / com / iver / cit / gvsig / fmap / drivers / dbf / DbaseFileHeader.java @ 2462

History | View | Annotate | Download (16.7 KB)

1
/*
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

    
11
import java.nio.ByteBuffer;
12
import java.nio.ByteOrder;
13

    
14
import java.util.Calendar;
15
import java.util.Date;
16

    
17
import com.iver.utiles.bigfile.BigByteBuffer;
18

    
19

    
20
/**
21
 * Class to represent the header of a Dbase III file. Creation date: (5/15/2001
22
 * 5:15:30 PM)
23
 */
24
public class DbaseFileHeader {
25
    // Constant for the size of a record
26
    private int FILE_DESCRIPTOR_SIZE = 32;
27

    
28
    // type of the file, must be 03h
29
    private int myFileType = 0x03;
30

    
31
    // Date the file was last updated.
32
    private Date myUpdateDate = new Date();
33

    
34
    // Number of records in the datafile
35
    private int myNumRecords = 0;
36

    
37
    // Length of the header structure
38
    private int myHeaderLength;
39

    
40
    // Length of the records
41
    private int myRecordLength;
42

    
43
    // Number of fields in the record.
44
    private int myNumFields;
45

    
46
    // collection of header records.
47
    private DbaseFieldDescriptor[] myFieldDescriptions;
48

    
49
    /**
50
     * DbaseFileHreader constructor comment.
51
     */
52
    public DbaseFileHeader() {
53
        super();
54
    }
55

    
56
    /**
57
     * Add a column to this DbaseFileHeader. The type is one of (C N L or D)
58
     * character, number, logical(true/false), or date. The Field length is
59
     * the total length in bytes reserved for this column. The decimal count
60
     * only applies to numbers(N), and floating point values (F), and refers
61
     * to the number of characters to reserve after the decimal point.
62
     *
63
     * @param inFieldName DOCUMENT ME!
64
     * @param inFieldType DOCUMENT ME!
65
     * @param inFieldLength DOCUMENT ME!
66
     * @param inDecimalCount DOCUMENT ME!
67
     *
68
     * @throws Exception DOCUMENT ME!
69
     */
70
    public void addColumn(String inFieldName, char inFieldType,
71
        int inFieldLength, int inDecimalCount) throws Exception {
72
        if (inFieldLength <= 0) {
73
            inFieldLength = 1;
74
        }
75

    
76
        if (myFieldDescriptions == null) {
77
            myFieldDescriptions = new DbaseFieldDescriptor[0];
78
        }
79

    
80
        int tempLength = 1; // the length is used for the offset, and there is a * for deleted as the first byte
81
        DbaseFieldDescriptor[] tempFieldDescriptors = new DbaseFieldDescriptor[myFieldDescriptions.length +
82
            1];
83

    
84
        for (int i = 0; i < myFieldDescriptions.length; i++) {
85
            myFieldDescriptions[i].myFieldDataAddress = tempLength;
86
            tempLength = tempLength + myFieldDescriptions[i].myFieldLength;
87
            tempFieldDescriptors[i] = myFieldDescriptions[i];
88
        }
89

    
90
        tempFieldDescriptors[myFieldDescriptions.length] = new DbaseFieldDescriptor();
91
        tempFieldDescriptors[myFieldDescriptions.length].myFieldLength = inFieldLength;
92
        tempFieldDescriptors[myFieldDescriptions.length].myDecimalCount = inDecimalCount;
93
        tempFieldDescriptors[myFieldDescriptions.length].myFieldDataAddress = tempLength;
94

    
95
        // set the field name
96
        String tempFieldName = inFieldName;
97

    
98
        if (tempFieldName == null) {
99
            tempFieldName = "NoName";
100
        }
101

    
102
        if (tempFieldName.length() > 11) {
103
            tempFieldName = tempFieldName.substring(0, 11);
104
            warn("FieldName " + inFieldName +
105
                " is longer than 11 characters, truncating to " +
106
                tempFieldName);
107
        }
108

    
109
        tempFieldDescriptors[myFieldDescriptions.length].myFieldName = tempFieldName;
110

    
111
        // the field type
112
        if ((inFieldType == 'C') || (inFieldType == 'c')) {
113
            tempFieldDescriptors[myFieldDescriptions.length].myFieldType = 'C';
114

    
115
            if (inFieldLength > 254) {
116
                warn("Field Length for " + inFieldName + " set to " +
117
                    inFieldLength +
118
                    " Which is longer than 254, not consistent with dbase III");
119
            }
120
        } else if ((inFieldType == 'S') || (inFieldType == 's')) {
121
            tempFieldDescriptors[myFieldDescriptions.length].myFieldType = 'C';
122
            warn("Field type for " + inFieldName +
123
                " set to S which is flat out wrong people!, I am setting this to C, in the hopes you meant character.");
124

    
125
            if (inFieldLength > 254) {
126
                warn("Field Length for " + inFieldName + " set to " +
127
                    inFieldLength +
128
                    " Which is longer than 254, not consistent with dbase III");
129
            }
130

    
131
            tempFieldDescriptors[myFieldDescriptions.length].myFieldLength = 8;
132
        } else if ((inFieldType == 'D') || (inFieldType == 'd')) {
133
            tempFieldDescriptors[myFieldDescriptions.length].myFieldType = 'D';
134

    
135
            if (inFieldLength != 8) {
136
                warn("Field Length for " + inFieldName + " set to " +
137
                    inFieldLength + " Setting to 8 digets YYYYMMDD");
138
            }
139

    
140
            tempFieldDescriptors[myFieldDescriptions.length].myFieldLength = 8;
141
        } else if ((inFieldType == 'F') || (inFieldType == 'f')) {
142
            tempFieldDescriptors[myFieldDescriptions.length].myFieldType = 'F';
143

    
144
            if (inFieldLength > 20) {
145
                warn("Field Length for " + inFieldName + " set to " +
146
                    inFieldLength +
147
                    " Preserving length, but should be set to Max of 20 not valid for dbase IV, and UP specification, not present in dbaseIII.");
148
            }
149
        } else if ((inFieldType == 'N') || (inFieldType == 'n')) {
150
            tempFieldDescriptors[myFieldDescriptions.length].myFieldType = 'N';
151

    
152
            if (inFieldLength > 18) {
153
                warn("Field Length for " + inFieldName + " set to " +
154
                    inFieldLength +
155
                    " Preserving length, but should be set to Max of 18 for dbase III specification.");
156
            }
157

    
158
            if (inDecimalCount < 0) {
159
                warn("Field Decimal Position for " + inFieldName + " set to " +
160
                    inDecimalCount +
161
                    " Setting to 0 no decimal data will be saved.");
162
                tempFieldDescriptors[myFieldDescriptions.length].myDecimalCount = 0;
163
            }
164

    
165
            if (inDecimalCount > (inFieldLength - 1)) {
166
                warn("Field Decimal Position for " + inFieldName + " set to " +
167
                    inDecimalCount + " Setting to " + (inFieldLength - 1) +
168
                    " no non decimal data will be saved.");
169
                tempFieldDescriptors[myFieldDescriptions.length].myDecimalCount = inFieldLength -
170
                    1;
171
            }
172
        } else if ((inFieldType == 'L') || (inFieldType == 'l')) {
173
            tempFieldDescriptors[myFieldDescriptions.length].myFieldType = 'L';
174

    
175
            if (inFieldLength != 1) {
176
                warn("Field Length for " + inFieldName + " set to " +
177
                    inFieldLength +
178
                    " Setting to length of 1 for logical fields.");
179
            }
180

    
181
            tempFieldDescriptors[myFieldDescriptions.length].myFieldLength = 1;
182
        } else {
183
            throw new Exception("Undefined field type " + inFieldType +
184
                " For column " + inFieldName);
185
        }
186

    
187
        // the length of a record
188
        tempLength = tempLength +
189
            tempFieldDescriptors[myFieldDescriptions.length].myFieldLength;
190

    
191
        // set the new fields.
192
        myFieldDescriptions = tempFieldDescriptors;
193
        myHeaderLength = 33 + (32 * myFieldDescriptions.length);
194
        myNumFields = myFieldDescriptions.length;
195
        myRecordLength = tempLength;
196
    }
197

    
198
    /**
199
     * Remove a column from this DbaseFileHeader.
200
     *
201
     * @param inFieldName DOCUMENT ME!
202
     *
203
     * @return index of the removed column, -1 if no found
204
     */
205
    public int removeColumn(String inFieldName) {
206
        int retCol = -1;
207
        int tempLength = 1;
208
        DbaseFieldDescriptor[] tempFieldDescriptors = new DbaseFieldDescriptor[myFieldDescriptions.length -
209
            1];
210

    
211
        for (int i = 0, j = 0; i < myFieldDescriptions.length; i++) {
212
            if (!inFieldName.equalsIgnoreCase(
213
                        myFieldDescriptions[i].myFieldName.trim())) {
214
                // if this is the last field and we still haven't found the
215
                // named field
216
                if ((i == j) && (i == (myFieldDescriptions.length - 1))) {
217
                    System.err.println("Could not find a field named '" +
218
                        inFieldName + "' for removal");
219

    
220
                    return retCol;
221
                }
222

    
223
                tempFieldDescriptors[j] = myFieldDescriptions[i];
224
                tempFieldDescriptors[j].myFieldDataAddress = tempLength;
225
                tempLength += tempFieldDescriptors[j].myFieldLength;
226

    
227
                // only increment j on non-matching fields
228
                j++;
229
            } else {
230
                retCol = i;
231
            }
232
        }
233

    
234
        // set the new fields.
235
        myFieldDescriptions = tempFieldDescriptors;
236
        myHeaderLength = 33 + (32 * myFieldDescriptions.length);
237
        myNumFields = myFieldDescriptions.length;
238
        myRecordLength = tempLength;
239

    
240
        return retCol;
241
    }
242

    
243
    /**
244
     * DOCUMENT ME!
245
     *
246
     * @param inWarn DOCUMENT ME!
247
     */
248
    private void warn(String inWarn) {
249
        //TODO Descomentar esto cuando tenga la clase warning support
250
        //            warnings.warn(inWarn);
251
    }
252

    
253
    /**
254
     * Return the Field Descriptor for the given field.
255
     *
256
     * @param inIndex DOCUMENT ME!
257
     *
258
     * @return DOCUMENT ME!
259
     */
260
    public DbaseFieldDescriptor getFieldDescription(int inIndex) {
261
        return myFieldDescriptions[inIndex];
262
    }
263

    
264
    // Retrieve the length of the field at the given index
265
    public int getFieldLength(int inIndex) {
266
        return myFieldDescriptions[inIndex].myFieldLength;
267
    }
268

    
269
    // Retrieve the location of the decimal point within the field.
270
    public int getFieldDecimalCount(int inIndex) {
271
        return myFieldDescriptions[inIndex].myDecimalCount;
272
    }
273

    
274
    // Retrieve the Name of the field at the given index
275
    public String getFieldName(int inIndex) {
276
        return myFieldDescriptions[inIndex].myFieldName;
277
    }
278

    
279
    // Retrieve the type of field at the given index
280
    public char getFieldType(int inIndex) {
281
        return myFieldDescriptions[inIndex].myFieldType;
282
    }
283

    
284
    /**
285
     * Return the date this file was last updated.
286
     *
287
     * @return DOCUMENT ME!
288
     */
289
    public Date getLastUpdateDate() {
290
        return myUpdateDate;
291
    }
292

    
293
    /**
294
     * Return the number of fields in the records.
295
     *
296
     * @return DOCUMENT ME!
297
     */
298
    public int getNumFields() {
299
        return myNumFields;
300
    }
301

    
302
    /**
303
     * Return the number of records in the file
304
     *
305
     * @return DOCUMENT ME!
306
     */
307
    public int getNumRecords() {
308
        return myNumRecords;
309
    }
310

    
311
    /**
312
     * Return the length of the records in bytes.
313
     *
314
     * @return DOCUMENT ME!
315
     */
316
    public int getRecordLength() {
317
        return myRecordLength;
318
    }
319

    
320
    /**
321
     * Return the length of the header
322
     *
323
     * @return DOCUMENT ME!
324
     */
325
    public int getHeaderLength() {
326
        return myHeaderLength;
327
    }
328

    
329
    /**
330
     * Read the header data from the DBF file.
331
     *
332
     * @param in DOCUMENT ME!
333
     *
334
     * @throws IOException DOCUMENT ME!
335
     */
336
    public void readHeader(BigByteBuffer in) throws IOException {
337
        // type of file.
338
        myFileType = in.get();
339

    
340
        if (myFileType != 0x03) {
341
            throw new IOException("Unsupported DBF file Type " +
342
                Integer.toHexString(myFileType));
343
        }
344

    
345
        // parse the update date information.
346
        int tempUpdateYear = (int) in.get();
347
        int tempUpdateMonth = (int) in.get();
348
        int tempUpdateDay = (int) in.get();
349
        tempUpdateYear = tempUpdateYear + 1900;
350

    
351
        Calendar c = Calendar.getInstance();
352
        c.set(Calendar.YEAR, tempUpdateYear);
353
        c.set(Calendar.MONTH, tempUpdateMonth - 1);
354
        c.set(Calendar.DATE, tempUpdateDay);
355
        myUpdateDate = c.getTime();
356

    
357
        // read the number of records.
358
        in.order(ByteOrder.LITTLE_ENDIAN);
359
        myNumRecords = in.getInt();
360

    
361
        // read the length of the header structure.
362
        myHeaderLength = in.getShort();
363

    
364
        // read the length of a record
365
        myRecordLength = in.getShort();
366

    
367
        in.order(ByteOrder.BIG_ENDIAN);
368

    
369
        // skip the reserved bytes in the header.
370
        in.position(in.position() + 20);
371

    
372
        // calculate the number of Fields in the header
373
        myNumFields = (myHeaderLength - FILE_DESCRIPTOR_SIZE - 1) / FILE_DESCRIPTOR_SIZE;
374

    
375
        // read all of the header records
376
        myFieldDescriptions = new DbaseFieldDescriptor[myNumFields];
377

    
378
        for (int i = 0; i < myNumFields; i++) {
379
            myFieldDescriptions[i] = new DbaseFieldDescriptor();
380

    
381
            // read the field name
382
            byte[] buffer = new byte[11];
383
            in.get(buffer);
384
            myFieldDescriptions[i].myFieldName = new String(buffer);
385

    
386
            // read the field type
387
            myFieldDescriptions[i].myFieldType = (char) in.get();
388

    
389
            // read the field data address, offset from the start of the record.
390
            myFieldDescriptions[i].myFieldDataAddress = in.getInt();
391

    
392
            // read the field length in bytes
393
            int tempLength = (int) in.get();
394

    
395
            if (tempLength < 0) {
396
                tempLength = tempLength + 256;
397
            }
398

    
399
            myFieldDescriptions[i].myFieldLength = tempLength;
400

    
401
            // read the field decimal count in bytes
402
            myFieldDescriptions[i].myDecimalCount = (int) in.get();
403

    
404
            // read the reserved bytes.
405
            in.position(in.position() + 14);
406
        }
407

    
408
        // Last byte is a marker for the end of the field definitions.
409
        in.get();
410
    }
411

    
412
    /**
413
     * Set the number of records in the file
414
     *
415
     * @param inNumRecords DOCUMENT ME!
416
     */
417
    protected void setNumRecords(int inNumRecords) {
418
        myNumRecords = inNumRecords;
419
    }
420

    
421
    /*
422
     * Write the header data to the DBF file.
423
     *
424
     * @param out DOCUMENT ME!
425
     *
426
     * @throws Exception DOCUMENT ME!
427
     *
428
           public void writeHeader(LEDataOutputStream out) throws Exception {
429
               // write the output file type.
430
               out.writeByte(myFileType);
431
               // write the date stuff
432
               Calendar c = Calendar.getInstance();
433
               c.setTime(new Date());
434
               out.writeByte(c.get(Calendar.YEAR) - 1900);
435
               out.writeByte(c.get(Calendar.MONTH) + 1);
436
               out.writeByte(c.get(Calendar.DAY_OF_MONTH));
437
               // write the number of records in the datafile.
438
               out.writeInt(myNumRecords);
439
               // write the length of the header structure.
440
               out.writeShort(myHeaderLength);
441
               // write the length of a record
442
               out.writeShort(myRecordLength);
443
               // write the reserved bytes in the header
444
               for (int i = 0; i < 20; i++)
445
                   out.writeByte(0);
446
               // write all of the header records
447
               int tempOffset = 0;
448
               for (int i = 0; i < myFieldDescriptions.length; i++) {
449
                   // write the field name
450
                   for (int j = 0; j < 11; j++) {
451
                       if (myFieldDescriptions[i].myFieldName.length() > j) {
452
                           out.writeByte((int) myFieldDescriptions[i].myFieldName.charAt(
453
                                   j));
454
                       } else {
455
                           out.writeByte(0);
456
                       }
457
                   }
458
                   // write the field type
459
                   out.writeByte(myFieldDescriptions[i].myFieldType);
460
                   // write the field data address, offset from the start of the record.
461
                   out.writeInt(tempOffset);
462
                   tempOffset += myFieldDescriptions[i].myFieldLength;
463
                   // write the length of the field.
464
                   out.writeByte(myFieldDescriptions[i].myFieldLength);
465
                   // write the decimal count.
466
                   out.writeByte(myFieldDescriptions[i].myDecimalCount);
467
                   // write the reserved bytes.
468
                   for (int j = 0; j < 14; j++)
469
                       out.writeByte(0);
470
               }
471
               // write the end of the field definitions marker
472
               out.writeByte(0x0D);
473
           }
474
     */
475

    
476
    /**
477
     * Class for holding the information assicated with a record.
478
     */
479
    class DbaseFieldDescriptor {
480
        // Field Name
481
        String myFieldName;
482

    
483
        // Field Type (C N L D F or M)
484
        char myFieldType;
485

    
486
        // Field Data Address offset from the start of the record.
487
        int myFieldDataAddress;
488

    
489
        // Length of the data in bytes
490
        int myFieldLength;
491

    
492
        // Field decimal count in Binary, indicating where the decimal is
493
        int myDecimalCount;
494
    }
495
}