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 @ 41860

History | View | Annotate | Download (13.9 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
import org.slf4j.Logger;
45
import org.slf4j.LoggerFactory;
46

    
47

    
48
/**
49
 * Class to read and write data to a dbase III format file. Creation date:
50
 * (5/15/2001 5:15:13 PM)
51
 */
52
public class DbaseFile {
53

    
54
    private static final Logger logger = LoggerFactory.getLogger(DbaseFile.class);
55
            
56
    public static final int MAX_FIELD_NAME_LENGTH = 10;
57
    
58
        // Header information for the DBase File
59
        private DbaseFileHeader myHeader;
60

    
61
        private File file;
62

    
63
        private RandomAccessFile raf;
64

    
65
        private FileChannel channel;
66

    
67
        private BigByteBuffer2 buffer;
68

    
69
        private FileChannel.MapMode mode;
70

    
71
        private FieldFormatter formatter = new FieldFormatter();
72

    
73
        private long posActual = -1;
74

    
75
        private int recordOffset;
76

    
77
        private ByteBuffer cachedRecord = null;
78

    
79
        private byte[] bytesCachedRecord = null;
80

    
81
        private final Number NULL_NUMBER = new Integer(0);
82

    
83
        private final String NULL_STRING = "";
84

    
85
        private final String NULL_DATE = "        ";
86

    
87
        private Charset chars = null;
88
        private Charset charsOriginal;
89

    
90
        private boolean isOpen = false;
91

    
92

    
93
        /** Utility for formatting Dbase fields. */
94
        public static class FieldFormatter {
95
                private StringBuffer buffer = new StringBuffer(255);
96

    
97
                private NumberFormat numFormat = NumberFormat
98
                                .getNumberInstance(Locale.US);
99

    
100
                private Calendar calendar = Calendar.getInstance(Locale.US);
101

    
102
                private String emtpyString;
103

    
104
                private static final int MAXCHARS = 255;
105

    
106
                public FieldFormatter() {
107
                        // Avoid grouping on number format
108
                        numFormat.setGroupingUsed(false);
109

    
110
                        // build a 255 white spaces string
111
                        StringBuffer sb = new StringBuffer(MAXCHARS);
112
                        sb.setLength(MAXCHARS);
113
                        for (int i = 0; i < MAXCHARS; i++) {
114
                                sb.setCharAt(i, ' ');
115
                        }
116

    
117
                        emtpyString = sb.toString();
118
                }
119

    
120
                public String getFieldString(int size, String s) {
121
                        buffer.replace(0, size, emtpyString);
122
                        buffer.setLength(size);
123

    
124
                        if (s != null) {
125
                                buffer.replace(0, size, s);
126
                                if (s.length() <= size) {
127
                                        for (int i = s.length(); i < size; i++) {
128
                                                buffer.append(' ');
129
                                        }
130
                                }
131
                        }
132

    
133
                        buffer.setLength(size);
134
                        return buffer.toString();
135
                }
136

    
137
                public String getFieldString(Date d) {
138

    
139
                        if (d != null) {
140
                                buffer.delete(0, buffer.length());
141

    
142
                                calendar.setTime(d);
143
                                int year = calendar.get(Calendar.YEAR);
144
                                int month = calendar.get(Calendar.MONTH) + 1; // returns 0
145
                                                                                                                                // based month?
146
                                int day = calendar.get(Calendar.DAY_OF_MONTH);
147

    
148
                                if (year < 1000) {
149
                                        if (year >= 100) {
150
                                                buffer.append("0");
151
                                        } else if (year >= 10) {
152
                                                buffer.append("00");
153
                                        } else {
154
                                                buffer.append("000");
155
                                        }
156
                                }
157
                                buffer.append(year);
158

    
159
                                if (month < 10) {
160
                                        buffer.append("0");
161
                                }
162
                                buffer.append(month);
163

    
164
                                if (day < 10) {
165
                                        buffer.append("0");
166
                                }
167
                                buffer.append(day);
168
                        } else {
169
                                buffer.setLength(8);
170
                                buffer.replace(0, 8, emtpyString);
171
                        }
172

    
173
                        buffer.setLength(8);
174
                        return buffer.toString();
175
                }
176

    
177
                public String getFieldString(int size, int decimalPlaces, Number n) {
178
                        buffer.delete(0, buffer.length());
179

    
180
                        if (n != null) {
181
                                numFormat.setMaximumFractionDigits(decimalPlaces);
182
                                numFormat.setMinimumFractionDigits(decimalPlaces);
183
                                numFormat.format(n, buffer, new FieldPosition(
184
                                                NumberFormat.INTEGER_FIELD));
185
                        }
186

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

    
199
        public DbaseFile(File afile){
200
                this(afile, null);
201
        }
202

    
203
        public DbaseFile(File afile, Charset chars) {
204
                this.file = afile;
205
                this.chars = chars;
206
        }
207

    
208
        public byte getCodePage() {
209
                return myHeader.getLanguageID();
210
        }
211

    
212
        // Retrieve number of records in the DbaseFile
213
        public int getRecordCount() {
214
                return myHeader.getNumRecords();
215
        }
216

    
217
        /**
218
         * DOCUMENT ME!
219
         *
220
         * @return DOCUMENT ME!
221
         */
222
        public int getFieldCount() {
223
                return myHeader.getNumFields();
224
        }
225

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

    
240
                // Se calcula el offset del campo
241
                int fieldOffset = 0;
242

    
243
                for (int i = 0; i < (fieldId - 1); i++) {
244
                        fieldOffset += myHeader.getFieldLength(i);
245
                }
246

    
247
                buffer.position(recordOffset + fieldOffset);
248

    
249
                char bool = (char) buffer.get();
250

    
251
                return ((bool == 't') || (bool == 'T') || (bool == 'Y') || (bool == 'y'));
252
        }
253

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

    
273
                        /*
274
                         * System.err.println("getStringFieldValue: rowIndex = " +
275
                         * rowIndex); System.err.println("recordOffset = " + recordOffset + "
276
                         * fieldOffset=" + fieldOffset);
277
                         */
278

    
279
                        buffer.position(recordOffset);
280
                        buffer.get(bytesCachedRecord);
281
                        cachedRecord = ByteBuffer.wrap(bytesCachedRecord);
282
                        posActual = rowIndex;
283

    
284
                }
285
                cachedRecord.position(fieldOffset);
286
                cachedRecord.get(data);
287

    
288
                try {
289
                        return new String(data, chars.name());
290
                } catch (java.io.UnsupportedEncodingException e) {
291
                        throw new UnsupportedEncodingException(
292
                                        e);
293
                }
294

    
295
        }
296

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

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

    
319
        }
320

    
321

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

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

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

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

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

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

    
434
                // buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0,
435
                // channel.size());
436
                buffer = new BigByteBuffer2(channel, mode);
437

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

    
453
        /**
454
         * Removes all data from the dataset
455
         * @throws CloseException
456
         *
457
         * @throws IOException
458
         *             DOCUMENT ME!
459
         */
460
        public void close() throws CloseException {
461
                logger.debug("Closing dbf file '"+this.file.getAbsolutePath()+"'");
462

    
463
                try{
464
                raf.close();
465
                channel.close();
466
                buffer = null;
467
                posActual=-1;
468
                myHeader = null;
469
                }catch (Exception e) {
470
                        throw new CloseException("DBF",e);
471
                }
472
                this.isOpen = false;
473
        }
474

    
475
        public FileChannel getWriteChannel() {
476
                return channel;
477
        }
478

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

    
525
                return o;
526
        }
527

    
528
        public boolean isOpen() {
529
                return this.isOpen;
530
        }
531

    
532
        public int getFieldIndex(String name) {
533
                return myHeader.getFieldIndex(name);
534
        }
535

    
536
        public Charset getCurrenCharset() {
537
                return chars;
538
        }
539

    
540
        public Charset getOriginalCharset() {
541
                return charsOriginal;
542
        }
543

    
544
        public void setCharset(Charset chars) {
545
                this.chars = chars;
546
        }
547

    
548
}