Statistics
| Revision:

svn-gvsig-desktop / trunk / org.gvsig.desktop / org.gvsig.desktop.compat.cdc / org.gvsig.fmap.dal / org.gvsig.fmap.dal.file / org.gvsig.fmap.dal.file.dbf / src / main / java / org / gvsig / fmap / dal / store / dbf / utils / DbaseFileHeader.java @ 41746

History | View | Annotate | Download (26 KB)

1
/**
2
 * gvSIG. Desktop Geographic Information System.
3
 *
4
 * Copyright (C) 2007-2013 gvSIG Association.
5
 *
6
 * This program is free software; you can redistribute it and/or
7
 * modify it under the terms of the GNU General Public License
8
 * as published by the Free Software Foundation; either version 3
9
 * of the License, or (at your option) any later version.
10
 *
11
 * This program is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with this program; if not, write to the Free Software
18
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
19
 * MA  02110-1301, USA.
20
 *
21
 * For any additional information, do not hesitate to contact us
22
 * at info AT gvsig.com, or visit our website www.gvsig.com.
23
 */
24
package org.gvsig.fmap.dal.store.dbf.utils;
25

    
26
import java.io.IOException;
27
import java.io.UnsupportedEncodingException;
28
import java.nio.ByteBuffer;
29
import java.nio.ByteOrder;
30
import java.nio.channels.FileChannel;
31
import java.nio.charset.Charset;
32
import java.util.ArrayList;
33
import java.util.Calendar;
34
import java.util.Date;
35
import java.util.Iterator;
36
import java.util.List;
37
import java.util.Set;
38
import java.util.SortedMap;
39

    
40
import org.gvsig.fmap.dal.DataTypes;
41
import org.gvsig.fmap.dal.exception.UnsupportedVersionException;
42
import org.gvsig.fmap.dal.feature.FeatureAttributeDescriptor;
43
import org.gvsig.fmap.dal.feature.FeatureType;
44
import org.gvsig.fmap.dal.feature.exception.AttributeFeatureTypeNotSuportedException;
45
import org.gvsig.tools.ToolsLocator;
46
import org.gvsig.utils.bigfile.BigByteBuffer2;
47

    
48

    
49

    
50
/**
51
 * Class to represent the header of a Dbase III file. Creation date: (5/15/2001
52
 * 5:15:30 PM)
53
 */
54
public class DbaseFileHeader {
55
    // Constant for the size of a record
56
    private int FILE_DESCRIPTOR_SIZE = 32;
57

    
58
        // type of the file, must be 03h
59
        private static final byte MAGIC = 0x03;
60

    
61
        private static final int MINIMUM_HEADER = 33;
62

    
63
    // type of the file, must be 03h
64
    private int myFileType = 0x03;
65

    
66
    // Date the file was last updated.
67
    private Date myUpdateDate = new Date();
68

    
69
    // Number of records in the datafile
70
        private int myNumRecords = 0;
71

    
72
    // Length of the header structure
73
    private int myHeaderLength;
74

    
75
    /**
76
     * Length of the records. Set to 1 as the default value as if there is
77
     * not any defined column, at least the deleted status initial byte
78
     * is taken into account.
79
     */
80
    private int myRecordLength = 1;
81

    
82
    // Number of fields in the record.
83
    private int myNumFields;
84

    
85
    // collection of header records.
86
    private DbaseFieldDescriptor[] myFieldDescriptions;
87

    
88
        private byte myLanguageID;
89
        
90
        private List<String>   encodingSupportedByString = null;
91

    
92
    /**
93
     * DbaseFileHreader constructor comment.
94
     */
95
    public DbaseFileHeader() {
96
        super();
97
        
98
        encodingSupportedByString = new ArrayList<String>();
99
                SortedMap<String, Charset> m = Charset.availableCharsets();
100
                Set<String> k = m.keySet();
101
                Iterator<String> it = k.iterator();
102
                while(it.hasNext()) {
103
                        encodingSupportedByString.add(it.next());
104
                }
105
    }
106

    
107
    /**
108
     * Add a column to this DbaseFileHeader. The type is one of (C N L or D)
109
     * character, number, logical(true/false), or date. The Field length is
110
     * the total length in bytes reserved for this column. The decimal count
111
     * only applies to numbers(N), and floating point values (F), and refers
112
     * to the number of characters to reserve after the decimal point.
113
     *
114
     * @param inFieldName DOCUMENT ME!
115
     * @param inFieldType DOCUMENT ME!
116
     * @param inFieldLength DOCUMENT ME!
117
     * @param inDecimalCount DOCUMENT ME!
118
     * @throws BadFieldDriverException
119
     *
120
     * @throws Exception DOCUMENT ME!
121
     */
122
    public void addColumn(String inFieldName, char inFieldType,
123
        int inFieldLength, int inDecimalCount)
124
                        throws AttributeFeatureTypeNotSuportedException {
125
        if (inFieldLength <= 0) {
126
            inFieldLength = 1;
127
        }
128

    
129
        if (myFieldDescriptions == null) {
130
            myFieldDescriptions = new DbaseFieldDescriptor[0];
131
        }
132

    
133
        int tempLength = 1; // the length is used for the offset, and there is a * for deleted as the first byte
134
        DbaseFieldDescriptor[] tempFieldDescriptors = new DbaseFieldDescriptor[myFieldDescriptions.length +
135
            1];
136

    
137
        for (int i = 0; i < myFieldDescriptions.length; i++) {
138
            myFieldDescriptions[i].myFieldDataAddress = tempLength;
139
            tempLength = tempLength + myFieldDescriptions[i].myFieldLength;
140
            tempFieldDescriptors[i] = myFieldDescriptions[i];
141
        }
142

    
143
        tempFieldDescriptors[myFieldDescriptions.length] = new DbaseFieldDescriptor();
144
        tempFieldDescriptors[myFieldDescriptions.length].myFieldLength = inFieldLength;
145
        tempFieldDescriptors[myFieldDescriptions.length].myDecimalCount = inDecimalCount;
146
        tempFieldDescriptors[myFieldDescriptions.length].myFieldDataAddress = tempLength;
147

    
148
        // set the field name
149
        String tempFieldName = inFieldName;
150

    
151
        if (tempFieldName == null) {
152
            tempFieldName = "NoName";
153
        }
154

    
155
        if (tempFieldName.length() > DbaseFile.MAX_FIELD_NAME_LENGTH) {
156
            tempFieldName = tempFieldName.substring(0, DbaseFile.MAX_FIELD_NAME_LENGTH);
157
            warn("FieldName " + inFieldName +
158
                " is longer than "+DbaseFile.MAX_FIELD_NAME_LENGTH+" characters, truncating to " +
159
                tempFieldName);
160
        }
161

    
162
        tempFieldDescriptors[myFieldDescriptions.length].myFieldName = tempFieldName;
163
        tempFieldDescriptors[myFieldDescriptions.length].myFieldName_trim = tempFieldName
164
                                .trim();
165

    
166
        // the field type
167
        if ((inFieldType == 'C') || (inFieldType == 'c')) {
168
            tempFieldDescriptors[myFieldDescriptions.length].myFieldType = 'C';
169

    
170
            if (inFieldLength > 254) {
171
                warn("Field Length for " + inFieldName + " set to " +
172
                    inFieldLength +
173
                    " Which is longer than 254, not consistent with dbase III");
174
            }
175
        } else if ((inFieldType == 'S') || (inFieldType == 's')) {
176
            tempFieldDescriptors[myFieldDescriptions.length].myFieldType = 'C';
177
            warn("Field type for " + inFieldName +
178
                " set to S which is flat out wrong people!, I am setting this to C, in the hopes you meant character.");
179

    
180
            if (inFieldLength > 254) {
181
                warn("Field Length for " + inFieldName + " set to " +
182
                    inFieldLength +
183
                    " Which is longer than 254, not consistent with dbase III");
184
            }
185

    
186
            tempFieldDescriptors[myFieldDescriptions.length].myFieldLength = 8;
187
        } else if ((inFieldType == 'D') || (inFieldType == 'd')) {
188
            tempFieldDescriptors[myFieldDescriptions.length].myFieldType = 'D';
189

    
190
            if (inFieldLength != 8) {
191
                warn("Field Length for " + inFieldName + " set to " +
192
                    inFieldLength + " Setting to 8 digets YYYYMMDD");
193
            }
194

    
195
            tempFieldDescriptors[myFieldDescriptions.length].myFieldLength = 8;
196
        } else if ((inFieldType == 'F') || (inFieldType == 'f')) {
197
            tempFieldDescriptors[myFieldDescriptions.length].myFieldType = 'F';
198

    
199
            if (inFieldLength > 20) {
200
                warn("Field Length for " + inFieldName + " set to " +
201
                    inFieldLength +
202
                    " Preserving length, but should be set to Max of 20 not valid for dbase IV, and UP specification, not present in dbaseIII.");
203
            }
204
        } else if ((inFieldType == 'N') || (inFieldType == 'n')) {
205
            tempFieldDescriptors[myFieldDescriptions.length].myFieldType = 'N';
206

    
207
            if (inFieldLength > 18) {
208
                warn("Field Length for " + inFieldName + " set to " +
209
                    inFieldLength +
210
                    " Preserving length, but should be set to Max of 18 for dbase III specification.");
211
            }
212

    
213
            if (inDecimalCount < 0) {
214
                warn("Field Decimal Position for " + inFieldName + " set to " +
215
                    inDecimalCount +
216
                    " Setting to 0 no decimal data will be saved.");
217
                tempFieldDescriptors[myFieldDescriptions.length].myDecimalCount = 0;
218
            }
219
//
220
//            if (inDecimalCount > (inFieldLength - 1)) {
221
//                warn("Field Decimal Position for " + inFieldName + " set to " +
222
//                    inDecimalCount + " Setting to " + (inFieldLength - 1) +
223
//                    " no non decimal data will be saved.");
224
//                tempFieldDescriptors[myFieldDescriptions.length].myDecimalCount = inFieldLength -
225
//                    1;
226
//            }
227
        } else if ((inFieldType == 'L') || (inFieldType == 'l')) {
228
            tempFieldDescriptors[myFieldDescriptions.length].myFieldType = 'L';
229

    
230
            if (inFieldLength != 1) {
231
                warn("Field Length for " + inFieldName + " set to " +
232
                    inFieldLength +
233
                    " Setting to length of 1 for logical fields.");
234
            }
235

    
236
            tempFieldDescriptors[myFieldDescriptions.length].myFieldLength = 1;
237
        } else {
238
            throw new AttributeFeatureTypeNotSuportedException(tempFieldName,
239
                                        inFieldType, ToolsLocator.getDataTypesManager().getTypeName(inFieldType), "DBF");
240
        }
241

    
242
        // the length of a record
243
        tempLength = tempLength +
244
            tempFieldDescriptors[myFieldDescriptions.length].myFieldLength;
245

    
246
        // set the new fields.
247
        myFieldDescriptions = tempFieldDescriptors;
248
        myHeaderLength = 33 + (32 * myFieldDescriptions.length);
249
        myNumFields = myFieldDescriptions.length;
250
        myRecordLength = tempLength;
251
    }
252

    
253
    /**
254
     * Remove a column from this DbaseFileHeader.
255
     *
256
     * @param inFieldName DOCUMENT ME!
257
     *
258
     * @return index of the removed column, -1 if no found
259
     */
260
    public int removeColumn(String inFieldName) {
261
        int retCol = -1;
262
        int tempLength = 1;
263
        DbaseFieldDescriptor[] tempFieldDescriptors = new DbaseFieldDescriptor[myFieldDescriptions.length -
264
            1];
265

    
266
        for (int i = 0, j = 0; i < myFieldDescriptions.length; i++) {
267
            if (!inFieldName.equalsIgnoreCase(
268
                        myFieldDescriptions[i].myFieldName.trim())) {
269
                // if this is the last field and we still haven't found the
270
                // named field
271
                if ((i == j) && (i == (myFieldDescriptions.length - 1))) {
272
                    System.err.println("Could not find a field named '" +
273
                        inFieldName + "' for removal");
274

    
275
                    return retCol;
276
                }
277

    
278
                tempFieldDescriptors[j] = myFieldDescriptions[i];
279
                tempFieldDescriptors[j].myFieldDataAddress = tempLength;
280
                tempLength += tempFieldDescriptors[j].myFieldLength;
281

    
282
                // only increment j on non-matching fields
283
                j++;
284
            } else {
285
                retCol = i;
286
            }
287
        }
288

    
289
        // set the new fields.
290
        myFieldDescriptions = tempFieldDescriptors;
291
        myHeaderLength = 33 + (32 * myFieldDescriptions.length);
292
        myNumFields = myFieldDescriptions.length;
293
        myRecordLength = tempLength;
294

    
295
        return retCol;
296
    }
297

    
298
    /**
299
     * DOCUMENT ME!
300
     *
301
     * @param inWarn DOCUMENT ME!
302
     */
303
    private void warn(String inWarn) {
304
        //TODO Descomentar esto cuando tenga la clase warning support
305
        //            warnings.warn(inWarn);
306
    }
307

    
308
    /**
309
     * Return the Field Descriptor for the given field.
310
     *
311
     * @param inIndex DOCUMENT ME!
312
     *
313
     * @return DOCUMENT ME!
314
     */
315
    public DbaseFieldDescriptor getFieldDescription(int inIndex) {
316
        return myFieldDescriptions[inIndex];
317
    }
318

    
319
    // Retrieve the length of the field at the given index
320
    public int getFieldLength(int inIndex) {
321
        return myFieldDescriptions[inIndex].myFieldLength;
322
    }
323

    
324
    // Retrieve the location of the decimal point within the field.
325
    public int getFieldDecimalCount(int inIndex) {
326
        return myFieldDescriptions[inIndex].myDecimalCount;
327
    }
328

    
329
    // Retrieve the Name of the field at the given index
330
    public String getFieldName(int inIndex) {
331
        return myFieldDescriptions[inIndex].myFieldName;
332
    }
333

    
334
    public int getFieldIndex(String name) {
335
                for (int i = 0; i < myFieldDescriptions.length; i++) {
336
                        if (myFieldDescriptions[i].myFieldName_trim
337
                                        .equalsIgnoreCase(name)) {
338
                                return i;
339
                        }
340
                }
341
                return -1;
342
        }
343

    
344
    // Retrieve the type of field at the given index
345
    public char getFieldType(int inIndex) {
346
        return myFieldDescriptions[inIndex].myFieldType;
347
    }
348

    
349
    /**
350
     * Return the date this file was last updated.
351
     *
352
     * @return DOCUMENT ME!
353
     */
354
    public Date getLastUpdateDate() {
355
        return myUpdateDate;
356
    }
357

    
358
     /**
359
     * Return the number of fields in the records.
360
     *
361
     * @return DOCUMENT ME!
362
     */
363
    public int getNumFields() {
364
        return myNumFields;
365
    }
366

    
367
    /**
368
     * Return the number of records in the file
369
     *
370
     * @return DOCUMENT ME!
371
     */
372
    public int getNumRecords() {
373
        return myNumRecords;
374
    }
375

    
376
    /**
377
     * Return the length of the records in bytes.
378
     *
379
     * @return DOCUMENT ME!
380
     */
381
    public int getRecordLength() {
382
        return myRecordLength;
383
    }
384

    
385
    /**
386
     * Return the length of the header
387
     *
388
     * @return DOCUMENT ME!
389
     */
390
    public int getHeaderLength() {
391
        return myHeaderLength;
392
    }
393

    
394
        /**
395
         * Read the header data from the DBF file.
396
         *
397
         * @param in
398
         *            DOCUMENT ME!
399
         * @throws UnsupportedVersionException
400
         * @throws UnsupportedEncodingException
401
         *
402
         * @throws IOException
403
         *             DOCUMENT ME!
404
         */
405
    public void readHeader(BigByteBuffer2 in, String charsName)
406
                        throws UnsupportedVersionException, UnsupportedEncodingException {
407
        // type of file.
408
        myFileType = in.get();
409

    
410
        if (myFileType != 0x03) {
411
            throw new UnsupportedVersionException("DBF", Integer
412
                                        .toHexString(myFileType));
413
        }
414

    
415
        // parse the update date information.
416
        int tempUpdateYear = in.get();
417
        int tempUpdateMonth = in.get();
418
        int tempUpdateDay = in.get();
419
        tempUpdateYear = tempUpdateYear + 1900;
420

    
421
        Calendar c = Calendar.getInstance();
422
        c.set(Calendar.YEAR, tempUpdateYear);
423
        c.set(Calendar.MONTH, tempUpdateMonth - 1);
424
        c.set(Calendar.DATE, tempUpdateDay);
425
        myUpdateDate = c.getTime();
426

    
427
        // read the number of records.
428
        in.order(ByteOrder.LITTLE_ENDIAN);
429
        myNumRecords = in.getInt();
430

    
431
        // read the length of the header structure.
432
        myHeaderLength = in.getShort();
433

    
434
        // read the length of a record
435
        myRecordLength = in.getShort(); //posicon 0h
436

    
437
        in.order(ByteOrder.BIG_ENDIAN);
438

    
439
        // skip the reserved bytes in the header.
440
        // in.position(in.position() + 20);
441

    
442
        // Leemos el byte de language
443
        in.position(29);
444
        myLanguageID = in.get();
445
        if (charsName == null) {
446
                charsName = getCharsetName();
447
                charsName = mappingEncoding(charsName);
448
                }
449

    
450

    
451
        // Posicionamos para empezar a leer los campos.
452
        in.position(32);
453

    
454
        // calculate the number of Fields in the header
455
        myNumFields = (myHeaderLength - FILE_DESCRIPTOR_SIZE - 1) / FILE_DESCRIPTOR_SIZE;
456

    
457
        // read all of the header records
458
        myFieldDescriptions = new DbaseFieldDescriptor[myNumFields];
459
        int fieldOffset = 0;
460

    
461
        for (int i = 0; i < myNumFields; i++) {
462
            myFieldDescriptions[i] = new DbaseFieldDescriptor();
463

    
464
            // read the field name
465
            byte[] buffer = new byte[11];
466
            in.get(buffer);
467
            if (charsName != null) {
468
                                myFieldDescriptions[i].myFieldName = new String(buffer,
469
                                                charsName);
470
                        } else {
471
                                myFieldDescriptions[i].myFieldName = new String(buffer);
472
                        }
473
            myFieldDescriptions[i].myFieldName_trim = myFieldDescriptions[i].myFieldName
474
                                        .trim();
475

    
476
            // read the field type
477
            myFieldDescriptions[i].myFieldType = (char) in.get();
478

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

    
482
            // read the field length in bytes
483
            int tempLength = in.get();
484

    
485
            if (tempLength < 0) {
486
                tempLength = tempLength + 256;
487
            }
488

    
489
            myFieldDescriptions[i].myFieldLength = tempLength;
490

    
491
            // read the field decimal count in bytes
492
            myFieldDescriptions[i].myDecimalCount = in.get();
493

    
494
            // NUEVO: Calculamos los offsets aqu? para no
495
            // tener que recalcular cada vez que nos piden
496
            // algo.
497
            myFieldDescriptions[i].myFieldDataAddress = fieldOffset;
498
            fieldOffset += tempLength;
499
            // Fin NUEVO
500
            // read the reserved bytes.
501
            in.position(in.position() + 14);
502
        }
503

    
504
        // Last byte is a marker for the end of the field definitions.
505
        in.get();
506
    }
507

    
508
    /**
509
     * Set the number of records in the file
510
     *
511
     * @param inNumRecords DOCUMENT ME!
512
     */
513
    public void setNumRecords(int inNumRecords) {
514
        myNumRecords = inNumRecords;
515
    }
516

    
517
    /*
518
     * Write the header data to the DBF file.
519
     *
520
     * @param out DOCUMENT ME!
521
     *
522
     * @throws Exception DOCUMENT ME!
523
     *
524
           public void writeHeader(LEDataOutputStream out) throws Exception {
525
               // write the output file type.
526
               out.writeByte(myFileType);
527
               // write the date stuff
528
               Calendar c = Calendar.getInstance();
529
               c.setTime(new Date());
530
               out.writeByte(c.get(Calendar.YEAR) - 1900);
531
               out.writeByte(c.get(Calendar.MONTH) + 1);
532
               out.writeByte(c.get(Calendar.DAY_OF_MONTH));
533
               // write the number of records in the datafile.
534
               out.writeInt(myNumRecords);
535
               // write the length of the header structure.
536
               out.writeShort(myHeaderLength);
537
               // write the length of a record
538
               out.writeShort(myRecordLength);
539
               // write the reserved bytes in the header
540
               for (int i = 0; i < 20; i++)
541
                   out.writeByte(0);
542
               // write all of the header records
543
               int tempOffset = 0;
544
               for (int i = 0; i < myFieldDescriptions.length; i++) {
545
                   // write the field name
546
                   for (int j = 0; j < 11; j++) {
547
                       if (myFieldDescriptions[i].myFieldName.length() > j) {
548
                           out.writeByte((int) myFieldDescriptions[i].myFieldName.charAt(
549
                                   j));
550
                       } else {
551
                           out.writeByte(0);
552
                       }
553
                   }
554
                   // write the field type
555
                   out.writeByte(myFieldDescriptions[i].myFieldType);
556
                   // write the field data address, offset from the start of the record.
557
                   out.writeInt(tempOffset);
558
                   tempOffset += myFieldDescriptions[i].myFieldLength;
559
                   // write the length of the field.
560
                   out.writeByte(myFieldDescriptions[i].myFieldLength);
561
                   // write the decimal count.
562
                   out.writeByte(myFieldDescriptions[i].myDecimalCount);
563
                   // write the reserved bytes.
564
                   for (int j = 0; j < 14; j++)
565
                       out.writeByte(0);
566
               }
567
               // write the end of the field definitions marker
568
               out.writeByte(0x0D);
569
           }
570
     */
571

    
572
    /**
573
     * Class for holding the information assicated with a record.
574
     */
575
    class DbaseFieldDescriptor {
576
        // Field Name
577
        String myFieldName;
578

    
579
        String myFieldName_trim;
580

    
581
        // Field Type (C N L D F or M)
582
        char myFieldType;
583

    
584
        // Field Data Address offset from the start of the record.
585
        int myFieldDataAddress;
586

    
587
        // Length of the data in bytes
588
        int myFieldLength;
589

    
590
        // Field decimal count in Binary, indicating where the decimal is
591
        int myDecimalCount;
592
    }
593

    
594
        public byte getLanguageID() {
595
                return myLanguageID;
596
        }
597

    
598

    
599

    
600
        public static DbaseFileHeader createDbaseHeader(FeatureType featureType)
601
                        throws AttributeFeatureTypeNotSuportedException {
602
                DbaseFileHeader header = new DbaseFileHeader();
603
                Iterator iterator=featureType.iterator();
604
                // TODO header.myLanguageID = langId;
605
                while (iterator.hasNext()) {
606
                        FeatureAttributeDescriptor descriptor = (FeatureAttributeDescriptor) iterator.next();
607

    
608

    
609
                        int type = descriptor.getType();
610
                        String colName = descriptor.getName();
611

    
612
                        int fieldLen = descriptor.getSize(); // TODO aqu? el
613
                        // tama?o no es
614
                        // correcto hay que
615
                        // calcularlo, ahora
616
                        // mismo est? puesto
617
                        // a pi??n.
618
                        int decimales = descriptor.getPrecision();
619
                        if ((type==DataTypes.DOUBLE || type==DataTypes.FLOAT) && decimales==0){
620
                                decimales=1;
621
                        }
622

    
623
                        if (DataTypes.DOUBLE == type || DataTypes.FLOAT == type
624
                                        || DataTypes.INT == type || DataTypes.LONG == type) {
625
                                header.addColumn(colName, 'N', Math.min(fieldLen, 18),
626
                                                decimales);
627
                        } else if (DataTypes.DATE == type) {
628
                                header.addColumn(colName, 'D', fieldLen, 0);
629
                        } else if (DataTypes.BOOLEAN == type) {
630
                                header.addColumn(colName, 'L', 1, 0);
631
                        } else if (DataTypes.STRING == type) {
632
                                header.addColumn(colName, 'C', Math.min(254, fieldLen), 0);
633
                        }
634

    
635

    
636
                }
637
                return header;
638
        }
639
        /**
640
         * Write the header data to the DBF file.
641
         *
642
         * @param out
643
         *            A channel to write to. If you have an OutputStream you can
644
         *            obtain the correct channel by using
645
         *            java.nio.Channels.newChannel(OutputStream out).
646
         *
647
         * @throws IOException
648
         *             If errors occur.
649
         */
650
        public void writeHeader(FileChannel out) throws IOException {
651
                // take care of the annoying case where no records have been added...
652
                if (myHeaderLength <= 0) {
653
                        myHeaderLength = MINIMUM_HEADER;
654
                }
655

    
656
                // Desde el principio
657
                out.position(0);
658

    
659
                ByteBuffer buffer = ByteBuffer.allocateDirect(myHeaderLength);
660
                buffer.order(ByteOrder.LITTLE_ENDIAN);
661

    
662
                // write the output file type.
663
                buffer.put(MAGIC);
664

    
665
                // write the date stuff
666
                Calendar c = Calendar.getInstance();
667
                c.setTime(new Date());
668
                buffer.put((byte) (c.get(Calendar.YEAR) % 100));
669
                buffer.put((byte) (c.get(Calendar.MONTH) + 1));
670
                buffer.put((byte) (c.get(Calendar.DAY_OF_MONTH)));
671

    
672
                // write the number of records in the datafile.
673
                buffer.putInt(myNumRecords);
674

    
675
                // write the length of the header structure.
676
                buffer.putShort((short) myHeaderLength);
677

    
678
                // write the length of a record
679
                buffer.putShort((short) myRecordLength);
680

    
681
                // // write the reserved bytes in the header
682
                // for (int i=0; i<20; i++) out.writeByteLE(0);
683
                buffer.position(buffer.position() + 20);
684

    
685
                // write all of the header records
686
                int tempOffset = 0;
687

    
688
                if (myFieldDescriptions != null) {
689
                        for (int i = 0; i < myFieldDescriptions.length; i++) {
690
                                // write the field name
691
                                for (int j = 0; j < DbaseFile.MAX_FIELD_NAME_LENGTH; j++) {
692
                                        if (myFieldDescriptions[i].myFieldName.length() > j) {
693
                                                buffer.put((byte) myFieldDescriptions[i].myFieldName.charAt(j));
694
                                        } else {
695
                                                buffer.put((byte) 0);
696
                                        }
697
                                }
698

    
699
                                // write the field type
700
                                buffer.put((byte) myFieldDescriptions[i].myFieldType);
701

    
702
                                // // write the field data address, offset from the start of the
703
                                // record.
704
                                buffer.putInt(tempOffset);
705
                                tempOffset += myFieldDescriptions[i].myFieldLength;
706

    
707
                                // write the length of the field.
708
                                buffer.put((byte) myFieldDescriptions[i].myFieldLength);
709

    
710
                                // write the decimal count.
711
                                buffer.put((byte) myFieldDescriptions[i].myDecimalCount);
712

    
713
                                // write the reserved bytes.
714
                                // for (in j=0; jj<14; j++) out.writeByteLE(0);
715
                                buffer.position(buffer.position() + 14);
716
                        }
717
                }
718
                // write the end of the field definitions marker
719
                buffer.put((byte) 0x0D);
720

    
721
                buffer.position(0);
722

    
723
                int r = buffer.remaining();
724

    
725
                while ((r -= out.write(buffer)) > 0) {
726
                        ; // do nothing
727
                }
728
        }
729

    
730
        /**
731
         *         01h                DOS USA        code page 437
732
                02h                DOS Multilingual code page 850
733
                03h                Windows ANSI code page 1252
734
                04h                Standard Macintosh
735
                64h                EE MS-DOS code page 852
736
                65h                Nordic MS-DOS code page 865
737
                66h                Russian MS-DOS code page 866
738
                67h                Icelandic MS-DOS
739
                68h                Kamenicky (Czech) MS-DOS
740
                69h                Mazovia (Polish) MS-DOS
741
                6Ah                Greek MS-DOS (437G)
742
                6Bh                Turkish MS-DOS
743
                96h                Russian Macintosh
744
                97h                Eastern European Macintosh
745
                98h                Greek Macintosh
746
                C8h                Windows EE        code page 1250
747
                C9h                Russian Windows
748
                CAh                Turkish Windows
749
                CBh                Greek Windows
750
         * @return
751
         */
752
        public String getCharsetName() {
753
                switch (getLanguageID()) {
754
                case 0x01:
755
                        return "US-ASCII";
756
                case 0x02:
757
                        return "ISO-8859-1";
758
                case 0x03:
759
                        return "windows-1252";
760
                case 0x04:
761
                        return "mac";
762
                case 0x64:
763
                        return "ISO-8859-1";
764
                case 0x65:
765
                        return "ISO-8859-1";
766
                case 0x66:
767
                        return "ISO-8859-1";
768
                case 0x67:
769
                        return "ISO-8859-1";
770
                case 0x68:
771
                        return "greek";
772
                case 0x69:
773
                        return "ISO-8859-1";
774
                case 0x6A:
775
                        return "greek";
776
                case 0x6B:
777
                        return "ISO-8859-1";
778

    
779
                default:
780
                        return "ISO-8859-1";
781
                }
782
        }
783
        
784
        public String mappingEncoding(String dbfEnconding) {
785
                if(encodingSupportedByString.contains(dbfEnconding))
786
                        return dbfEnconding;
787
                else
788
                        return "UTF-8";
789
        }
790

    
791
}