Statistics
| Revision:

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

History | View | Annotate | Download (13.6 KB)

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

    
26
import java.io.File;
27
import java.io.IOException;
28
import java.io.RandomAccessFile;
29
import java.nio.ByteBuffer;
30
import java.nio.channels.FileChannel;
31
import java.nio.charset.Charset;
32
import java.text.FieldPosition;
33
import java.text.NumberFormat;
34
import java.util.Calendar;
35
import java.util.Date;
36
import java.util.Locale;
37

    
38
import org.gvsig.fmap.dal.exception.CloseException;
39
import org.gvsig.fmap.dal.exception.FileNotFoundException;
40
import org.gvsig.fmap.dal.exception.UnsupportedEncodingException;
41
import org.gvsig.fmap.dal.exception.UnsupportedVersionException;
42
import org.gvsig.fmap.dal.exception.WriteException;
43
import org.gvsig.utils.bigfile.BigByteBuffer2;
44

    
45

    
46
/**
47
 * Class to read and write data to a dbase III format file. Creation date:
48
 * (5/15/2001 5:15:13 PM)
49
 */
50
public class DbaseFile {
51
    
52
    public static final int MAX_FIELD_NAME_LENGTH = 11;
53
    
54
        // Header information for the DBase File
55
        private DbaseFileHeader myHeader;
56

    
57
        private File file;
58

    
59
        private RandomAccessFile raf;
60

    
61
        private FileChannel channel;
62

    
63
        private BigByteBuffer2 buffer;
64

    
65
        private FileChannel.MapMode mode;
66

    
67
        private FieldFormatter formatter = new FieldFormatter();
68

    
69
        private long posActual = -1;
70

    
71
        private int recordOffset;
72

    
73
        private ByteBuffer cachedRecord = null;
74

    
75
        private byte[] bytesCachedRecord = null;
76

    
77
        private final Number NULL_NUMBER = new Integer(0);
78

    
79
        private final String NULL_STRING = "";
80

    
81
        private final String NULL_DATE = "        ";
82

    
83
        private Charset chars = null;
84
        private Charset charsOriginal;
85

    
86
        private boolean isOpen = false;
87

    
88

    
89
        /** Utility for formatting Dbase fields. */
90
        public static class FieldFormatter {
91
                private StringBuffer buffer = new StringBuffer(255);
92

    
93
                private NumberFormat numFormat = NumberFormat
94
                                .getNumberInstance(Locale.US);
95

    
96
                private Calendar calendar = Calendar.getInstance(Locale.US);
97

    
98
                private String emtpyString;
99

    
100
                private static final int MAXCHARS = 255;
101

    
102
                public FieldFormatter() {
103
                        // Avoid grouping on number format
104
                        numFormat.setGroupingUsed(false);
105

    
106
                        // build a 255 white spaces string
107
                        StringBuffer sb = new StringBuffer(MAXCHARS);
108
                        sb.setLength(MAXCHARS);
109
                        for (int i = 0; i < MAXCHARS; i++) {
110
                                sb.setCharAt(i, ' ');
111
                        }
112

    
113
                        emtpyString = sb.toString();
114
                }
115

    
116
                public String getFieldString(int size, String s) {
117
                        buffer.replace(0, size, emtpyString);
118
                        buffer.setLength(size);
119

    
120
                        if (s != null) {
121
                                buffer.replace(0, size, s);
122
                                if (s.length() <= size) {
123
                                        for (int i = s.length(); i < size; i++) {
124
                                                buffer.append(' ');
125
                                        }
126
                                }
127
                        }
128

    
129
                        buffer.setLength(size);
130
                        return buffer.toString();
131
                }
132

    
133
                public String getFieldString(Date d) {
134

    
135
                        if (d != null) {
136
                                buffer.delete(0, buffer.length());
137

    
138
                                calendar.setTime(d);
139
                                int year = calendar.get(Calendar.YEAR);
140
                                int month = calendar.get(Calendar.MONTH) + 1; // returns 0
141
                                                                                                                                // based month?
142
                                int day = calendar.get(Calendar.DAY_OF_MONTH);
143

    
144
                                if (year < 1000) {
145
                                        if (year >= 100) {
146
                                                buffer.append("0");
147
                                        } else if (year >= 10) {
148
                                                buffer.append("00");
149
                                        } else {
150
                                                buffer.append("000");
151
                                        }
152
                                }
153
                                buffer.append(year);
154

    
155
                                if (month < 10) {
156
                                        buffer.append("0");
157
                                }
158
                                buffer.append(month);
159

    
160
                                if (day < 10) {
161
                                        buffer.append("0");
162
                                }
163
                                buffer.append(day);
164
                        } else {
165
                                buffer.setLength(8);
166
                                buffer.replace(0, 8, emtpyString);
167
                        }
168

    
169
                        buffer.setLength(8);
170
                        return buffer.toString();
171
                }
172

    
173
                public String getFieldString(int size, int decimalPlaces, Number n) {
174
                        buffer.delete(0, buffer.length());
175

    
176
                        if (n != null) {
177
                                numFormat.setMaximumFractionDigits(decimalPlaces);
178
                                numFormat.setMinimumFractionDigits(decimalPlaces);
179
                                numFormat.format(n, buffer, new FieldPosition(
180
                                                NumberFormat.INTEGER_FIELD));
181
                        }
182

    
183
                        int diff = size - buffer.length();
184
                        if (diff >= 0) {
185
                                while (diff-- > 0) {
186
                                        buffer.insert(0, ' ');
187
                                }
188
                        } else {
189
                                buffer.setLength(size);
190
                        }
191
                        return buffer.toString();
192
                }
193
        }
194

    
195
        public DbaseFile(File afile){
196
                this(afile, null);
197
        }
198

    
199
        public DbaseFile(File afile, Charset chars) {
200
                this.file = afile;
201
                this.chars = chars;
202
        }
203

    
204
        public byte getCodePage() {
205
                return myHeader.getLanguageID();
206
        }
207

    
208
        // Retrieve number of records in the DbaseFile
209
        public int getRecordCount() {
210
                return myHeader.getNumRecords();
211
        }
212

    
213
        /**
214
         * DOCUMENT ME!
215
         *
216
         * @return DOCUMENT ME!
217
         */
218
        public int getFieldCount() {
219
                return myHeader.getNumFields();
220
        }
221

    
222
        /**
223
         * DOCUMENT ME!
224
         *
225
         * @param rowIndex
226
         *            DOCUMENT ME!
227
         * @param fieldId
228
         *            DOCUMENT ME!
229
         *
230
         * @return DOCUMENT ME!
231
         */
232
        public boolean getBooleanFieldValue(int rowIndex, int fieldId) {
233
                int recordOffset = (myHeader.getRecordLength() * rowIndex)
234
                                + myHeader.getHeaderLength() + 1;
235

    
236
                // Se calcula el offset del campo
237
                int fieldOffset = 0;
238

    
239
                for (int i = 0; i < (fieldId - 1); i++) {
240
                        fieldOffset += myHeader.getFieldLength(i);
241
                }
242

    
243
                buffer.position(recordOffset + fieldOffset);
244

    
245
                char bool = (char) buffer.get();
246

    
247
                return ((bool == 't') || (bool == 'T') || (bool == 'Y') || (bool == 'y'));
248
        }
249

    
250
        /**
251
         * DOCUMENT ME!
252
         *
253
         * @param rowIndex
254
         *            DOCUMENT ME!
255
         * @param fieldId
256
         *            DOCUMENT ME!
257
         *
258
         * @return DOCUMENT ME!
259
         * @throws UnsupportedEncodingException
260
         */
261
        public String getStringFieldValue(int rowIndex, int fieldId)
262
                        throws UnsupportedEncodingException {
263
                int fieldOffset = myHeader.getFieldDescription(fieldId).myFieldDataAddress;
264
                byte[] data = new byte[myHeader.getFieldLength(fieldId)];
265
                if (rowIndex != posActual) {
266
                        recordOffset = (myHeader.getRecordLength() * rowIndex)
267
                                        + myHeader.getHeaderLength() + 1;
268

    
269
                        /*
270
                         * System.err.println("getStringFieldValue: rowIndex = " +
271
                         * rowIndex); System.err.println("recordOffset = " + recordOffset + "
272
                         * fieldOffset=" + fieldOffset);
273
                         */
274

    
275
                        buffer.position(recordOffset);
276
                        buffer.get(bytesCachedRecord);
277
                        cachedRecord = ByteBuffer.wrap(bytesCachedRecord);
278
                        posActual = rowIndex;
279

    
280
                }
281
                cachedRecord.position(fieldOffset);
282
                cachedRecord.get(data);
283

    
284
                try {
285
                        return new String(data, chars.name());
286
                } catch (java.io.UnsupportedEncodingException e) {
287
                        throw new UnsupportedEncodingException(
288
                                        e);
289
                }
290

    
291
        }
292

    
293
        public void setFieldValue(int rowIndex, int fieldId, Object obj) throws UnsupportedEncodingException, WriteException {
294
                try{
295
                        int fieldOffset = myHeader.getFieldDescription(fieldId).myFieldDataAddress;
296
                        String str = fieldString(obj, fieldId);
297
                        byte[] data = new byte[myHeader.getFieldLength(fieldId)];
298
                        recordOffset = (myHeader.getRecordLength() * rowIndex)
299
                                        + myHeader.getHeaderLength() + 1;
300

    
301
                        ByteBuffer aux = ByteBuffer.wrap(data);
302
                        aux.put(str.getBytes(chars.name()));
303
//                        raf.seek(recordOffset + fieldOffset);
304
//                        raf.writeBytes(str);
305
                        aux.flip();
306
//                        int numBytesWritten = channel.write(aux, recordOffset + fieldOffset);
307
                        channel.write(aux, recordOffset + fieldOffset);
308
                        //channel.force(true);
309
                }catch (java.io.UnsupportedEncodingException e) {
310
                        throw new UnsupportedEncodingException(e);
311
                }catch (IOException e) {
312
                        throw new WriteException("DBF",e);
313
                }
314

    
315
        }
316

    
317

    
318
        /**
319
         * Retrieve the name of the given column.
320
         *
321
         * @param inIndex
322
         *            DOCUMENT ME!
323
         *
324
         * @return DOCUMENT ME!
325
         */
326
        public String getFieldName(int inIndex) {
327
                return myHeader.getFieldName(inIndex).trim();
328
        }
329

    
330
        /**
331
         * Retrieve the type of the given column.
332
         *
333
         * @param inIndex
334
         *            DOCUMENT ME!
335
         *
336
         * @return DOCUMENT ME!
337
         */
338
        public char getFieldType(int inIndex) {
339
                return myHeader.getFieldType(inIndex);
340
        }
341

    
342
        /**
343
         * Retrieve the length of the given column.
344
         *
345
         * @param inIndex
346
         *            DOCUMENT ME!
347
         *
348
         * @return DOCUMENT ME!
349
         */
350
        public int getFieldLength(int inIndex) {
351
                return myHeader.getFieldLength(inIndex);
352
        }
353

    
354
        /*
355
         * Retrieve the value of the given column as string.
356
         *
357
         * @param idField DOCUMENT ME! @param idRecord DOCUMENT ME!
358
         *
359
         * @return DOCUMENT ME!
360
         *
361
         * public Object getFieldValue(int idField, long idRecord) throws
362
         * IOException { Object[] tmpReg = getRecord(idRecord); return
363
         * tmpReg[idField]; }
364
         */
365
        /*
366
         * DOCUMENT ME!
367
         *
368
         * @param idField DOCUMENT ME! @param idRecord DOCUMENT ME!
369
         *
370
         * @return DOCUMENT ME!
371
         *
372
         * public double getFieldValueAsDouble(int idField, int idRecord) throws
373
         * IOException { Object[] tmpReg = getRecord(idRecord); return (double)
374
         * Double.parseDouble(tmpReg[idField].toString()); }
375
         */
376

    
377
        /**
378
         * Retrieve the location of the decimal point.
379
         *
380
         * @param inIndex
381
         *            DOCUMENT ME!
382
         *
383
         * @return DOCUMENT ME!
384
         */
385
        public int getFieldDecimalLength(int inIndex) {
386
                return myHeader.getFieldDecimalCount(inIndex);
387
        }
388

    
389
        /**
390
         * read the DBF file into memory.
391
         *
392
         * @param file
393
         *            DOCUMENT ME!
394
         * @throws FileNotFoundException
395
         * @throws UnsupportedVersionException
396
         * @throws IOException
397
         *
398
         * @throws IOException
399
         *             DOCUMENT ME!
400
         */
401
        public void open() throws FileNotFoundException,
402
                        UnsupportedVersionException, IOException {
403
                /*
404
                 * 01h DOS USA code page 437 02h DOS Multilingual code page 850 03h
405
                 * Windows ANSI code page 1252 04h Standard Macintosh 64h EE MS-DOS code
406
                 * page 852 65h Nordic MS-DOS code page 865 66h Russian MS-DOS code page
407
                 * 866 67h Icelandic MS-DOS 68h Kamenicky (Czech) MS-DOS 69h Mazovia
408
                 * (Polish) MS-DOS 6Ah Greek MS-DOS (437G) 6Bh Turkish MS-DOS 96h
409
                 * Russian Macintosh 97h Eastern European Macintosh 98h Greek Macintosh
410
                 * C8h Windows EE code page 1250 C9h Russian Windows CAh Turkish Windows
411
                 * CBh Greek Windows
412
                 */
413
                if (!file.exists()) {
414
                        throw new FileNotFoundException(file);
415
                }
416
//                if (file.canWrite()) {
417
//                        try {
418
//                                raf = new RandomAccessFile(file, "rw");
419
//                                mode = FileChannel.MapMode.READ_WRITE;
420
//                        } catch (java.io.FileNotFoundException e) {
421
//                                raf = new RandomAccessFile(file, "r");
422
//                                mode = FileChannel.MapMode.READ_ONLY;
423
//                        }
424
//                } else {
425
                        raf = new RandomAccessFile(file, "r");
426
                        mode = FileChannel.MapMode.READ_ONLY;
427
//                }
428
                channel = raf.getChannel();
429

    
430
                // buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0,
431
                // channel.size());
432
                buffer = new BigByteBuffer2(channel, mode);
433

    
434
                // create the header to contain the header information.
435
                myHeader = new DbaseFileHeader();
436
                if (chars == null) {
437
                        myHeader.readHeader(buffer, null);
438
                } else {
439
                        myHeader.readHeader(buffer, chars.name());
440
                }
441
                charsOriginal = Charset.forName(myHeader.mappingEncoding(myHeader.getCharsetName()));
442
                if (chars == null) {
443
                        chars = charsOriginal;
444
                }
445
                bytesCachedRecord = new byte[myHeader.getRecordLength()];
446
                this.isOpen = true;
447
        }
448

    
449
        /**
450
         * Removes all data from the dataset
451
         * @throws CloseException
452
         *
453
         * @throws IOException
454
         *             DOCUMENT ME!
455
         */
456
        public void close() throws CloseException {
457
                try{
458
                raf.close();
459
                channel.close();
460
                buffer = null;
461
                posActual=-1;
462
                myHeader = null;
463
                }catch (Exception e) {
464
                        throw new CloseException("DBF",e);
465
                }
466
                this.isOpen = false;
467
        }
468

    
469
        public FileChannel getWriteChannel() {
470
                return channel;
471
        }
472

    
473
        private String fieldString(Object obj, final int col) {
474
                String o;
475
                final int fieldLen = myHeader.getFieldLength(col);
476
                switch (myHeader.getFieldType(col)) {
477
                case 'C':
478
                case 'c':
479
                        o = formatter.getFieldString(fieldLen, (obj == null) ? NULL_STRING
480
                                        : ((String) obj));
481
                        break;
482
                case 'L':
483
                case 'l':
484
                        o = (obj == null) ? "F"
485
                                        : ((Boolean) obj).booleanValue() == true ? "T" : "F";
486
                        break;
487
                case 'M':
488
                case 'G':
489
                        o = formatter.getFieldString(fieldLen, (obj == null) ? NULL_STRING
490
                                        : ((String) obj));
491
                        break;
492
                case 'N':
493
                case 'n':
494
                case 'F':
495
                case 'f':
496
                        Number number = null;
497
                        if (obj == null) {
498
                                number = NULL_NUMBER;
499
                        } else {
500
                                Number gVal = (Number) obj;
501
                                number = new Double(gVal.doubleValue());
502
                        }
503
                        o = formatter.getFieldString(fieldLen, myHeader
504
                                        .getFieldDecimalCount(col), number);
505
                        break;
506
                case 'D':
507
                case 'd':
508
                        if (obj == null) {
509
                                o = NULL_DATE;
510
                        } else {
511
                                o = formatter.getFieldString(((Date) obj));
512
                        }
513
                        break;
514
                default:
515
                        throw new RuntimeException("Unknown type "
516
                                        + myHeader.getFieldType(col));
517
                }
518

    
519
                return o;
520
        }
521

    
522
        public boolean isOpen() {
523
                return this.isOpen;
524
        }
525

    
526
        public int getFieldIndex(String name) {
527
                return myHeader.getFieldIndex(name);
528
        }
529

    
530
        public Charset getCurrenCharset() {
531
                return chars;
532
        }
533

    
534
        public Charset getOriginalCharset() {
535
                return charsOriginal;
536
        }
537

    
538
        public void setCharset(Charset chars) {
539
                this.chars = chars;
540
        }
541

    
542
}