Statistics
| Revision:

root / trunk / libraries / libFMap / src / com / iver / cit / gvsig / fmap / drivers / shp / DbaseFileHeaderNIO.java @ 28792

History | View | Annotate | Download (24.6 KB)

1
/*
2
 *    Geotools - OpenSource mapping toolkit
3
 *    (C) 2002, Centre for Computational Geography
4
 *
5
 *    This library is free software; you can redistribute it and/or
6
 *    modify it under the terms of the GNU Lesser General Public
7
 *    License as published by the Free Software Foundation;
8
 *    version 2.1 of the License.
9
 *
10
 *    This library is distributed in the hope that it will be useful,
11
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13
 *    Lesser General Public License for more details.
14
 *
15
 *    You should have received a copy of the GNU Lesser General Public
16
 *    License along with this library; if not, write to the Free Software
17
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
18
 *
19
 *    This file is based on an origional contained in the GISToolkit project:
20
 *    http://gistoolkit.sourceforge.net/
21
 *
22
 */
23
/* gvSIG. Sistema de Informaci?n Geogr?fica de la Generalitat Valenciana
24
 *
25
 * Copyright (C) 2004 IVER T.I. and Generalitat Valenciana.
26
 *
27
 * This program is free software; you can redistribute it and/or
28
 * modify it under the terms of the GNU General Public License
29
 * as published by the Free Software Foundation; either version 2
30
 * of the License, or (at your option) any later version.
31
 *
32
 * This program is distributed in the hope that it will be useful,
33
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
34
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
35
 * GNU General Public License for more details.
36
 *
37
 * You should have received a copy of the GNU General Public License
38
 * along with this program; if not, write to the Free Software
39
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307,USA.
40
 *
41
 * For more information, contact:
42
 *
43
 *  Generalitat Valenciana
44
 *   Conselleria d'Infraestructures i Transport
45
 *   Av. Blasco Ib??ez, 50
46
 *   46010 VALENCIA
47
 *   SPAIN
48
 *
49
 *      +34 963862235
50
 *   gvsig@gva.es
51
 *      www.gvsig.gva.es
52
 *
53
 *    or
54
 *
55
 *   IVER T.I. S.A
56
 *   Salamanca 50
57
 *   46005 Valencia
58
 *   Spain
59
 *
60
 *   +34 963163400
61
 *   dac@iver.es
62
 */
63
package com.iver.cit.gvsig.fmap.drivers.shp;
64

    
65
import java.io.EOFException;
66
import java.io.IOException;
67
import java.nio.ByteBuffer;
68
import java.nio.ByteOrder;
69
import java.nio.channels.FileChannel;
70
import java.nio.channels.ReadableByteChannel;
71
import java.sql.Types;
72
import java.util.Calendar;
73
import java.util.Date;
74
import java.util.logging.Level;
75
import java.util.logging.Logger;
76

    
77
import com.hardcode.gdbms.driver.exceptions.ReadDriverException;
78
import com.hardcode.gdbms.engine.data.DataSource;
79
import com.iver.cit.gvsig.fmap.drivers.FieldDescription;
80
import com.iver.utiles.bigfile.BigByteBuffer2;
81
import com.vividsolutions.jts.geom.Geometry;
82

    
83
/**
84
 * Class to represent the header of a Dbase III file. Creation date: (5/15/2001
85
 * 5:15:30 PM)
86
 */
87
public class DbaseFileHeaderNIO {
88
        // Constant for the size of a record
89
        private static final int FILE_DESCRIPTOR_SIZE = 32;
90

    
91
        // type of the file, must be 03h
92
        private static final byte MAGIC = 0x03;
93

    
94
        private static final int MINIMUM_HEADER = 33;
95

    
96
        // Date the file was last updated.
97
        private Date date = new Date();
98

    
99
        private int recordCnt = 0;
100

    
101
        private int fieldCnt = 0;
102

    
103
        private int myFileType = 0;
104

    
105
        // set this to a default length of 1, which is enough for one "space"
106
        // character which signifies an empty record
107
        private int recordLength = 1;
108

    
109
        // set this to a flagged value so if no fields are added before the write,
110
        // we know to adjust the headerLength to MINIMUM_HEADER
111
        private int headerLength = -1;
112

    
113
        private short myLanguageID;
114

    
115
        private int largestFieldSize = 0;
116

    
117
        private Logger logger = Logger.getLogger("org.geotools.data.shapefile");
118

    
119
        // collection of header records.
120
        // lets start out with a zero-length array, just in case
121
        private DbaseField[] fields = null; // new DbaseField[0];
122

    
123
        /**
124
         * Lee del buffer.
125
         *
126
         * @param buffer .
127
         * @param channel .
128
         *
129
         * @throws IOException .
130
         * @throws EOFException .
131
         */
132
        private void read(ByteBuffer buffer, ReadableByteChannel channel)
133
                        throws IOException {
134
                while (buffer.remaining() > 0) {
135
                        if (channel.read(buffer) == -1) {
136
                                throw new EOFException("Premature end of file");
137
                        }
138
                }
139
        }
140

    
141
        /**
142
         * Determine the most appropriate Java Class for representing the data in
143
         * the field.
144
         *
145
         * <PRE>
146
         *
147
         * All packages are java.lang unless otherwise specified. C (Character) ->
148
         * String N (Numeric) -> Integer or Double (depends on field's decimal
149
         * count) F (Floating) -> Double L (Logical) -> Boolean D (Date) ->
150
         * java.util.Date Unknown -> String
151
         *
152
         * </PRE>
153
         *
154
         * @param i
155
         *            The index of the field, from 0 to <CODE>getNumFields() - 1</CODE> .
156
         *
157
         * @return A Class which closely represents the dbase field type.
158
         */
159
        public Class getFieldClass(int i) {
160
                Class typeClass = null;
161

    
162
                switch (fields[i].fieldType) {
163
                case 'C':
164
                        typeClass = String.class;
165

    
166
                        break;
167

    
168
                case 'N':
169

    
170
                        if (fields[i].decimalCount == 0) {
171
                                typeClass = Integer.class;
172
                        } else {
173
                                typeClass = Double.class;
174
                        }
175

    
176
                        break;
177

    
178
                case 'F':
179
                        typeClass = Double.class;
180

    
181
                        break;
182

    
183
                case 'L':
184
                        typeClass = Boolean.class;
185

    
186
                        break;
187

    
188
                case 'D':
189
                        typeClass = Date.class;
190

    
191
                        break;
192

    
193
                default:
194
                        typeClass = String.class;
195

    
196
                        break;
197
                }
198

    
199
                return typeClass;
200
        }
201

    
202
        /**
203
         * Add a column to this DbaseFileHeader. The type is one of (C N L or D)
204
         * character, number, logical(true/false), or date. The Field length is the
205
         * total length in bytes reserved for this column. The decimal count only
206
         * applies to numbers(N), and floating point values (F), and refers to the
207
         * number of characters to reserve after the decimal point. <B>Don't expect
208
         * miracles from this...</B>
209
         *
210
         * <PRE>
211
         *
212
         * Field Type MaxLength ---------- --------- C 254 D 8 F 20 N 18
213
         *
214
         * </PRE>
215
         *
216
         * @param inFieldName
217
         *            The name of the new field, must be less than 10 characters or
218
         *            it gets truncated.
219
         * @param inFieldType
220
         *            A character representing the dBase field, ( see above ). Case
221
         *            insensitive.
222
         * @param inFieldLength
223
         *            The length of the field, in bytes ( see above )
224
         * @param inDecimalCount
225
         *            For numeric fields, the number of decimal places to track.
226
         */
227
        public void addColumn(String inFieldName, char inFieldType,
228
                        int inFieldLength, int inDecimalCount) {
229
                /*
230
                 * if (inFieldLength <=0) { throw new DbaseFileException("field length <=
231
                 * 0"); }
232
                 */
233
                if (fields == null) {
234
                        fields = new DbaseField[0];
235
                }
236

    
237
                int tempLength = 1; // the length is used for the offset, and there is a
238
                // * for deleted as the first byte
239
                DbaseField[] tempFieldDescriptors = new DbaseField[fields.length + 1];
240

    
241
                for (int i = 0; i < fields.length; i++) {
242
                        fields[i].fieldDataAddress = tempLength;
243
                        tempLength = tempLength + fields[i].fieldLength;
244
                        tempFieldDescriptors[i] = fields[i];
245
                }
246

    
247
                tempFieldDescriptors[fields.length] = new DbaseField();
248
                tempFieldDescriptors[fields.length].fieldLength = inFieldLength;
249
                tempFieldDescriptors[fields.length].decimalCount = inDecimalCount;
250
                tempFieldDescriptors[fields.length].fieldDataAddress = tempLength;
251

    
252
                // set the field name
253
                String tempFieldName = inFieldName;
254

    
255
                if (tempFieldName == null) {
256
                        tempFieldName = "NoName";
257
                }
258

    
259
                // Fix for GEOT-42, ArcExplorer will not handle field names > 10 chars
260
                // Sorry folks.
261
                if (tempFieldName.length() > 10) {
262
                        tempFieldName = tempFieldName.substring(0, 10);
263
                        warn("FieldName " + inFieldName
264
                                        + " is longer than 10 characters, truncating to "
265
                                        + tempFieldName);
266
                }
267

    
268
                tempFieldDescriptors[fields.length].fieldName = tempFieldName;
269

    
270
                // the field type
271
                if ((inFieldType == 'C') || (inFieldType == 'c')) {
272
                        tempFieldDescriptors[fields.length].fieldType = 'C';
273

    
274
                        if (inFieldLength > 254) {
275
                                warn("Field Length for "
276
                                                + inFieldName
277
                                                + " set to "
278
                                                + inFieldLength
279
                                                + " Which is longer than 254, not consistent with dbase III");
280
                        }
281
                } else if ((inFieldType == 'S') || (inFieldType == 's')) {
282
                        tempFieldDescriptors[fields.length].fieldType = 'C';
283
                        warn("Field type for "
284
                                        + inFieldName
285
                                        + " set to S which is flat out wrong people!, I am setting this to C, in the hopes you meant character.");
286

    
287
                        if (inFieldLength > 254) {
288
                                warn("Field Length for "
289
                                                + inFieldName
290
                                                + " set to "
291
                                                + inFieldLength
292
                                                + " Which is longer than 254, not consistent with dbase III");
293
                        }
294

    
295
                        tempFieldDescriptors[fields.length].fieldLength = 8;
296
                } else if ((inFieldType == 'D') || (inFieldType == 'd')) {
297
                        tempFieldDescriptors[fields.length].fieldType = 'D';
298

    
299
                        if (inFieldLength != 8) {
300
                                warn("Field Length for " + inFieldName + " set to "
301
                                                + inFieldLength + " Setting to 8 digets YYYYMMDD");
302
                        }
303

    
304
                        tempFieldDescriptors[fields.length].fieldLength = 8;
305
                } else if ((inFieldType == 'F') || (inFieldType == 'f')) {
306
                        tempFieldDescriptors[fields.length].fieldType = 'F';
307

    
308
                        if (inFieldLength > 20) {
309
                                warn("Field Length for "
310
                                                + inFieldName
311
                                                + " set to "
312
                                                + inFieldLength
313
                                                + " Preserving length, but should be set to Max of 20 not valid for dbase IV, and UP specification, not present in dbaseIII.");
314
                        }
315
                } else if ((inFieldType == 'N') || (inFieldType == 'n')) {
316
                        tempFieldDescriptors[fields.length].fieldType = 'N';
317

    
318
                        if (inFieldLength > 18) {
319
                                warn("Field Length for "
320
                                                + inFieldName
321
                                                + " set to "
322
                                                + inFieldLength
323
                                                + " Preserving length, but should be set to Max of 18 for dbase III specification.");
324
                        }
325

    
326
                        if (inDecimalCount < 0) {
327
                                warn("Field Decimal Position for " + inFieldName + " set to "
328
                                                + inDecimalCount
329
                                                + " Setting to 0 no decimal data will be saved.");
330
                                tempFieldDescriptors[fields.length].decimalCount = 0;
331
                        }
332

    
333
                        if (inDecimalCount > (inFieldLength - 1)) {
334
                                warn("Field Decimal Position for " + inFieldName + " set to "
335
                                                + inDecimalCount + " Setting to " + (inFieldLength - 1)
336
                                                + " no non decimal data will be saved.");
337
                                tempFieldDescriptors[fields.length].decimalCount = inFieldLength - 1;
338
                        }
339
                } else if ((inFieldType == 'L') || (inFieldType == 'l')) {
340
                        tempFieldDescriptors[fields.length].fieldType = 'L';
341

    
342
                        if (inFieldLength != 1) {
343
                                warn("Field Length for " + inFieldName + " set to "
344
                                                + inFieldLength
345
                                                + " Setting to length of 1 for logical fields.");
346
                        }
347

    
348
                        tempFieldDescriptors[fields.length].fieldLength = 1;
349
                } else {
350
                        // throw new DbaseFileException("Undefined field type "+inFieldType
351
                        // + " For column "+inFieldName);
352
                }
353

    
354
                // the length of a record
355
                tempLength = tempLength
356
                                + tempFieldDescriptors[fields.length].fieldLength;
357

    
358
                // set the new fields.
359
                fields = tempFieldDescriptors;
360
                fieldCnt = fields.length;
361
                headerLength = MINIMUM_HEADER + (32 * fields.length);
362
                recordLength = tempLength;
363
        }
364

    
365
        /**
366
         * Remove a column from this DbaseFileHeader.
367
         *
368
         * @param inFieldName
369
         *            The name of the field, will ignore case and trim.
370
         *
371
         * @return index of the removed column, -1 if no found
372
         *
373
         * @todo This is really ugly, don't know who wrote it, but it needs fixin...
374
         */
375
        public int removeColumn(String inFieldName) {
376
                int retCol = -1;
377
                int tempLength = 1;
378
                DbaseField[] tempFieldDescriptors = new DbaseField[fields.length - 1];
379

    
380
                for (int i = 0, j = 0; i < fields.length; i++) {
381
                        if (!inFieldName.equalsIgnoreCase(fields[i].fieldName.trim())) {
382
                                // if this is the last field and we still haven't found the
383
                                // named field
384
                                if ((i == j) && (i == (fields.length - 1))) {
385
                                        System.err.println("Could not find a field named '"
386
                                                        + inFieldName + "' for removal");
387

    
388
                                        return retCol;
389
                                }
390

    
391
                                tempFieldDescriptors[j] = fields[i];
392
                                tempFieldDescriptors[j].fieldDataAddress = tempLength;
393
                                tempLength += tempFieldDescriptors[j].fieldLength;
394

    
395
                                // only increment j on non-matching fields
396
                                j++;
397
                        } else {
398
                                retCol = i;
399
                        }
400
                }
401

    
402
                // set the new fields.
403
                fields = tempFieldDescriptors;
404
                headerLength = 33 + (32 * fields.length);
405
                recordLength = tempLength;
406

    
407
                return retCol;
408
        }
409

    
410
        /**
411
         * DOCUMENT ME!
412
         *
413
         * @param inWarn
414
         *            DOCUMENT ME!
415
         *
416
         * @todo addProgessListener handling
417
         */
418
        private void warn(String inWarn) {
419
                if (logger.isLoggable(Level.WARNING)) {
420
                        logger.warning(inWarn);
421
                }
422
        }
423

    
424
        // Retrieve the length of the field at the given index
425

    
426
        /**
427
         * Returns the field length in bytes.
428
         *
429
         * @param inIndex
430
         *            The field index.
431
         *
432
         * @return The length in bytes.
433
         */
434
        public int getFieldLength(int inIndex) {
435
                return fields[inIndex].fieldLength;
436
        }
437

    
438
        // Retrieve the location of the decimal point within the field.
439

    
440
        /**
441
         * Get the decimal count of this field.
442
         *
443
         * @param inIndex
444
         *            The field index.
445
         *
446
         * @return The decimal count.
447
         */
448
        public int getFieldDecimalCount(int inIndex) {
449
                return fields[inIndex].decimalCount;
450
        }
451

    
452
        // Retrieve the Name of the field at the given index
453

    
454
        /**
455
         * Get the field name.
456
         *
457
         * @param inIndex
458
         *            The field index.
459
         *
460
         * @return The name of the field.
461
         */
462
        public String getFieldName(int inIndex) {
463
                return fields[inIndex].fieldName;
464
        }
465

    
466
        // Retrieve the type of field at the given index
467

    
468
        /**
469
         * Get the character class of the field.
470
         *
471
         * @param inIndex
472
         *            The field index.
473
         *
474
         * @return The dbase character representing this field.
475
         */
476
        public char getFieldType(int inIndex) {
477
                return fields[inIndex].fieldType;
478
        }
479

    
480
        /**
481
         * Get the date this file was last updated.
482
         *
483
         * @return The Date last modified.
484
         */
485
        public Date getLastUpdateDate() {
486
                return date;
487
        }
488

    
489
        /**
490
         * Return the number of fields in the records.
491
         *
492
         * @return The number of fields in this table.
493
         */
494
        public int getNumFields() {
495
                if (fields == null)
496
                        return 0;
497
                return fields.length;
498
        }
499

    
500
        /**
501
         * Return the number of records in the file
502
         *
503
         * @return The number of records in this table.
504
         */
505
        public int getNumRecords() {
506
                return recordCnt;
507
        }
508

    
509
        /**
510
         * Get the length of the records in bytes.
511
         *
512
         * @return The number of bytes per record.
513
         */
514
        public int getRecordLength() {
515
                return recordLength;
516
        }
517

    
518
        /**
519
         * Get the length of the header
520
         *
521
         * @return The length of the header in bytes.
522
         */
523
        public int getHeaderLength() {
524
                return headerLength;
525
        }
526

    
527
        /**
528
         * Read the header data from the DBF file.
529
         *
530
         * @param in
531
         *            DOCUMENT ME!
532
         *
533
         * @throws IOException
534
         *             DOCUMENT ME!
535
         */
536
        public void readHeader(BigByteBuffer2 in) throws IOException {
537
                // type of file.
538
                myFileType = in.get();
539

    
540
                if (myFileType != 0x03) {
541
                        throw new IOException("Unsupported DBF file Type "
542
                                        + Integer.toHexString(myFileType));
543
                }
544

    
545
                // parse the update date information.
546
                int tempUpdateYear = (int) in.get();
547
                int tempUpdateMonth = (int) in.get();
548
                int tempUpdateDay = (int) in.get();
549
                tempUpdateYear = tempUpdateYear + 1900;
550

    
551
                Calendar c = Calendar.getInstance();
552
                c.set(Calendar.YEAR, tempUpdateYear);
553
                c.set(Calendar.MONTH, tempUpdateMonth - 1);
554
                c.set(Calendar.DATE, tempUpdateDay);
555
                date = c.getTime();
556

    
557
                // read the number of records.
558
                in.order(ByteOrder.LITTLE_ENDIAN);
559
                recordCnt = in.getInt();
560

    
561
                // read the length of the header structure.
562
                headerLength = in.getShort();
563

    
564
                // read the length of a record
565
                recordLength = in.getShort();
566

    
567
                // Leemos el byte de language
568
        in.position(29);
569
        myLanguageID = in.get();
570
        if (myLanguageID < 0) {
571
                myLanguageID = (short) (myLanguageID + 256);
572
        }
573

    
574
                in.order(ByteOrder.BIG_ENDIAN);
575

    
576
                // skip the reserved bytes in the header.
577
                in.position(32);
578

    
579
                // calculate the number of Fields in the header
580
                fieldCnt = (headerLength - FILE_DESCRIPTOR_SIZE - 1)
581
                                / FILE_DESCRIPTOR_SIZE;
582

    
583
                // read all of the header records
584
                fields = new DbaseField[fieldCnt];
585

    
586
                for (int i = 0; i < fieldCnt; i++) {
587
                        fields[i] = new DbaseField();
588

    
589
                        // read the field name
590
                        byte[] buffer = new byte[11];
591
                        in.get(buffer);
592
                        fields[i].fieldName = new String(buffer);
593

    
594
                        // read the field type
595
                        fields[i].fieldType = (char) in.get();
596

    
597
                        // read the field data address, offset from the start of the record.
598
                        fields[i].fieldDataAddress = in.getInt();
599

    
600
                        // read the field length in bytes
601
                        int tempLength = (int) in.get();
602

    
603
                        if (tempLength < 0) {
604
                                tempLength = tempLength + 256;
605
                        }
606

    
607
                        fields[i].fieldLength = tempLength;
608

    
609
                        // read the field decimal count in bytes
610
                        fields[i].decimalCount = (int) in.get();
611

    
612
                        // read the reserved bytes.
613
                        in.position(in.position() + 14);
614
                }
615

    
616
                // Last byte is a marker for the end of the field definitions.
617
                in.get();
618
        }
619

    
620
        /**
621
         * Get the largest field size of this table.
622
         *
623
         * @return The largt field size iiin bytes.
624
         */
625
        public int getLargestFieldSize() {
626
                return largestFieldSize;
627
        }
628

    
629
        /**
630
         * Set the number of records in the file
631
         *
632
         * @param inNumRecords
633
         *            The number of records.
634
         */
635
        public void setNumRecords(int inNumRecords) {
636
                recordCnt = inNumRecords;
637
        }
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 (headerLength == -1) {
653
                        headerLength = MINIMUM_HEADER;
654
                }
655

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

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

    
662
                // write the output file type.
663
                buffer.put((byte) 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(recordCnt);
674

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

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

    
681

    
682
                buffer.position(29);
683
                buffer.putShort(myLanguageID);
684

    
685
                // // write the reserved bytes in the header
686
                // for (int i=0; i<20; i++) out.writeByteLE(0);
687
                buffer.position(32);
688

    
689
                // write all of the header records
690
                int tempOffset = 0;
691

    
692
                if (fields != null) {
693
                        for (int i = 0; i < fields.length; i++) {
694
                                // write the field name
695
                                for (int j = 0; j < 11; j++) {
696
                                        if (fields[i].fieldName.length() > j) {
697
                                                buffer.put((byte) fields[i].fieldName.charAt(j));
698
                                        } else {
699
                                                buffer.put((byte) 0);
700
                                        }
701
                                }
702

    
703
                                // write the field type
704
                                buffer.put((byte) fields[i].fieldType);
705

    
706
                                // // write the field data address, offset from the start of the
707
                                // record.
708
                                buffer.putInt(tempOffset);
709
                                tempOffset += fields[i].fieldLength;
710

    
711
                                // write the length of the field.
712
                                buffer.put((byte) fields[i].fieldLength);
713

    
714
                                // write the decimal count.
715
                                buffer.put((byte) fields[i].decimalCount);
716

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

    
725
                buffer.position(0);
726

    
727
                int r = buffer.remaining();
728

    
729
                while ((r -= out.write(buffer)) > 0) {
730
                        ; // do nothing
731
                }
732
        }
733

    
734
        /**
735
         * Get a simple representation of this header.
736
         *
737
         * @return A String representing the state of the header.
738
         */
739
        public String toString() {
740
                StringBuffer fs = new StringBuffer();
741

    
742
                for (int i = 0, ii = fields.length; i < ii; i++) {
743
                        DbaseField f = fields[i];
744
                        fs.append(f.fieldName + " " + f.fieldType + " " + f.fieldLength
745
                                        + " " + f.decimalCount + " " + f.fieldDataAddress + "\n");
746
                }
747

    
748
                return "DB3 Header\n" + "Date : " + date + "\n" + "Records : "
749
                                + recordCnt + "\n" + "Fields : " + fieldCnt + "\n" + fs;
750
        }
751

    
752
        /**
753
         * Crea un DbaseFile.
754
         *
755
         * @return DbaseFileHeaderNIO
756
         *
757
         * @throws IOException .
758
         */
759
        public static DbaseFileHeaderNIO createNewDbaseHeader() throws IOException {
760
                DbaseFileHeaderNIO header = new DbaseFileHeaderNIO();
761

    
762
                for (int i = 0, ii = 1; i < ii; i++) {
763
                        // AttributeType type = featureType.getAttributeType(i);
764
                        Class colType = Integer.class;
765
                        String colName = "ID";
766
                        int fieldLen = 10;
767

    
768
                        if (fieldLen <= 0) {
769
                                fieldLen = 255;
770
                        }
771

    
772
                        // @todo respect field length
773
                        if ((colType == Integer.class) || (colType == Short.class)
774
                                        || (colType == Byte.class)) {
775
                                header.addColumn(colName, 'N', Math.min(fieldLen, 10), 0);
776
                        } else if (colType == Long.class) {
777
                                header.addColumn(colName, 'N', Math.min(fieldLen, 19), 0);
778
                        } else if ((colType == Double.class) || (colType == Float.class)
779
                                        || (colType == Number.class)) {
780
                                int l = Math.min(fieldLen, 33);
781
                                int d = Math.max(l - 2, 0);
782
                                header.addColumn(colName, 'N', l, d);
783
                        } else if (java.util.Date.class.isAssignableFrom(colType)) {
784
                                header.addColumn(colName, 'D', fieldLen, 0);
785
                        } else if (colType == Boolean.class) {
786
                                header.addColumn(colName, 'L', 1, 0);
787
                        } else if (CharSequence.class.isAssignableFrom(colType)) {
788
                                // Possible fix for GEOT-42 : ArcExplorer doesn't like 0 length
789
                                // ensure that maxLength is at least 1
790
                                header.addColumn(colName, 'C', Math.min(254, fieldLen), 0);
791
                        } else if (Geometry.class.isAssignableFrom(colType)) {
792
                                continue;
793
                        } else {
794
                                throw new IOException("Unable to write : " + colType.getName());
795
                        }
796
                }
797

    
798
                return header;
799
        }
800

    
801
        public static DbaseFileHeaderNIO createDbaseHeader(DataSource ds)
802
                        throws ReadDriverException {
803
                int[] fieldTypes = new int[ds.getFieldCount()];
804
                int[] fieldLength = new int[fieldTypes.length];
805
                for (int i = 0; i < fieldTypes.length; i++) {
806
                        fieldTypes[i] = ds.getFieldType(i);
807
                        fieldLength[i] = ds.getFieldWidth(i);
808
                }
809

    
810
                return createDbaseHeader(ds.getFieldNames(), fieldTypes, fieldLength);
811
        }
812

    
813
        /**
814
         * DOCUMENT ME!
815
         *
816
         * @param sds
817
         *            DOCUMENT ME!
818
         *
819
         * @return DOCUMENT ME!
820
         *
821
         * @throws IOException
822
         *             DOCUMENT ME!
823
         */
824
        public static DbaseFileHeaderNIO createDbaseHeader(String[] fieldNames,
825
                        int[] fieldTypes, int[] fieldLength) {
826
                DbaseFileHeaderNIO header = new DbaseFileHeaderNIO();
827

    
828
                for (int i = 0, ii = fieldNames.length; i < ii; i++) {
829

    
830
                        int type = fieldTypes[i];
831
                        String colName = fieldNames[i];
832

    
833
                        // /int fieldLen = ((DBFDriver)sds.getDriver()).getFieldLength(i);
834
                        int fieldLen = fieldLength[i]; // TODO aqu? el tama?o no es
835
                        // correcto hay que calcularlo,
836
                        // ahora mismo est? puesto a pi??n.
837
                        int decimales = 5;
838

    
839
                        // if (fieldLen <= 0) {
840
                        // fieldLen = 255;
841
                        // }
842
                        // TODO [AZABALA] HE INTENTADO CREAR UN TIPO Types.BIGINT y
843
                        // ha petado (por eso lo a?ado)
844
                        if ((type == Types.DOUBLE) || (type == Types.FLOAT)
845
                                        || (type == Types.INTEGER) || (type == Types.BIGINT))
846

    
847
                                header.addColumn(colName, 'N', Math.min(fieldLen, 18),
848
                                                decimales);
849
                        if (type == Types.DATE)
850
                                header.addColumn(colName, 'D', fieldLen, 0);
851
                        if ((type == Types.BIT) || (type == Types.BOOLEAN))
852
                                header.addColumn(colName, 'L', 1, 0);
853
                        if ((type == Types.VARCHAR) || (type == Types.CHAR)
854
                                        || (type == Types.LONGVARCHAR))
855
                                header.addColumn(colName, 'C', Math.min(254, fieldLen), 0);
856
                }
857

    
858
                return header;
859
        }
860

    
861
        /**
862
         * Class for holding the information assicated with a record.
863
         */
864
        class DbaseField {
865
                // Field Name
866
                String fieldName;
867

    
868
                // Field Type (C N L D or M)
869
                char fieldType;
870

    
871
                // Field Data Address offset from the start of the record.
872
                int fieldDataAddress;
873

    
874
                // Length of the data in bytes
875
                int fieldLength;
876

    
877
                // Field decimal count in Binary, indicating where the decimal is
878
                int decimalCount;
879
        }
880

    
881
        public static DbaseFileHeaderNIO createDbaseHeader(
882
                        FieldDescription[] fieldsDesc) {
883
                DbaseFileHeaderNIO header = new DbaseFileHeaderNIO();
884

    
885
                for (int i = 0, ii = fieldsDesc.length; i < ii; i++) {
886

    
887
                        int type = fieldsDesc[i].getFieldType();
888
                        String colName = fieldsDesc[i].getFieldName();
889

    
890
                        int fieldLen = fieldsDesc[i].getFieldLength(); // TODO aqu? el
891
                        // tama?o no es
892
                        // correcto hay que
893
                        // calcularlo, ahora
894
                        // mismo est? puesto
895
                        // a pi??n.
896
                        int decimales = fieldsDesc[i].getFieldDecimalCount();
897

    
898
                        switch (type) {
899
                        case Types.DOUBLE:
900
                        case Types.FLOAT:
901
                        case Types.INTEGER:
902
                        case Types.BIGINT:
903
                        case Types.SMALLINT:
904
                                header.addColumn(colName, 'N', Math.min(fieldLen, 18),
905
                                                decimales);
906
                                break;
907
                        case Types.DATE:
908
                                header.addColumn(colName, 'D', fieldLen, 0);
909
                                break;
910
                        case Types.BIT:
911
                        case Types.BOOLEAN:
912
                                header.addColumn(colName, 'L', 1, 0);
913
                                break;
914
                        case Types.VARCHAR:
915
                        case Types.CHAR:
916
                        case Types.LONGVARCHAR:
917
                                header.addColumn(colName, 'C', Math.min(254, fieldLen), 0);
918
                                break;
919
                        default:
920
                                throw new RuntimeException("Field type " + type + " not supported in DBF writer");
921

    
922
                        }
923
                } // for
924

    
925
                return header;
926

    
927
        }
928

    
929
        public void setFieldName(int j, String newName) {
930
                fields[j].fieldName = newName;
931
        }
932

    
933
        public short getLanguageID() {
934
                return myLanguageID;
935
        }
936
}