Statistics
| Revision:

svn-gvsig-desktop / tags / v1_1_Build_1010 / libraries / libFMap / src / com / iver / cit / gvsig / fmap / drivers / dbf / DbaseFileHeader.java @ 12804

History | View | Annotate | Download (17.3 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
import java.nio.ByteOrder;
11
import java.util.Calendar;
12
import java.util.Date;
13

    
14
import com.iver.utiles.bigfile.BigByteBuffer2;
15

    
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
        private byte myLanguageID;
47

    
48
    /**
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
    public void readHeader(BigByteBuffer2 in) throws IOException {
336
        // 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
        myRecordLength = in.getShort(); //posicon 0h
365

    
366
        in.order(ByteOrder.BIG_ENDIAN);
367

    
368
        // skip the reserved bytes in the header.
369
        // 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

    
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
        int fieldOffset = 0;
384

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

    
509
        public byte getLanguageID() {
510
                return myLanguageID;
511
        }
512
}