Statistics
| Revision:

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

History | View | Annotate | Download (23.7 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 com.hardcode.gdbms.engine.data.DriverException;
66
import com.hardcode.gdbms.engine.instruction.SemanticException;
67
import com.hardcode.gdbms.engine.values.Value;
68

    
69
import com.iver.cit.gvsig.fmap.layers.SelectableDataSource;
70

    
71
import com.vividsolutions.jts.geom.Geometry;
72

    
73
import java.io.EOFException;
74
import java.io.IOException;
75

    
76
import java.nio.ByteBuffer;
77
import java.nio.ByteOrder;
78
import java.nio.channels.ReadableByteChannel;
79
import java.nio.channels.WritableByteChannel;
80

    
81
import java.util.Calendar;
82
import java.util.Date;
83
import java.util.logging.Level;
84
import java.util.logging.Logger;
85

    
86

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

    
95
        // type of the file, must be 03h
96
        private static final byte MAGIC = 0x03;
97
        private static final int MINIMUM_HEADER = 33;
98

    
99
        // Date the file was last updated.
100
        private Date date = new Date();
101
        private int recordCnt = 0;
102
        private int fieldCnt = 0;
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
        private int largestFieldSize = 0;
113
        private Logger logger = Logger.getLogger("org.geotools.data.shapefile");
114

    
115
        // collection of header records.
116
        // lets start out with a zero-length array, just in case
117
        private DbaseField[] fields = null; // new DbaseField[0];
118

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

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

    
158
                switch (fields[i].fieldType) {
159
                        case 'C':
160
                                typeClass = String.class;
161

    
162
                                break;
163

    
164
                        case 'N':
165

    
166
                                if (fields[i].decimalCount == 0) {
167
                                        typeClass = Integer.class;
168
                                } else {
169
                                        typeClass = Double.class;
170
                                }
171

    
172
                                break;
173

    
174
                        case 'F':
175
                                typeClass = Double.class;
176

    
177
                                break;
178

    
179
                        case 'L':
180
                                typeClass = Boolean.class;
181

    
182
                                break;
183

    
184
                        case 'D':
185
                                typeClass = Date.class;
186

    
187
                                break;
188

    
189
                        default:
190
                                typeClass = String.class;
191

    
192
                                break;
193
                }
194

    
195
                return typeClass;
196
        }
197

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

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

    
235
                for (int i = 0; i < fields.length; i++) {
236
                        fields[i].fieldDataAddress = tempLength;
237
                        tempLength = tempLength + fields[i].fieldLength;
238
                        tempFieldDescriptors[i] = fields[i];
239
                }
240

    
241
                tempFieldDescriptors[fields.length] = new DbaseField();
242
                tempFieldDescriptors[fields.length].fieldLength = inFieldLength;
243
                tempFieldDescriptors[fields.length].decimalCount = inDecimalCount;
244
                tempFieldDescriptors[fields.length].fieldDataAddress = tempLength;
245

    
246
                // set the field name
247
                String tempFieldName = inFieldName;
248

    
249
                if (tempFieldName == null) {
250
                        tempFieldName = "NoName";
251
                }
252

    
253
                // Fix for GEOT-42, ArcExplorer will not handle field names > 10 chars
254
                // Sorry folks.
255
                if (tempFieldName.length() > 10) {
256
                        tempFieldName = tempFieldName.substring(0, 10);
257
                        warn("FieldName " + inFieldName +
258
                                " is longer than 10 characters, truncating to " +
259
                                tempFieldName);
260
                }
261

    
262
                tempFieldDescriptors[fields.length].fieldName = tempFieldName;
263

    
264
                // the field type
265
                if ((inFieldType == 'C') || (inFieldType == 'c')) {
266
                        tempFieldDescriptors[fields.length].fieldType = 'C';
267

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

    
278
                        if (inFieldLength > 254) {
279
                                warn("Field Length for " + inFieldName + " set to " +
280
                                        inFieldLength +
281
                                        " Which is longer than 254, not consistent with dbase III");
282
                        }
283

    
284
                        tempFieldDescriptors[fields.length].fieldLength = 8;
285
                } else if ((inFieldType == 'D') || (inFieldType == 'd')) {
286
                        tempFieldDescriptors[fields.length].fieldType = 'D';
287

    
288
                        if (inFieldLength != 8) {
289
                                warn("Field Length for " + inFieldName + " set to " +
290
                                        inFieldLength + " Setting to 8 digets YYYYMMDD");
291
                        }
292

    
293
                        tempFieldDescriptors[fields.length].fieldLength = 8;
294
                } else if ((inFieldType == 'F') || (inFieldType == 'f')) {
295
                        tempFieldDescriptors[fields.length].fieldType = 'F';
296

    
297
                        if (inFieldLength > 20) {
298
                                warn("Field Length for " + inFieldName + " set to " +
299
                                        inFieldLength +
300
                                        " Preserving length, but should be set to Max of 20 not valid for dbase IV, and UP specification, not present in dbaseIII.");
301
                        }
302
                } else if ((inFieldType == 'N') || (inFieldType == 'n')) {
303
                        tempFieldDescriptors[fields.length].fieldType = 'N';
304

    
305
                        if (inFieldLength > 18) {
306
                                warn("Field Length for " + inFieldName + " set to " +
307
                                        inFieldLength +
308
                                        " Preserving length, but should be set to Max of 18 for dbase III specification.");
309
                        }
310

    
311
                        if (inDecimalCount < 0) {
312
                                warn("Field Decimal Position for " + inFieldName + " set to " +
313
                                        inDecimalCount +
314
                                        " Setting to 0 no decimal data will be saved.");
315
                                tempFieldDescriptors[fields.length].decimalCount = 0;
316
                        }
317

    
318
                        if (inDecimalCount > (inFieldLength - 1)) {
319
                                warn("Field Decimal Position for " + inFieldName + " set to " +
320
                                        inDecimalCount + " Setting to " + (inFieldLength - 1) +
321
                                        " no non decimal data will be saved.");
322
                                tempFieldDescriptors[fields.length].decimalCount = inFieldLength -
323
                                        1;
324
                        }
325
                } else if ((inFieldType == 'L') || (inFieldType == 'l')) {
326
                        tempFieldDescriptors[fields.length].fieldType = 'L';
327

    
328
                        if (inFieldLength != 1) {
329
                                warn("Field Length for " + inFieldName + " set to " +
330
                                        inFieldLength +
331
                                        " Setting to length of 1 for logical fields.");
332
                        }
333

    
334
                        tempFieldDescriptors[fields.length].fieldLength = 1;
335
                } else {
336
                        //throw new DbaseFileException("Undefined field type "+inFieldType + " For column "+inFieldName);
337
                }
338

    
339
                // the length of a record
340
                tempLength = tempLength +
341
                        tempFieldDescriptors[fields.length].fieldLength;
342

    
343
                // set the new fields.
344
                fields = tempFieldDescriptors;
345
                fieldCnt = fields.length;
346
                headerLength = MINIMUM_HEADER + (32 * fields.length);
347
                recordLength = tempLength;
348
        }
349

    
350
        /**
351
         * Remove a column from this DbaseFileHeader.
352
         *
353
         * @param inFieldName The name of the field, will ignore case and trim.
354
         *
355
         * @return index of the removed column, -1 if no found
356
         *
357
         * @todo This is really ugly, don't know who wrote it, but it needs
358
         *                  fixin...
359
         */
360
        public int removeColumn(String inFieldName) {
361
                int retCol = -1;
362
                int tempLength = 1;
363
                DbaseField[] tempFieldDescriptors = new DbaseField[fields.length - 1];
364

    
365
                for (int i = 0, j = 0; i < fields.length; i++) {
366
                        if (!inFieldName.equalsIgnoreCase(fields[i].fieldName.trim())) {
367
                                // if this is the last field and we still haven't found the
368
                                // named field
369
                                if ((i == j) && (i == (fields.length - 1))) {
370
                                        System.err.println("Could not find a field named '" +
371
                                                inFieldName + "' for removal");
372

    
373
                                        return retCol;
374
                                }
375

    
376
                                tempFieldDescriptors[j] = fields[i];
377
                                tempFieldDescriptors[j].fieldDataAddress = tempLength;
378
                                tempLength += tempFieldDescriptors[j].fieldLength;
379

    
380
                                // only increment j on non-matching fields
381
                                j++;
382
                        } else {
383
                                retCol = i;
384
                        }
385
                }
386

    
387
                // set the new fields.
388
                fields = tempFieldDescriptors;
389
                headerLength = 33 + (32 * fields.length);
390
                recordLength = tempLength;
391

    
392
                return retCol;
393
        }
394

    
395
        /**
396
         * DOCUMENT ME!
397
         *
398
         * @param inWarn DOCUMENT ME!
399
         *
400
         * @todo addProgessListener handling
401
         */
402
        private void warn(String inWarn) {
403
                if (logger.isLoggable(Level.WARNING)) {
404
                        logger.warning(inWarn);
405
                }
406
        }
407

    
408
        // Retrieve the length of the field at the given index
409

    
410
        /**
411
         * Returns the field length in bytes.
412
         *
413
         * @param inIndex The field index.
414
         *
415
         * @return The length in bytes.
416
         */
417
        public int getFieldLength(int inIndex) {
418
                return fields[inIndex].fieldLength;
419
        }
420

    
421
        // Retrieve the location of the decimal point within the field.
422

    
423
        /**
424
         * Get the decimal count of this field.
425
         *
426
         * @param inIndex The field index.
427
         *
428
         * @return The decimal count.
429
         */
430
        public int getFieldDecimalCount(int inIndex) {
431
                return fields[inIndex].decimalCount;
432
        }
433

    
434
        // Retrieve the Name of the field at the given index
435

    
436
        /**
437
         * Get the field name.
438
         *
439
         * @param inIndex The field index.
440
         *
441
         * @return The name of the field.
442
         */
443
        public String getFieldName(int inIndex) {
444
                return fields[inIndex].fieldName;
445
        }
446

    
447
        // Retrieve the type of field at the given index
448

    
449
        /**
450
         * Get the character class of the field.
451
         *
452
         * @param inIndex The field index.
453
         *
454
         * @return The dbase character representing this field.
455
         */
456
        public char getFieldType(int inIndex) {
457
                return fields[inIndex].fieldType;
458
        }
459

    
460
        /**
461
         * Get the date this file was last updated.
462
         *
463
         * @return The Date last modified.
464
         */
465
        public Date getLastUpdateDate() {
466
                return date;
467
        }
468

    
469
        /**
470
         * Return the number of fields in the records.
471
         *
472
         * @return The number of fields in this table.
473
         */
474
        public int getNumFields() {
475
                return fields.length;
476
        }
477

    
478
        /**
479
         * Return the number of records in the file
480
         *
481
         * @return The number of records in this table.
482
         */
483
        public int getNumRecords() {
484
                return recordCnt;
485
        }
486

    
487
        /**
488
         * Get the length of the records in bytes.
489
         *
490
         * @return The number of bytes per record.
491
         */
492
        public int getRecordLength() {
493
                return recordLength;
494
        }
495

    
496
        /**
497
         * Get the length of the header
498
         *
499
         * @return The length of the header in bytes.
500
         */
501
        public int getHeaderLength() {
502
                return headerLength;
503
        }
504

    
505
        /**
506
         * Read the header data from the DBF file.
507
         *
508
         * @param in DOCUMENT ME!
509
         *
510
         * @throws IOException DOCUMENT ME!
511
         */
512
        public void readHeader(ByteBuffer in) throws IOException {
513
                // type of file.
514
                myFileType = in.get();
515

    
516
                if (myFileType != 0x03) {
517
                        throw new IOException("Unsupported DBF file Type " +
518
                                Integer.toHexString(myFileType));
519
                }
520

    
521
                // parse the update date information.
522
                int tempUpdateYear = (int) in.get();
523
                int tempUpdateMonth = (int) in.get();
524
                int tempUpdateDay = (int) in.get();
525
                tempUpdateYear = tempUpdateYear + 1900;
526

    
527
                Calendar c = Calendar.getInstance();
528
                c.set(Calendar.YEAR, tempUpdateYear);
529
                c.set(Calendar.MONTH, tempUpdateMonth - 1);
530
                c.set(Calendar.DATE, tempUpdateDay);
531
                date = c.getTime();
532

    
533
                // read the number of records.
534
                in.order(ByteOrder.LITTLE_ENDIAN);
535
                recordCnt = in.getInt();
536

    
537
                // read the length of the header structure.
538
                headerLength = in.getShort();
539

    
540
                // read the length of a record
541
                recordLength = in.getShort();
542

    
543
                in.order(ByteOrder.BIG_ENDIAN);
544

    
545
                // skip the reserved bytes in the header.
546
                in.position(in.position() + 20);
547

    
548
                // calculate the number of Fields in the header
549
                fieldCnt = (headerLength - FILE_DESCRIPTOR_SIZE - 1) / FILE_DESCRIPTOR_SIZE;
550

    
551
                // read all of the header records
552
                fields = new DbaseField[fieldCnt];
553

    
554
                for (int i = 0; i < fieldCnt; i++) {
555
                        fields[i] = new DbaseField();
556

    
557
                        // read the field name
558
                        byte[] buffer = new byte[11];
559
                        in.get(buffer);
560
                        fields[i].fieldName = new String(buffer);
561

    
562
                        // read the field type
563
                        fields[i].fieldType = (char) in.get();
564

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

    
568
                        // read the field length in bytes
569
                        int tempLength = (int) in.get();
570

    
571
                        if (tempLength < 0) {
572
                                tempLength = tempLength + 256;
573
                        }
574

    
575
                        fields[i].fieldLength = tempLength;
576

    
577
                        // read the field decimal count in bytes
578
                        fields[i].decimalCount = (int) in.get();
579

    
580
                        // read the reserved bytes.
581
                        in.position(in.position() + 14);
582
                }
583

    
584
                // Last byte is a marker for the end of the field definitions.
585
                in.get();
586
        }
587

    
588
        /**
589
         * Get the largest field size of this table.
590
         *
591
         * @return The largt field size iiin bytes.
592
         */
593
        public int getLargestFieldSize() {
594
                return largestFieldSize;
595
        }
596

    
597
        /**
598
         * Set the number of records in the file
599
         *
600
         * @param inNumRecords The number of records.
601
         */
602
        public void setNumRecords(int inNumRecords) {
603
                recordCnt = inNumRecords;
604
        }
605

    
606
        /**
607
         * Write the header data to the DBF file.
608
         *
609
         * @param out A channel to write to. If you have an OutputStream you can
610
         *                   obtain the correct channel by using
611
         *                   java.nio.Channels.newChannel(OutputStream out).
612
         *
613
         * @throws IOException If errors occur.
614
         */
615
        public void writeHeader(WritableByteChannel out) throws IOException {
616
                // take care of the annoying case where no records have been added...
617
                if (headerLength == -1) {
618
                        headerLength = MINIMUM_HEADER;
619
                }
620

    
621
                ByteBuffer buffer = ByteBuffer.allocateDirect(headerLength);
622
                buffer.order(ByteOrder.LITTLE_ENDIAN);
623

    
624
                // write the output file type.
625
                buffer.put((byte) MAGIC);
626

    
627
                // write the date stuff
628
                Calendar c = Calendar.getInstance();
629
                c.setTime(new Date());
630
                buffer.put((byte) (c.get(Calendar.YEAR) % 100));
631
                buffer.put((byte) (c.get(Calendar.MONTH) + 1));
632
                buffer.put((byte) (c.get(Calendar.DAY_OF_MONTH)));
633

    
634
                // write the number of records in the datafile.
635
                buffer.putInt(recordCnt);
636

    
637
                // write the length of the header structure.
638
                buffer.putShort((short) headerLength);
639

    
640
                // write the length of a record
641
                buffer.putShort((short) recordLength);
642

    
643
                //    // write the reserved bytes in the header
644
                //    for (int i=0; i<20; i++) out.writeByteLE(0);
645
                buffer.position(buffer.position() + 20);
646

    
647
                // write all of the header records
648
                int tempOffset = 0;
649

    
650
                for (int i = 0; i < fields.length; i++) {
651
                        // write the field name
652
                        for (int j = 0; j < 11; j++) {
653
                                if (fields[i].fieldName.length() > j) {
654
                                        buffer.put((byte) fields[i].fieldName.charAt(j));
655
                                } else {
656
                                        buffer.put((byte) 0);
657
                                }
658
                        }
659

    
660
                        // write the field type
661
                        buffer.put((byte) fields[i].fieldType);
662

    
663
                        //    // write the field data address, offset from the start of the record.
664
                        buffer.putInt(tempOffset);
665
                        tempOffset += fields[i].fieldLength;
666

    
667
                        // write the length of the field.
668
                        buffer.put((byte) fields[i].fieldLength);
669

    
670
                        // write the decimal count.
671
                        buffer.put((byte) fields[i].decimalCount);
672

    
673
                        // write the reserved bytes.
674
                        //for (in j=0; jj<14; j++) out.writeByteLE(0);
675
                        buffer.position(buffer.position() + 14);
676
                }
677

    
678
                // write the end of the field definitions marker
679
                buffer.put((byte) 0x0D);
680

    
681
                buffer.position(0);
682

    
683
                int r = buffer.remaining();
684

    
685
                while ((r -= out.write(buffer)) > 0) {
686
                        ; // do nothing
687
                }
688
        }
689

    
690
        /**
691
         * Get a simple representation of this header.
692
         *
693
         * @return A String representing the state of the header.
694
         */
695
        public String toString() {
696
                StringBuffer fs = new StringBuffer();
697

    
698
                for (int i = 0, ii = fields.length; i < ii; i++) {
699
                        DbaseField f = fields[i];
700
                        fs.append(f.fieldName + " " + f.fieldType + " " + f.fieldLength +
701
                                " " + f.decimalCount + " " + f.fieldDataAddress + "\n");
702
                }
703

    
704
                return "DB3 Header\n" + "Date : " + date + "\n" + "Records : " +
705
                recordCnt + "\n" + "Fields : " + fieldCnt + "\n" + fs;
706
        }
707

    
708
        /**
709
         * Crea un DbaseFile.
710
         *
711
         * @return DbaseFileHeaderNIO
712
         *
713
         * @throws IOException .
714
         */
715
        public static DbaseFileHeaderNIO createNewDbaseHeader()
716
                throws IOException {
717
                DbaseFileHeaderNIO header = new DbaseFileHeaderNIO();
718

    
719
                for (int i = 0, ii = 1; i < ii; i++) {
720
                        //AttributeType type = featureType.getAttributeType(i);
721
                        Class colType = Integer.class;
722
                        String colName = "ID";
723
                        int fieldLen = 10;
724

    
725
                        if (fieldLen <= 0) {
726
                                fieldLen = 255;
727
                        }
728

    
729
                        // @todo respect field length
730
                        if ((colType == Integer.class) || (colType == Short.class) ||
731
                                        (colType == Byte.class)) {
732
                                header.addColumn(colName, 'N', Math.min(fieldLen, 10), 0);
733
                        } else if (colType == Long.class) {
734
                                header.addColumn(colName, 'N', Math.min(fieldLen, 19), 0);
735
                        } else if ((colType == Double.class) || (colType == Float.class) ||
736
                                        (colType == Number.class)) {
737
                                int l = Math.min(fieldLen, 33);
738
                                int d = Math.max(l - 2, 0);
739
                                header.addColumn(colName, 'N', l, d);
740
                        } else if (java.util.Date.class.isAssignableFrom(colType)) {
741
                                header.addColumn(colName, 'D', fieldLen, 0);
742
                        } else if (colType == Boolean.class) {
743
                                header.addColumn(colName, 'L', 1, 0);
744
                        } else if (CharSequence.class.isAssignableFrom(colType)) {
745
                                // Possible fix for GEOT-42 : ArcExplorer doesn't like 0 length
746
                                // ensure that maxLength is at least 1
747
                                header.addColumn(colName, 'C', Math.min(254, fieldLen), 0);
748
                        } else if (Geometry.class.isAssignableFrom(colType)) {
749
                                continue;
750
                        } else {
751
                                throw new IOException("Unable to write : " + colType.getName());
752
                        }
753
                }
754

    
755
                return header;
756
        }
757

    
758
        /**
759
         * DOCUMENT ME!
760
         *
761
         * @param sds DOCUMENT ME!
762
         *
763
         * @return DOCUMENT ME!
764
         *
765
         * @throws IOException DOCUMENT ME!
766
         */
767
        public static DbaseFileHeaderNIO createDbaseHeader(SelectableDataSource sds)
768
                throws IOException {
769
                DbaseFileHeaderNIO header = new DbaseFileHeaderNIO();
770

    
771
                try {
772
                        for (int i = 0, ii = sds.getFieldCount(); i < ii; i++) {
773
                                //AttributeType type = featureType.getAttributeType(i);
774
                                Value value = sds.getFieldValue(0, i);
775

    
776
                                //int type = sds.getFieldType(i);
777
                                int type = getTypeValue(value);
778
                                String colName = sds.getFieldName(i);
779

    
780
                                ///int fieldLen = ((DBFDriver)sds.getDriver()).getFieldLength(i);
781
                                int fieldLen = 100; //TODO aqu? el tama?o no es correcto hay que calcularlo, ahora mismo est? puesto a pi??n.
782
                                int decimales = 5;
783

    
784
                                //  if (fieldLen <= 0) {
785
                                //       fieldLen = 255;
786
                                //   }
787
                                // @todo respect field length
788
                                switch (type) {
789
                                        case (Value.DOUBLE):
790
                                        case (Value.INTEGER):
791
                                        case (Value.DECIMAL):
792
                                                header.addColumn(colName, 'N', Math.min(fieldLen, 10),
793
                                                        decimales);
794

    
795
                                                break;
796

    
797
                                        case (Value.DATE):
798
                                                header.addColumn(colName, 'D', fieldLen, 0);
799

    
800
                                                break;
801

    
802
                                        case (Value.BOOLEAN):
803
                                                header.addColumn(colName, 'L', 1, 0);
804

    
805
                                                break;
806

    
807
                                        case (Value.STRING):
808
                                                header.addColumn(colName, 'C', Math.min(254, fieldLen),
809
                                                        0);
810
                                }
811
                        }
812
                } catch (DriverException e) {
813
                        e.printStackTrace();
814
                } catch (SemanticException e) {
815
                        e.printStackTrace();
816
                }
817

    
818
                return header;
819
        }
820

    
821
        /**
822
         * DOCUMENT ME!
823
         *
824
         * @param value DOCUMENT ME!
825
         *
826
         * @return DOCUMENT ME!
827
         *
828
         * @throws SemanticException DOCUMENT ME!
829
         */
830
        private static int getTypeValue(Value value) throws SemanticException {
831
                if (value.getClass().getName().equals("com.hardcode.gdbms.engine.values.BooleanValue")) {
832
                        return Value.BOOLEAN;
833
                } else if (value.getClass().getName().equals("com.hardcode.gdbms.engine.values.DateValue")) {
834
                        return Value.DATE;
835
                } else if (value.getClass().getName().equals("com.hardcode.gdbms.engine.values.DoubleValue")) {
836
                        return Value.DOUBLE;
837
                } else if (value.getClass().getName().equals("com.hardcode.gdbms.engine.values.FloatValue")) {
838
                        return Value.FLOAT;
839
                } else if (value.getClass().getName().equals("com.hardcode.gdbms.engine.values.IntValue")) {
840
                        return Value.INTEGER;
841
                } else if (value.getClass().getName().equals("com.hardcode.gdbms.engine.values.LongValue")) {
842
                        return Value.LONG;
843
                } else if (value.getClass().getName().equals("com.hardcode.gdbms.engine.values.StringValue")) {
844
                        return Value.STRING;
845
                } else if (value.getClass().getName().equals("com.hardcode.gdbms.engine.values.NullValue")) {
846
                        return Value.NULL;
847
                }
848

    
849
                // default:
850
                throw new SemanticException(
851
                        "Unexpected className in getTypeValue (GDBMS)");
852
        }
853

    
854
        /**
855
         * Class for holding the information assicated with a record.
856
         */
857
        class DbaseField {
858
                // Field Name
859
                String fieldName;
860

    
861
                // Field Type (C N L D or M)
862
                char fieldType;
863

    
864
                // Field Data Address offset from the start of the record.
865
                int fieldDataAddress;
866

    
867
                // Length of the data in bytes
868
                int fieldLength;
869

    
870
                // Field decimal count in Binary, indicating where the decimal is
871
                int decimalCount;
872
        }
873
}