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

History | View | Annotate | Download (13.8 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
/*
25
 * Created on 16-feb-2004
26
 *
27
 * To change the template for this generated file go to
28
 * Window>Preferences>Java>Code Generation>Code and Comments
29
 */
30
package org.gvsig.fmap.dal.store.dbf.utils;
31

    
32
/**
33
 */
34
import java.io.File;
35
import java.io.IOException;
36
import java.io.RandomAccessFile;
37
import java.nio.ByteBuffer;
38
import java.nio.channels.FileChannel;
39
import java.nio.charset.Charset;
40
import java.text.FieldPosition;
41
import java.text.NumberFormat;
42
import java.util.Calendar;
43
import java.util.Date;
44
import java.util.Locale;
45

    
46
import org.gvsig.fmap.dal.exception.CloseException;
47
import org.gvsig.fmap.dal.exception.FileNotFoundException;
48
import org.gvsig.fmap.dal.exception.UnsupportedEncodingException;
49
import org.gvsig.fmap.dal.exception.UnsupportedVersionException;
50
import org.gvsig.fmap.dal.exception.WriteException;
51
import org.gvsig.utils.bigfile.BigByteBuffer2;
52

    
53

    
54
/**
55
 * Class to read and write data to a dbase III format file. Creation date:
56
 * (5/15/2001 5:15:13 PM)
57
 */
58
public class DbaseFile {
59
    
60
    public static final int MAX_FIELD_NAME_LENGTH = 11;
61
    
62
        // Header information for the DBase File
63
        private DbaseFileHeader myHeader;
64

    
65
        private File file;
66

    
67
        private RandomAccessFile raf;
68

    
69
        private FileChannel channel;
70

    
71
        private BigByteBuffer2 buffer;
72

    
73
        private FileChannel.MapMode mode;
74

    
75
        private FieldFormatter formatter = new FieldFormatter();
76

    
77
        private long posActual = -1;
78

    
79
        private int recordOffset;
80

    
81
        private ByteBuffer cachedRecord = null;
82

    
83
        private byte[] bytesCachedRecord = null;
84

    
85
        private final Number NULL_NUMBER = new Integer(0);
86

    
87
        private final String NULL_STRING = "";
88

    
89
        private final String NULL_DATE = "        ";
90

    
91
        private Charset chars = null;
92
        private Charset charsOriginal;
93

    
94
        private boolean isOpen = false;
95

    
96

    
97
        /** Utility for formatting Dbase fields. */
98
        public static class FieldFormatter {
99
                private StringBuffer buffer = new StringBuffer(255);
100

    
101
                private NumberFormat numFormat = NumberFormat
102
                                .getNumberInstance(Locale.US);
103

    
104
                private Calendar calendar = Calendar.getInstance(Locale.US);
105

    
106
                private String emtpyString;
107

    
108
                private static final int MAXCHARS = 255;
109

    
110
                public FieldFormatter() {
111
                        // Avoid grouping on number format
112
                        numFormat.setGroupingUsed(false);
113

    
114
                        // build a 255 white spaces string
115
                        StringBuffer sb = new StringBuffer(MAXCHARS);
116
                        sb.setLength(MAXCHARS);
117
                        for (int i = 0; i < MAXCHARS; i++) {
118
                                sb.setCharAt(i, ' ');
119
                        }
120

    
121
                        emtpyString = sb.toString();
122
                }
123

    
124
                public String getFieldString(int size, String s) {
125
                        buffer.replace(0, size, emtpyString);
126
                        buffer.setLength(size);
127

    
128
                        if (s != null) {
129
                                buffer.replace(0, size, s);
130
                                if (s.length() <= size) {
131
                                        for (int i = s.length(); i < size; i++) {
132
                                                buffer.append(' ');
133
                                        }
134
                                }
135
                        }
136

    
137
                        buffer.setLength(size);
138
                        return buffer.toString();
139
                }
140

    
141
                public String getFieldString(Date d) {
142

    
143
                        if (d != null) {
144
                                buffer.delete(0, buffer.length());
145

    
146
                                calendar.setTime(d);
147
                                int year = calendar.get(Calendar.YEAR);
148
                                int month = calendar.get(Calendar.MONTH) + 1; // returns 0
149
                                                                                                                                // based month?
150
                                int day = calendar.get(Calendar.DAY_OF_MONTH);
151

    
152
                                if (year < 1000) {
153
                                        if (year >= 100) {
154
                                                buffer.append("0");
155
                                        } else if (year >= 10) {
156
                                                buffer.append("00");
157
                                        } else {
158
                                                buffer.append("000");
159
                                        }
160
                                }
161
                                buffer.append(year);
162

    
163
                                if (month < 10) {
164
                                        buffer.append("0");
165
                                }
166
                                buffer.append(month);
167

    
168
                                if (day < 10) {
169
                                        buffer.append("0");
170
                                }
171
                                buffer.append(day);
172
                        } else {
173
                                buffer.setLength(8);
174
                                buffer.replace(0, 8, emtpyString);
175
                        }
176

    
177
                        buffer.setLength(8);
178
                        return buffer.toString();
179
                }
180

    
181
                public String getFieldString(int size, int decimalPlaces, Number n) {
182
                        buffer.delete(0, buffer.length());
183

    
184
                        if (n != null) {
185
                                numFormat.setMaximumFractionDigits(decimalPlaces);
186
                                numFormat.setMinimumFractionDigits(decimalPlaces);
187
                                numFormat.format(n, buffer, new FieldPosition(
188
                                                NumberFormat.INTEGER_FIELD));
189
                        }
190

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

    
203
        public DbaseFile(File afile){
204
                this(afile, null);
205
        }
206

    
207
        public DbaseFile(File afile, Charset chars) {
208
                this.file = afile;
209
                this.chars = chars;
210
        }
211

    
212
        public byte getCodePage() {
213
                return myHeader.getLanguageID();
214
        }
215

    
216
        // Retrieve number of records in the DbaseFile
217
        public int getRecordCount() {
218
                return myHeader.getNumRecords();
219
        }
220

    
221
        /**
222
         * DOCUMENT ME!
223
         *
224
         * @return DOCUMENT ME!
225
         */
226
        public int getFieldCount() {
227
                return myHeader.getNumFields();
228
        }
229

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

    
244
                // Se calcula el offset del campo
245
                int fieldOffset = 0;
246

    
247
                for (int i = 0; i < (fieldId - 1); i++) {
248
                        fieldOffset += myHeader.getFieldLength(i);
249
                }
250

    
251
                buffer.position(recordOffset + fieldOffset);
252

    
253
                char bool = (char) buffer.get();
254

    
255
                return ((bool == 't') || (bool == 'T') || (bool == 'Y') || (bool == 'y'));
256
        }
257

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

    
277
                        /*
278
                         * System.err.println("getStringFieldValue: rowIndex = " +
279
                         * rowIndex); System.err.println("recordOffset = " + recordOffset + "
280
                         * fieldOffset=" + fieldOffset);
281
                         */
282

    
283
                        buffer.position(recordOffset);
284
                        buffer.get(bytesCachedRecord);
285
                        cachedRecord = ByteBuffer.wrap(bytesCachedRecord);
286
                        posActual = rowIndex;
287

    
288
                }
289
                cachedRecord.position(fieldOffset);
290
                cachedRecord.get(data);
291

    
292
                try {
293
                        return new String(data, chars.name());
294
                } catch (java.io.UnsupportedEncodingException e) {
295
                        throw new UnsupportedEncodingException(
296
                                        e);
297
                }
298

    
299
        }
300

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

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

    
323
        }
324

    
325

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

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

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

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

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

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

    
438
                // buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0,
439
                // channel.size());
440
                buffer = new BigByteBuffer2(channel, mode);
441

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

    
457
        /**
458
         * Removes all data from the dataset
459
         * @throws CloseException
460
         *
461
         * @throws IOException
462
         *             DOCUMENT ME!
463
         */
464
        public void close() throws CloseException {
465
                try{
466
                raf.close();
467
                channel.close();
468
                buffer = null;
469
                posActual=-1;
470
                myHeader = null;
471
                }catch (Exception e) {
472
                        throw new CloseException("DBF",e);
473
                }
474
                this.isOpen = false;
475
        }
476

    
477
        public FileChannel getWriteChannel() {
478
                return channel;
479
        }
480

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

    
527
                return o;
528
        }
529

    
530
        public boolean isOpen() {
531
                return this.isOpen;
532
        }
533

    
534
        public int getFieldIndex(String name) {
535
                return myHeader.getFieldIndex(name);
536
        }
537

    
538
        public Charset getCurrenCharset() {
539
                return chars;
540
        }
541

    
542
        public Charset getOriginalCharset() {
543
                return charsOriginal;
544
        }
545

    
546
        public void setCharset(Charset chars) {
547
                this.chars = chars;
548
        }
549

    
550
}