Statistics
| Revision:

svn-gvsig-desktop / tags / v1_1_Build_1007 / libraries / libFMap / src / com / iver / cit / gvsig / fmap / drivers / shp / DbaseFileHeaderNIO.java @ 12478

History | View | Annotate | Download (24.5 KB)

1 377 fjp
/*
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 1100 fjp
/* 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 377 fjp
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 3672 fjp
import java.nio.channels.FileChannel;
70 377 fjp
import java.nio.channels.ReadableByteChannel;
71 1773 fernando
import java.sql.Types;
72 377 fjp
import java.util.Calendar;
73
import java.util.Date;
74
import java.util.logging.Level;
75
import java.util.logging.Logger;
76
77 2573 caballero
import com.hardcode.gdbms.engine.data.DataSource;
78 1828 fernando
import com.hardcode.gdbms.engine.data.driver.DriverException;
79 3983 fjp
import com.iver.cit.gvsig.fmap.drivers.FieldDescription;
80 4430 fjp
import com.iver.utiles.bigfile.BigByteBuffer2;
81 1828 fernando
import com.vividsolutions.jts.geom.Geometry;
82 377 fjp
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 1005 vcaballero
        // Constant for the size of a record
89
        private static final int FILE_DESCRIPTOR_SIZE = 32;
90 377 fjp
91 1005 vcaballero
        // type of the file, must be 03h
92
        private static final byte MAGIC = 0x03;
93 8765 jjdelcerro
94 1005 vcaballero
        private static final int MINIMUM_HEADER = 33;
95 377 fjp
96 1005 vcaballero
        // Date the file was last updated.
97
        private Date date = new Date();
98 8765 jjdelcerro
99 1005 vcaballero
        private int recordCnt = 0;
100 8765 jjdelcerro
101 1005 vcaballero
        private int fieldCnt = 0;
102 8765 jjdelcerro
103 1005 vcaballero
        private int myFileType = 0;
104 377 fjp
105 1005 vcaballero
        // 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 377 fjp
109 1005 vcaballero
        // 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 8765 jjdelcerro
113 1005 vcaballero
        private int largestFieldSize = 0;
114 8765 jjdelcerro
115 1005 vcaballero
        private Logger logger = Logger.getLogger("org.geotools.data.shapefile");
116 377 fjp
117 1005 vcaballero
        // collection of header records.
118
        // lets start out with a zero-length array, just in case
119
        private DbaseField[] fields = null; // new DbaseField[0];
120 377 fjp
121 1005 vcaballero
        /**
122
         * Lee del buffer.
123 8765 jjdelcerro
         *
124 1005 vcaballero
         * @param buffer .
125
         * @param channel .
126 8765 jjdelcerro
         *
127 1005 vcaballero
         * @throws IOException .
128
         * @throws EOFException .
129
         */
130
        private void read(ByteBuffer buffer, ReadableByteChannel channel)
131 8765 jjdelcerro
                        throws IOException {
132 1005 vcaballero
                while (buffer.remaining() > 0) {
133
                        if (channel.read(buffer) == -1) {
134
                                throw new EOFException("Premature end of file");
135
                        }
136
                }
137
        }
138 377 fjp
139 1005 vcaballero
        /**
140
         * Determine the most appropriate Java Class for representing the data in
141
         * the field.
142 8765 jjdelcerro
         *
143 1005 vcaballero
         * <PRE>
144 8765 jjdelcerro
         *
145
         * All packages are java.lang unless otherwise specified. C (Character) ->
146
         * String N (Numeric) -> Integer or Double (depends on field's decimal
147
         * count) F (Floating) -> Double L (Logical) -> Boolean D (Date) ->
148
         * java.util.Date Unknown -> String
149
         *
150 1005 vcaballero
         * </PRE>
151 8765 jjdelcerro
         *
152
         * @param i
153
         *            The index of the field, from 0 to <CODE>getNumFields() - 1</CODE> .
154
         *
155 1005 vcaballero
         * @return A Class which closely represents the dbase field type.
156
         */
157
        public Class getFieldClass(int i) {
158
                Class typeClass = null;
159 377 fjp
160 1005 vcaballero
                switch (fields[i].fieldType) {
161 8765 jjdelcerro
                case 'C':
162
                        typeClass = String.class;
163 377 fjp
164 8765 jjdelcerro
                        break;
165 377 fjp
166 8765 jjdelcerro
                case 'N':
167 377 fjp
168 8765 jjdelcerro
                        if (fields[i].decimalCount == 0) {
169
                                typeClass = Integer.class;
170
                        } else {
171
                                typeClass = Double.class;
172
                        }
173 377 fjp
174 8765 jjdelcerro
                        break;
175 377 fjp
176 8765 jjdelcerro
                case 'F':
177
                        typeClass = Double.class;
178 377 fjp
179 8765 jjdelcerro
                        break;
180 377 fjp
181 8765 jjdelcerro
                case 'L':
182
                        typeClass = Boolean.class;
183 377 fjp
184 8765 jjdelcerro
                        break;
185 377 fjp
186 8765 jjdelcerro
                case 'D':
187
                        typeClass = Date.class;
188 377 fjp
189 8765 jjdelcerro
                        break;
190 377 fjp
191 8765 jjdelcerro
                default:
192
                        typeClass = String.class;
193 377 fjp
194 8765 jjdelcerro
                        break;
195 1005 vcaballero
                }
196 377 fjp
197 1005 vcaballero
                return typeClass;
198
        }
199 377 fjp
200 1005 vcaballero
        /**
201
         * Add a column to this DbaseFileHeader. The type is one of (C N L or D)
202 8765 jjdelcerro
         * character, number, logical(true/false), or date. The Field length is the
203
         * total length in bytes reserved for this column. The decimal count only
204
         * applies to numbers(N), and floating point values (F), and refers to the
205
         * number of characters to reserve after the decimal point. <B>Don't expect
206
         * miracles from this...</B>
207
         *
208 1005 vcaballero
         * <PRE>
209 8765 jjdelcerro
         *
210
         * Field Type MaxLength ---------- --------- C 254 D 8 F 20 N 18
211
         *
212 1005 vcaballero
         * </PRE>
213 8765 jjdelcerro
         *
214
         * @param inFieldName
215
         *            The name of the new field, must be less than 10 characters or
216
         *            it gets truncated.
217
         * @param inFieldType
218
         *            A character representing the dBase field, ( see above ). Case
219
         *            insensitive.
220
         * @param inFieldLength
221
         *            The length of the field, in bytes ( see above )
222
         * @param inDecimalCount
223
         *            For numeric fields, the number of decimal places to track.
224 1005 vcaballero
         */
225
        public void addColumn(String inFieldName, char inFieldType,
226 8765 jjdelcerro
                        int inFieldLength, int inDecimalCount) {
227
                /*
228
                 * if (inFieldLength <=0) { throw new DbaseFileException("field length <=
229
                 * 0"); }
230 1005 vcaballero
                 */
231
                if (fields == null) {
232
                        fields = new DbaseField[0];
233
                }
234 377 fjp
235 8765 jjdelcerro
                int tempLength = 1; // the length is used for the offset, and there is a
236
                // * for deleted as the first byte
237 1005 vcaballero
                DbaseField[] tempFieldDescriptors = new DbaseField[fields.length + 1];
238 377 fjp
239 1005 vcaballero
                for (int i = 0; i < fields.length; i++) {
240
                        fields[i].fieldDataAddress = tempLength;
241
                        tempLength = tempLength + fields[i].fieldLength;
242
                        tempFieldDescriptors[i] = fields[i];
243
                }
244 377 fjp
245 1005 vcaballero
                tempFieldDescriptors[fields.length] = new DbaseField();
246
                tempFieldDescriptors[fields.length].fieldLength = inFieldLength;
247
                tempFieldDescriptors[fields.length].decimalCount = inDecimalCount;
248
                tempFieldDescriptors[fields.length].fieldDataAddress = tempLength;
249 377 fjp
250 1005 vcaballero
                // set the field name
251
                String tempFieldName = inFieldName;
252 377 fjp
253 1005 vcaballero
                if (tempFieldName == null) {
254
                        tempFieldName = "NoName";
255
                }
256 377 fjp
257 1005 vcaballero
                // Fix for GEOT-42, ArcExplorer will not handle field names > 10 chars
258
                // Sorry folks.
259
                if (tempFieldName.length() > 10) {
260
                        tempFieldName = tempFieldName.substring(0, 10);
261 8765 jjdelcerro
                        warn("FieldName " + inFieldName
262
                                        + " is longer than 10 characters, truncating to "
263
                                        + tempFieldName);
264 1005 vcaballero
                }
265 377 fjp
266 1005 vcaballero
                tempFieldDescriptors[fields.length].fieldName = tempFieldName;
267 377 fjp
268 1005 vcaballero
                // the field type
269
                if ((inFieldType == 'C') || (inFieldType == 'c')) {
270
                        tempFieldDescriptors[fields.length].fieldType = 'C';
271 377 fjp
272 1005 vcaballero
                        if (inFieldLength > 254) {
273 8765 jjdelcerro
                                warn("Field Length for "
274
                                                + inFieldName
275
                                                + " set to "
276
                                                + inFieldLength
277
                                                + " Which is longer than 254, not consistent with dbase III");
278 1005 vcaballero
                        }
279
                } else if ((inFieldType == 'S') || (inFieldType == 's')) {
280
                        tempFieldDescriptors[fields.length].fieldType = 'C';
281 8765 jjdelcerro
                        warn("Field type for "
282
                                        + inFieldName
283
                                        + " set to S which is flat out wrong people!, I am setting this to C, in the hopes you meant character.");
284 377 fjp
285 1005 vcaballero
                        if (inFieldLength > 254) {
286 8765 jjdelcerro
                                warn("Field Length for "
287
                                                + inFieldName
288
                                                + " set to "
289
                                                + inFieldLength
290
                                                + " Which is longer than 254, not consistent with dbase III");
291 1005 vcaballero
                        }
292 377 fjp
293 1005 vcaballero
                        tempFieldDescriptors[fields.length].fieldLength = 8;
294
                } else if ((inFieldType == 'D') || (inFieldType == 'd')) {
295
                        tempFieldDescriptors[fields.length].fieldType = 'D';
296 377 fjp
297 1005 vcaballero
                        if (inFieldLength != 8) {
298 8765 jjdelcerro
                                warn("Field Length for " + inFieldName + " set to "
299
                                                + inFieldLength + " Setting to 8 digets YYYYMMDD");
300 1005 vcaballero
                        }
301 377 fjp
302 1005 vcaballero
                        tempFieldDescriptors[fields.length].fieldLength = 8;
303
                } else if ((inFieldType == 'F') || (inFieldType == 'f')) {
304
                        tempFieldDescriptors[fields.length].fieldType = 'F';
305 377 fjp
306 1005 vcaballero
                        if (inFieldLength > 20) {
307 8765 jjdelcerro
                                warn("Field Length for "
308
                                                + inFieldName
309
                                                + " set to "
310
                                                + inFieldLength
311
                                                + " Preserving length, but should be set to Max of 20 not valid for dbase IV, and UP specification, not present in dbaseIII.");
312 1005 vcaballero
                        }
313
                } else if ((inFieldType == 'N') || (inFieldType == 'n')) {
314
                        tempFieldDescriptors[fields.length].fieldType = 'N';
315 377 fjp
316 1005 vcaballero
                        if (inFieldLength > 18) {
317 8765 jjdelcerro
                                warn("Field Length for "
318
                                                + inFieldName
319
                                                + " set to "
320
                                                + inFieldLength
321
                                                + " Preserving length, but should be set to Max of 18 for dbase III specification.");
322 1005 vcaballero
                        }
323 377 fjp
324 1005 vcaballero
                        if (inDecimalCount < 0) {
325 8765 jjdelcerro
                                warn("Field Decimal Position for " + inFieldName + " set to "
326
                                                + inDecimalCount
327
                                                + " Setting to 0 no decimal data will be saved.");
328 1005 vcaballero
                                tempFieldDescriptors[fields.length].decimalCount = 0;
329
                        }
330 377 fjp
331 1005 vcaballero
                        if (inDecimalCount > (inFieldLength - 1)) {
332 8765 jjdelcerro
                                warn("Field Decimal Position for " + inFieldName + " set to "
333
                                                + inDecimalCount + " Setting to " + (inFieldLength - 1)
334
                                                + " no non decimal data will be saved.");
335
                                tempFieldDescriptors[fields.length].decimalCount = inFieldLength - 1;
336 1005 vcaballero
                        }
337
                } else if ((inFieldType == 'L') || (inFieldType == 'l')) {
338
                        tempFieldDescriptors[fields.length].fieldType = 'L';
339 377 fjp
340 1005 vcaballero
                        if (inFieldLength != 1) {
341 8765 jjdelcerro
                                warn("Field Length for " + inFieldName + " set to "
342
                                                + inFieldLength
343
                                                + " Setting to length of 1 for logical fields.");
344 1005 vcaballero
                        }
345 377 fjp
346 1005 vcaballero
                        tempFieldDescriptors[fields.length].fieldLength = 1;
347
                } else {
348 8765 jjdelcerro
                        // throw new DbaseFileException("Undefined field type "+inFieldType
349
                        // + " For column "+inFieldName);
350 1005 vcaballero
                }
351 377 fjp
352 1005 vcaballero
                // the length of a record
353 8765 jjdelcerro
                tempLength = tempLength
354
                                + tempFieldDescriptors[fields.length].fieldLength;
355 377 fjp
356 1005 vcaballero
                // set the new fields.
357
                fields = tempFieldDescriptors;
358
                fieldCnt = fields.length;
359
                headerLength = MINIMUM_HEADER + (32 * fields.length);
360
                recordLength = tempLength;
361
        }
362 377 fjp
363 1005 vcaballero
        /**
364
         * Remove a column from this DbaseFileHeader.
365 8765 jjdelcerro
         *
366
         * @param inFieldName
367
         *            The name of the field, will ignore case and trim.
368
         *
369 1005 vcaballero
         * @return index of the removed column, -1 if no found
370 8765 jjdelcerro
         *
371
         * @todo This is really ugly, don't know who wrote it, but it needs fixin...
372 1005 vcaballero
         */
373
        public int removeColumn(String inFieldName) {
374
                int retCol = -1;
375
                int tempLength = 1;
376
                DbaseField[] tempFieldDescriptors = new DbaseField[fields.length - 1];
377 377 fjp
378 1005 vcaballero
                for (int i = 0, j = 0; i < fields.length; i++) {
379
                        if (!inFieldName.equalsIgnoreCase(fields[i].fieldName.trim())) {
380
                                // if this is the last field and we still haven't found the
381
                                // named field
382
                                if ((i == j) && (i == (fields.length - 1))) {
383 8765 jjdelcerro
                                        System.err.println("Could not find a field named '"
384
                                                        + inFieldName + "' for removal");
385 377 fjp
386 1005 vcaballero
                                        return retCol;
387
                                }
388 377 fjp
389 1005 vcaballero
                                tempFieldDescriptors[j] = fields[i];
390
                                tempFieldDescriptors[j].fieldDataAddress = tempLength;
391
                                tempLength += tempFieldDescriptors[j].fieldLength;
392 377 fjp
393 1005 vcaballero
                                // only increment j on non-matching fields
394
                                j++;
395
                        } else {
396
                                retCol = i;
397
                        }
398
                }
399 377 fjp
400 1005 vcaballero
                // set the new fields.
401
                fields = tempFieldDescriptors;
402
                headerLength = 33 + (32 * fields.length);
403
                recordLength = tempLength;
404 377 fjp
405 1005 vcaballero
                return retCol;
406
        }
407 377 fjp
408 1005 vcaballero
        /**
409
         * DOCUMENT ME!
410 8765 jjdelcerro
         *
411
         * @param inWarn
412
         *            DOCUMENT ME!
413
         *
414 1005 vcaballero
         * @todo addProgessListener handling
415
         */
416
        private void warn(String inWarn) {
417
                if (logger.isLoggable(Level.WARNING)) {
418
                        logger.warning(inWarn);
419
                }
420
        }
421 377 fjp
422 1005 vcaballero
        // Retrieve the length of the field at the given index
423 377 fjp
424 1005 vcaballero
        /**
425
         * Returns the field length in bytes.
426 8765 jjdelcerro
         *
427
         * @param inIndex
428
         *            The field index.
429
         *
430 1005 vcaballero
         * @return The length in bytes.
431
         */
432
        public int getFieldLength(int inIndex) {
433
                return fields[inIndex].fieldLength;
434
        }
435 377 fjp
436 1005 vcaballero
        // Retrieve the location of the decimal point within the field.
437 377 fjp
438 1005 vcaballero
        /**
439
         * Get the decimal count of this field.
440 8765 jjdelcerro
         *
441
         * @param inIndex
442
         *            The field index.
443
         *
444 1005 vcaballero
         * @return The decimal count.
445
         */
446
        public int getFieldDecimalCount(int inIndex) {
447
                return fields[inIndex].decimalCount;
448
        }
449 377 fjp
450 1005 vcaballero
        // Retrieve the Name of the field at the given index
451 377 fjp
452 1005 vcaballero
        /**
453
         * Get the field name.
454 8765 jjdelcerro
         *
455
         * @param inIndex
456
         *            The field index.
457
         *
458 1005 vcaballero
         * @return The name of the field.
459
         */
460
        public String getFieldName(int inIndex) {
461
                return fields[inIndex].fieldName;
462
        }
463 377 fjp
464 1005 vcaballero
        // Retrieve the type of field at the given index
465 377 fjp
466 1005 vcaballero
        /**
467
         * Get the character class of the field.
468 8765 jjdelcerro
         *
469
         * @param inIndex
470
         *            The field index.
471
         *
472 1005 vcaballero
         * @return The dbase character representing this field.
473
         */
474
        public char getFieldType(int inIndex) {
475
                return fields[inIndex].fieldType;
476
        }
477 377 fjp
478 1005 vcaballero
        /**
479
         * Get the date this file was last updated.
480 8765 jjdelcerro
         *
481 1005 vcaballero
         * @return The Date last modified.
482
         */
483
        public Date getLastUpdateDate() {
484
                return date;
485
        }
486 377 fjp
487 1005 vcaballero
        /**
488
         * Return the number of fields in the records.
489 8765 jjdelcerro
         *
490 1005 vcaballero
         * @return The number of fields in this table.
491
         */
492
        public int getNumFields() {
493 4416 fjp
                if (fields == null)
494
                        return 0;
495 1005 vcaballero
                return fields.length;
496
        }
497 377 fjp
498 1005 vcaballero
        /**
499
         * Return the number of records in the file
500 8765 jjdelcerro
         *
501 1005 vcaballero
         * @return The number of records in this table.
502
         */
503
        public int getNumRecords() {
504
                return recordCnt;
505
        }
506 377 fjp
507 1005 vcaballero
        /**
508
         * Get the length of the records in bytes.
509 8765 jjdelcerro
         *
510 1005 vcaballero
         * @return The number of bytes per record.
511
         */
512
        public int getRecordLength() {
513
                return recordLength;
514
        }
515 377 fjp
516 1005 vcaballero
        /**
517
         * Get the length of the header
518 8765 jjdelcerro
         *
519 1005 vcaballero
         * @return The length of the header in bytes.
520
         */
521
        public int getHeaderLength() {
522
                return headerLength;
523
        }
524 377 fjp
525 1005 vcaballero
        /**
526
         * Read the header data from the DBF file.
527 8765 jjdelcerro
         *
528
         * @param in
529
         *            DOCUMENT ME!
530
         *
531
         * @throws IOException
532
         *             DOCUMENT ME!
533 1005 vcaballero
         */
534 4430 fjp
        public void readHeader(BigByteBuffer2 in) throws IOException {
535 1005 vcaballero
                // type of file.
536
                myFileType = in.get();
537 377 fjp
538 1005 vcaballero
                if (myFileType != 0x03) {
539 8765 jjdelcerro
                        throw new IOException("Unsupported DBF file Type "
540
                                        + Integer.toHexString(myFileType));
541 1005 vcaballero
                }
542 377 fjp
543 1005 vcaballero
                // parse the update date information.
544
                int tempUpdateYear = (int) in.get();
545
                int tempUpdateMonth = (int) in.get();
546
                int tempUpdateDay = (int) in.get();
547
                tempUpdateYear = tempUpdateYear + 1900;
548 377 fjp
549 1005 vcaballero
                Calendar c = Calendar.getInstance();
550
                c.set(Calendar.YEAR, tempUpdateYear);
551
                c.set(Calendar.MONTH, tempUpdateMonth - 1);
552
                c.set(Calendar.DATE, tempUpdateDay);
553
                date = c.getTime();
554 377 fjp
555 1005 vcaballero
                // read the number of records.
556
                in.order(ByteOrder.LITTLE_ENDIAN);
557
                recordCnt = in.getInt();
558 377 fjp
559 1005 vcaballero
                // read the length of the header structure.
560
                headerLength = in.getShort();
561 377 fjp
562 1005 vcaballero
                // read the length of a record
563
                recordLength = in.getShort();
564
565 377 fjp
                in.order(ByteOrder.BIG_ENDIAN);
566
567 1005 vcaballero
                // skip the reserved bytes in the header.
568
                in.position(in.position() + 20);
569 377 fjp
570 1005 vcaballero
                // calculate the number of Fields in the header
571 8765 jjdelcerro
                fieldCnt = (headerLength - FILE_DESCRIPTOR_SIZE - 1)
572
                                / FILE_DESCRIPTOR_SIZE;
573 377 fjp
574 1005 vcaballero
                // read all of the header records
575
                fields = new DbaseField[fieldCnt];
576 377 fjp
577 1005 vcaballero
                for (int i = 0; i < fieldCnt; i++) {
578
                        fields[i] = new DbaseField();
579 377 fjp
580 1005 vcaballero
                        // read the field name
581
                        byte[] buffer = new byte[11];
582
                        in.get(buffer);
583
                        fields[i].fieldName = new String(buffer);
584 377 fjp
585 1005 vcaballero
                        // read the field type
586
                        fields[i].fieldType = (char) in.get();
587 377 fjp
588 1005 vcaballero
                        // read the field data address, offset from the start of the record.
589
                        fields[i].fieldDataAddress = in.getInt();
590 377 fjp
591 1005 vcaballero
                        // read the field length in bytes
592
                        int tempLength = (int) in.get();
593 377 fjp
594 1005 vcaballero
                        if (tempLength < 0) {
595
                                tempLength = tempLength + 256;
596
                        }
597 377 fjp
598 1005 vcaballero
                        fields[i].fieldLength = tempLength;
599 377 fjp
600 1005 vcaballero
                        // read the field decimal count in bytes
601
                        fields[i].decimalCount = (int) in.get();
602 377 fjp
603 1005 vcaballero
                        // read the reserved bytes.
604
                        in.position(in.position() + 14);
605
                }
606 377 fjp
607 1005 vcaballero
                // Last byte is a marker for the end of the field definitions.
608
                in.get();
609
        }
610 377 fjp
611 1005 vcaballero
        /**
612
         * Get the largest field size of this table.
613 8765 jjdelcerro
         *
614 1005 vcaballero
         * @return The largt field size iiin bytes.
615
         */
616
        public int getLargestFieldSize() {
617
                return largestFieldSize;
618
        }
619 377 fjp
620 1005 vcaballero
        /**
621
         * Set the number of records in the file
622 8765 jjdelcerro
         *
623
         * @param inNumRecords
624
         *            The number of records.
625 1005 vcaballero
         */
626
        public void setNumRecords(int inNumRecords) {
627
                recordCnt = inNumRecords;
628
        }
629 377 fjp
630 1005 vcaballero
        /**
631
         * Write the header data to the DBF file.
632 8765 jjdelcerro
         *
633
         * @param out
634
         *            A channel to write to. If you have an OutputStream you can
635
         *            obtain the correct channel by using
636
         *            java.nio.Channels.newChannel(OutputStream out).
637
         *
638
         * @throws IOException
639
         *             If errors occur.
640 1005 vcaballero
         */
641 3672 fjp
        public void writeHeader(FileChannel out) throws IOException {
642 1005 vcaballero
                // take care of the annoying case where no records have been added...
643
                if (headerLength == -1) {
644
                        headerLength = MINIMUM_HEADER;
645
                }
646 377 fjp
647 3672 fjp
                // Desde el principio
648
                out.position(0);
649 8765 jjdelcerro
650 1005 vcaballero
                ByteBuffer buffer = ByteBuffer.allocateDirect(headerLength);
651
                buffer.order(ByteOrder.LITTLE_ENDIAN);
652 377 fjp
653 1005 vcaballero
                // write the output file type.
654
                buffer.put((byte) MAGIC);
655 377 fjp
656 1005 vcaballero
                // write the date stuff
657
                Calendar c = Calendar.getInstance();
658
                c.setTime(new Date());
659
                buffer.put((byte) (c.get(Calendar.YEAR) % 100));
660
                buffer.put((byte) (c.get(Calendar.MONTH) + 1));
661
                buffer.put((byte) (c.get(Calendar.DAY_OF_MONTH)));
662 377 fjp
663 1005 vcaballero
                // write the number of records in the datafile.
664
                buffer.putInt(recordCnt);
665 377 fjp
666 1005 vcaballero
                // write the length of the header structure.
667
                buffer.putShort((short) headerLength);
668 377 fjp
669 1005 vcaballero
                // write the length of a record
670
                buffer.putShort((short) recordLength);
671 377 fjp
672 8765 jjdelcerro
                // // write the reserved bytes in the header
673
                // for (int i=0; i<20; i++) out.writeByteLE(0);
674 1005 vcaballero
                buffer.position(buffer.position() + 20);
675 377 fjp
676 1005 vcaballero
                // write all of the header records
677
                int tempOffset = 0;
678 377 fjp
679 8765 jjdelcerro
                if (fields != null) {
680 4416 fjp
                        for (int i = 0; i < fields.length; i++) {
681
                                // write the field name
682
                                for (int j = 0; j < 11; j++) {
683
                                        if (fields[i].fieldName.length() > j) {
684
                                                buffer.put((byte) fields[i].fieldName.charAt(j));
685
                                        } else {
686
                                                buffer.put((byte) 0);
687
                                        }
688 1005 vcaballero
                                }
689 8765 jjdelcerro
690 4416 fjp
                                // write the field type
691
                                buffer.put((byte) fields[i].fieldType);
692 8765 jjdelcerro
693
                                // // write the field data address, offset from the start of the
694
                                // record.
695 4416 fjp
                                buffer.putInt(tempOffset);
696
                                tempOffset += fields[i].fieldLength;
697 8765 jjdelcerro
698 4416 fjp
                                // write the length of the field.
699
                                buffer.put((byte) fields[i].fieldLength);
700 8765 jjdelcerro
701 4416 fjp
                                // write the decimal count.
702
                                buffer.put((byte) fields[i].decimalCount);
703 8765 jjdelcerro
704 4416 fjp
                                // write the reserved bytes.
705 8765 jjdelcerro
                                // for (in j=0; jj<14; j++) out.writeByteLE(0);
706 4416 fjp
                                buffer.position(buffer.position() + 14);
707 1005 vcaballero
                        }
708
                }
709
                // write the end of the field definitions marker
710
                buffer.put((byte) 0x0D);
711 377 fjp
712 1005 vcaballero
                buffer.position(0);
713 377 fjp
714 1005 vcaballero
                int r = buffer.remaining();
715 377 fjp
716 1005 vcaballero
                while ((r -= out.write(buffer)) > 0) {
717
                        ; // do nothing
718
                }
719
        }
720 377 fjp
721 1005 vcaballero
        /**
722
         * Get a simple representation of this header.
723 8765 jjdelcerro
         *
724 1005 vcaballero
         * @return A String representing the state of the header.
725
         */
726
        public String toString() {
727
                StringBuffer fs = new StringBuffer();
728 377 fjp
729 1005 vcaballero
                for (int i = 0, ii = fields.length; i < ii; i++) {
730
                        DbaseField f = fields[i];
731 8765 jjdelcerro
                        fs.append(f.fieldName + " " + f.fieldType + " " + f.fieldLength
732
                                        + " " + f.decimalCount + " " + f.fieldDataAddress + "\n");
733 1005 vcaballero
                }
734 377 fjp
735 8765 jjdelcerro
                return "DB3 Header\n" + "Date : " + date + "\n" + "Records : "
736
                                + recordCnt + "\n" + "Fields : " + fieldCnt + "\n" + fs;
737 1005 vcaballero
        }
738 377 fjp
739 1005 vcaballero
        /**
740
         * Crea un DbaseFile.
741 8765 jjdelcerro
         *
742 1005 vcaballero
         * @return DbaseFileHeaderNIO
743 8765 jjdelcerro
         *
744 1005 vcaballero
         * @throws IOException .
745
         */
746 8765 jjdelcerro
        public static DbaseFileHeaderNIO createNewDbaseHeader() throws IOException {
747 1005 vcaballero
                DbaseFileHeaderNIO header = new DbaseFileHeaderNIO();
748 377 fjp
749 1005 vcaballero
                for (int i = 0, ii = 1; i < ii; i++) {
750 8765 jjdelcerro
                        // AttributeType type = featureType.getAttributeType(i);
751 1005 vcaballero
                        Class colType = Integer.class;
752
                        String colName = "ID";
753
                        int fieldLen = 10;
754 377 fjp
755 1005 vcaballero
                        if (fieldLen <= 0) {
756
                                fieldLen = 255;
757
                        }
758 377 fjp
759 1005 vcaballero
                        // @todo respect field length
760 8765 jjdelcerro
                        if ((colType == Integer.class) || (colType == Short.class)
761
                                        || (colType == Byte.class)) {
762 1005 vcaballero
                                header.addColumn(colName, 'N', Math.min(fieldLen, 10), 0);
763
                        } else if (colType == Long.class) {
764
                                header.addColumn(colName, 'N', Math.min(fieldLen, 19), 0);
765 8765 jjdelcerro
                        } else if ((colType == Double.class) || (colType == Float.class)
766
                                        || (colType == Number.class)) {
767 1005 vcaballero
                                int l = Math.min(fieldLen, 33);
768
                                int d = Math.max(l - 2, 0);
769
                                header.addColumn(colName, 'N', l, d);
770
                        } else if (java.util.Date.class.isAssignableFrom(colType)) {
771
                                header.addColumn(colName, 'D', fieldLen, 0);
772
                        } else if (colType == Boolean.class) {
773
                                header.addColumn(colName, 'L', 1, 0);
774
                        } else if (CharSequence.class.isAssignableFrom(colType)) {
775
                                // Possible fix for GEOT-42 : ArcExplorer doesn't like 0 length
776
                                // ensure that maxLength is at least 1
777
                                header.addColumn(colName, 'C', Math.min(254, fieldLen), 0);
778
                        } else if (Geometry.class.isAssignableFrom(colType)) {
779
                                continue;
780
                        } else {
781
                                throw new IOException("Unable to write : " + colType.getName());
782
                        }
783
                }
784 377 fjp
785 1005 vcaballero
                return header;
786
        }
787 377 fjp
788 8765 jjdelcerro
        public static DbaseFileHeaderNIO createDbaseHeader(DataSource ds)
789
                        throws IOException {
790
                try {
791
                        int[] fieldTypes = new int[ds.getFieldCount()];
792
                        int[] fieldLength = new int[fieldTypes.length];
793
                        for (int i = 0; i < fieldTypes.length; i++) {
794
                                fieldTypes[i] = ds.getFieldType(i);
795
                                fieldLength[i] = ds.getFieldWidth(i);
796
                        }
797 2867 jmorell
798 8765 jjdelcerro
                        return createDbaseHeader(ds.getFieldNames(), fieldTypes,
799
                                        fieldLength);
800
                } catch (DriverException e) {
801
                        // TODO Auto-generated catch block
802
                        e.printStackTrace();
803
                        return null;
804
                }
805
        }
806
807 1005 vcaballero
        /**
808
         * DOCUMENT ME!
809 8765 jjdelcerro
         *
810
         * @param sds
811
         *            DOCUMENT ME!
812
         *
813 1160 vcaballero
         * @return DOCUMENT ME!
814 8765 jjdelcerro
         *
815
         * @throws IOException
816
         *             DOCUMENT ME!
817 1005 vcaballero
         */
818 8765 jjdelcerro
        public static DbaseFileHeaderNIO createDbaseHeader(String[] fieldNames,
819
                        int[] fieldTypes, int[] fieldLength) throws IOException {
820 1160 vcaballero
                DbaseFileHeaderNIO header = new DbaseFileHeaderNIO();
821 377 fjp
822 8765 jjdelcerro
                for (int i = 0, ii = fieldNames.length; i < ii; i++) {
823 1658 fjp
824 8765 jjdelcerro
                        int type = fieldTypes[i];
825
                        String colName = fieldNames[i];
826 1160 vcaballero
827 8765 jjdelcerro
                        // /int fieldLen = ((DBFDriver)sds.getDriver()).getFieldLength(i);
828
                        int fieldLen = fieldLength[i]; // TODO aqu? el tama?o no es
829
                        // correcto hay que calcularlo,
830
                        // ahora mismo est? puesto a pi??n.
831
                        int decimales = 5;
832 1160 vcaballero
833 8765 jjdelcerro
                        // if (fieldLen <= 0) {
834
                        // fieldLen = 255;
835
                        // }
836
                        // TODO [AZABALA] HE INTENTADO CREAR UN TIPO Types.BIGINT y
837
                        // ha petado (por eso lo a?ado)
838
                        if ((type == Types.DOUBLE) || (type == Types.FLOAT)
839
                                        || (type == Types.INTEGER) || (type == Types.BIGINT))
840 1160 vcaballero
841 8765 jjdelcerro
                                header.addColumn(colName, 'N', Math.min(fieldLen, 18),
842
                                                decimales);
843
                        if (type == Types.DATE)
844
                                header.addColumn(colName, 'D', fieldLen, 0);
845
                        if ((type == Types.BIT) || (type == Types.BOOLEAN))
846
                                header.addColumn(colName, 'L', 1, 0);
847
                        if ((type == Types.VARCHAR) || (type == Types.CHAR)
848
                                        || (type == Types.LONGVARCHAR))
849
                                header.addColumn(colName, 'C', Math.min(254, fieldLen), 0);
850
                }
851
852 1160 vcaballero
                return header;
853 1132 vcaballero
        }
854 1160 vcaballero
855 1005 vcaballero
        /**
856
         * Class for holding the information assicated with a record.
857
         */
858
        class DbaseField {
859
                // Field Name
860
                String fieldName;
861 377 fjp
862 1005 vcaballero
                // Field Type (C N L D or M)
863
                char fieldType;
864 682 fjp
865 1005 vcaballero
                // Field Data Address offset from the start of the record.
866
                int fieldDataAddress;
867 377 fjp
868 1005 vcaballero
                // Length of the data in bytes
869
                int fieldLength;
870 377 fjp
871 1005 vcaballero
                // Field decimal count in Binary, indicating where the decimal is
872
                int decimalCount;
873
        }
874 3983 fjp
875 8765 jjdelcerro
        public static DbaseFileHeaderNIO createDbaseHeader(
876
                        FieldDescription[] fieldsDesc) {
877 3983 fjp
                DbaseFileHeaderNIO header = new DbaseFileHeaderNIO();
878
879
                for (int i = 0, ii = fieldsDesc.length; i < ii; i++) {
880
881
                        int type = fieldsDesc[i].getFieldType();
882
                        String colName = fieldsDesc[i].getFieldName();
883
884 8765 jjdelcerro
                        int fieldLen = fieldsDesc[i].getFieldLength(); // TODO aqu? el
885
                        // tama?o no es
886
                        // correcto hay que
887
                        // calcularlo, ahora
888
                        // mismo est? puesto
889
                        // a pi??n.
890 3983 fjp
                        int decimales = fieldsDesc[i].getFieldDecimalCount();
891
892 8765 jjdelcerro
                        switch (type) {
893
                        case Types.DOUBLE:
894
                        case Types.FLOAT:
895
                        case Types.INTEGER:
896
                        case Types.BIGINT:
897
                        case Types.SMALLINT:
898
                                header.addColumn(colName, 'N', Math.min(fieldLen, 18),
899 3983 fjp
                                                decimales);
900 8765 jjdelcerro
                                break;
901
                        case Types.DATE:
902
                                header.addColumn(colName, 'D', fieldLen, 0);
903
                                break;
904
                        case Types.BIT:
905
                        case Types.BOOLEAN:
906
                                header.addColumn(colName, 'L', 1, 0);
907
                                break;
908
                        case Types.VARCHAR:
909
                        case Types.CHAR:
910
                        case Types.LONGVARCHAR:
911
                                header.addColumn(colName, 'C', Math.min(254, fieldLen), 0);
912
                                break;
913
                        default:
914
                                throw new RuntimeException("Field type " + type + " not supported in DBF writer");
915
916 3983 fjp
                        }
917 8765 jjdelcerro
                } // for
918 3983 fjp
919 8765 jjdelcerro
                return header;
920 3983 fjp
921
        }
922 6127 fjp
923
        public void setFieldName(int j, String newName) {
924
                fields[j].fieldName = newName;
925
        }
926 377 fjp
}