Statistics
| Revision:

root / trunk / libraries / libFMap / src / com / iver / cit / gvsig / fmap / drivers / dbf / DbaseFile.java @ 28367

History | View | Annotate | Download (11.5 KB)

1
/*
2
 * Created on 16-feb-2004
3
 *
4
 * To change the template for this generated file go to
5
 * Window>Preferences>Java>Code Generation>Code and Comments
6
 */
7
package com.iver.cit.gvsig.fmap.drivers.dbf;
8

    
9
/**
10
 */
11
import java.io.File;
12
import java.io.FileNotFoundException;
13
import java.io.IOException;
14
import java.io.RandomAccessFile;
15
import java.io.UnsupportedEncodingException;
16
import java.nio.ByteBuffer;
17
import java.nio.channels.FileChannel;
18
import java.nio.charset.Charset;
19
import java.text.FieldPosition;
20
import java.text.NumberFormat;
21
import java.util.Calendar;
22
import java.util.Date;
23
import java.util.Locale;
24

    
25
import com.hardcode.gdbms.driver.exceptions.FileNotFoundDriverException;
26
import com.iver.utiles.bigfile.BigByteBuffer2;
27

    
28
/**
29
 * Class to read and write data to a dbase III format file. Creation date:
30
 * (5/15/2001 5:15:13 PM)
31
 */
32
public class DbaseFile {
33
        // Header information for the DBase File
34
        private DbaseFileHeader myHeader;
35

    
36
        private RandomAccessFile raf;
37

    
38
        private FileChannel channel;
39

    
40
        private BigByteBuffer2 buffer;
41

    
42
        private FileChannel.MapMode mode;
43

    
44
        private FieldFormatter formatter = new FieldFormatter();
45

    
46
        private int posActual = -1;
47

    
48
        private int recordOffset;
49

    
50
        private ByteBuffer cachedRecord = null;
51

    
52
        private byte[] bytesCachedRecord = null;
53

    
54
        private final Number NULL_NUMBER = new Integer(0);
55

    
56
        private final String NULL_STRING = "";
57

    
58
        private final String NULL_DATE = "        ";
59

    
60
        private Charset chars;
61

    
62
        /** Utility for formatting Dbase fields. */
63
        public static class FieldFormatter {
64
                private StringBuffer buffer = new StringBuffer(255);
65

    
66
                private NumberFormat numFormat = NumberFormat
67
                                .getNumberInstance(Locale.US);
68

    
69
                private Calendar calendar = Calendar.getInstance(Locale.US);
70

    
71
                private String emtpyString;
72

    
73
                private static final int MAXCHARS = 255;
74

    
75
                public FieldFormatter() {
76
                        // Avoid grouping on number format
77
                        numFormat.setGroupingUsed(false);
78

    
79
                        // build a 255 white spaces string
80
                        StringBuffer sb = new StringBuffer(MAXCHARS);
81
                        sb.setLength(MAXCHARS);
82
                        for (int i = 0; i < MAXCHARS; i++) {
83
                                sb.setCharAt(i, ' ');
84
                        }
85

    
86
                        emtpyString = sb.toString();
87
                }
88

    
89
                public String getFieldString(int size, String s) {
90
                        buffer.replace(0, size, emtpyString);
91
                        buffer.setLength(size);
92

    
93
                        if (s != null) {
94
                                buffer.replace(0, size, s);
95
                                if (s.length() <= size) {
96
                                        for (int i = s.length(); i < size; i++) {
97
                                                buffer.append(' ');
98
                                        }
99
                                }
100
                        }
101

    
102
                        buffer.setLength(size);
103
                        return buffer.toString();
104
                }
105

    
106
                public String getFieldString(Date d) {
107

    
108
                        if (d != null) {
109
                                buffer.delete(0, buffer.length());
110

    
111
                                calendar.setTime(d);
112
                                int year = calendar.get(Calendar.YEAR);
113
                                int month = calendar.get(Calendar.MONTH) + 1; // returns 0
114
                                                                                                                                // based month?
115
                                int day = calendar.get(Calendar.DAY_OF_MONTH);
116

    
117
                                if (year < 1000) {
118
                                        if (year >= 100) {
119
                                                buffer.append("0");
120
                                        } else if (year >= 10) {
121
                                                buffer.append("00");
122
                                        } else {
123
                                                buffer.append("000");
124
                                        }
125
                                }
126
                                buffer.append(year);
127

    
128
                                if (month < 10) {
129
                                        buffer.append("0");
130
                                }
131
                                buffer.append(month);
132

    
133
                                if (day < 10) {
134
                                        buffer.append("0");
135
                                }
136
                                buffer.append(day);
137
                        } else {
138
                                buffer.setLength(8);
139
                                buffer.replace(0, 8, emtpyString);
140
                        }
141

    
142
                        buffer.setLength(8);
143
                        return buffer.toString();
144
                }
145

    
146
                public String getFieldString(int size, int decimalPlaces, Number n) {
147
                        buffer.delete(0, buffer.length());
148

    
149
                        if (n != null) {
150
                                numFormat.setMaximumFractionDigits(decimalPlaces);
151
                                numFormat.setMinimumFractionDigits(decimalPlaces);
152
                                numFormat.format(n, buffer, new FieldPosition(
153
                                                NumberFormat.INTEGER_FIELD));
154
                        }
155

    
156
                        int diff = size - buffer.length();
157
                        if (diff >= 0) {
158
                                while (diff-- > 0) {
159
                                        buffer.insert(0, ' ');
160
                                }
161
                        } else {
162
                                buffer.setLength(size);
163
                        }
164
                        return buffer.toString();
165
                }
166
        }
167

    
168
        // Retrieve number of records in the DbaseFile
169
        public int getRecordCount() {
170
                return myHeader.getNumRecords();
171
        }
172

    
173
        /**
174
         * DOCUMENT ME!
175
         *
176
         * @return DOCUMENT ME!
177
         */
178
        public int getFieldCount() {
179
                return myHeader.getNumFields();
180
        }
181

    
182
        /**
183
         * DOCUMENT ME!
184
         *
185
         * @param rowIndex
186
         *            DOCUMENT ME!
187
         * @param fieldId
188
         *            DOCUMENT ME!
189
         *
190
         * @return DOCUMENT ME!
191
         */
192
        public boolean getBooleanFieldValue(int rowIndex, int fieldId) {
193
                int recordOffset = (myHeader.getRecordLength() * rowIndex)
194
                                + myHeader.getHeaderLength() + 1;
195

    
196
                // Se calcula el offset del campo
197
                int fieldOffset = 0;
198

    
199
                for (int i = 0; i < (fieldId - 1); i++) {
200
                        fieldOffset += myHeader.getFieldLength(i);
201
                }
202

    
203
                buffer.position(recordOffset + fieldOffset);
204

    
205
                char bool = (char) buffer.get();
206

    
207
                return ((bool == 't') || (bool == 'T') || (bool == 'Y') || (bool == 'y'));
208
        }
209

    
210
        /**
211
         * DOCUMENT ME!
212
         *
213
         * @param rowIndex
214
         *            DOCUMENT ME!
215
         * @param fieldId
216
         *            DOCUMENT ME!
217
         *
218
         * @return DOCUMENT ME!
219
         * @throws UnsupportedEncodingException
220
         */
221
        public String getStringFieldValue(int rowIndex, int fieldId)
222
                        throws UnsupportedEncodingException {
223
                try{
224
                int fieldOffset = myHeader.getFieldDescription(fieldId).myFieldDataAddress;
225
                byte[] data = new byte[myHeader.getFieldLength(fieldId)];
226
                if (rowIndex != posActual) {
227
                        recordOffset = (myHeader.getRecordLength() * rowIndex)
228
                                        + myHeader.getHeaderLength() + 1;
229

    
230
                        /*
231
                         * System.err.println("getStringFieldValue: rowIndex = " +
232
                         * rowIndex); System.err.println("recordOffset = " + recordOffset + "
233
                         * fieldOffset=" + fieldOffset);
234
                         */
235
                        buffer.position(recordOffset);
236
                        buffer.get(bytesCachedRecord);
237
                        cachedRecord = ByteBuffer.wrap(bytesCachedRecord);
238
                        posActual = rowIndex;
239

    
240
                }
241
                cachedRecord.position(fieldOffset);
242
                cachedRecord.get(data);
243

    
244
                return new String(data, chars.name());
245
                }catch (Exception e) {
246
                        return "";
247
                }
248

    
249
        }
250

    
251
        public void setFieldValue(int rowIndex, int fieldId, Object obj)
252
                        throws IOException {
253
                int fieldOffset = myHeader.getFieldDescription(fieldId).myFieldDataAddress;
254
                String str = fieldString(obj, fieldId);
255
                byte[] data = new byte[myHeader.getFieldLength(fieldId)];
256
                recordOffset = (myHeader.getRecordLength() * rowIndex)
257
                                + myHeader.getHeaderLength() + 1;
258

    
259
                ByteBuffer aux = ByteBuffer.wrap(data);
260
                aux.put(str.getBytes(chars.name()));
261
//                raf.seek(recordOffset + fieldOffset);
262
//                raf.writeBytes(str);
263
                aux.flip();
264
                int numBytesWritten = channel.write(aux, recordOffset + fieldOffset);
265
                //channel.force(true);
266

    
267

    
268
        }
269

    
270

    
271
        /**
272
         * Retrieve the name of the given column.
273
         *
274
         * @param inIndex
275
         *            DOCUMENT ME!
276
         *
277
         * @return DOCUMENT ME!
278
         */
279
        public String getFieldName(int inIndex) {
280
                return myHeader.getFieldName(inIndex).trim();
281
        }
282

    
283
        /**
284
         * Retrieve the type of the given column.
285
         *
286
         * @param inIndex
287
         *            DOCUMENT ME!
288
         *
289
         * @return DOCUMENT ME!
290
         */
291
        public char getFieldType(int inIndex) {
292
                return myHeader.getFieldType(inIndex);
293
        }
294

    
295
        /**
296
         * Retrieve the length of the given column.
297
         *
298
         * @param inIndex
299
         *            DOCUMENT ME!
300
         *
301
         * @return DOCUMENT ME!
302
         */
303
        public int getFieldLength(int inIndex) {
304
                return myHeader.getFieldLength(inIndex);
305
        }
306

    
307
        /*
308
         * Retrieve the value of the given column as string.
309
         *
310
         * @param idField DOCUMENT ME! @param idRecord DOCUMENT ME!
311
         *
312
         * @return DOCUMENT ME!
313
         *
314
         * public Object getFieldValue(int idField, long idRecord) throws
315
         * IOException { Object[] tmpReg = getRecord(idRecord); return
316
         * tmpReg[idField]; }
317
         */
318
        /*
319
         * DOCUMENT ME!
320
         *
321
         * @param idField DOCUMENT ME! @param idRecord DOCUMENT ME!
322
         *
323
         * @return DOCUMENT ME!
324
         *
325
         * public double getFieldValueAsDouble(int idField, int idRecord) throws
326
         * IOException { Object[] tmpReg = getRecord(idRecord); return (double)
327
         * Double.parseDouble(tmpReg[idField].toString()); }
328
         */
329

    
330
        /**
331
         * Retrieve the location of the decimal point.
332
         *
333
         * @param inIndex
334
         *            DOCUMENT ME!
335
         *
336
         * @return DOCUMENT ME!
337
         */
338
        public int getFieldDecimalLength(int inIndex) {
339
                return myHeader.getFieldDecimalCount(inIndex);
340
        }
341

    
342
        /**
343
         * read the DBF file into memory.
344
         *
345
         * @param file
346
         *            DOCUMENT ME!
347
         *
348
         * @throws IOException
349
         *             DOCUMENT ME!
350
         */
351
        public void open(File file) throws FileNotFoundDriverException {
352
                /*
353
                 * 01h DOS USA code page 437 02h DOS Multilingual code page 850 03h
354
                 * Windows ANSI code page 1252 04h Standard Macintosh 64h EE MS-DOS code
355
                 * page 852 65h Nordic MS-DOS code page 865 66h Russian MS-DOS code page
356
                 * 866 67h Icelandic MS-DOS 68h Kamenicky (Czech) MS-DOS 69h Mazovia
357
                 * (Polish) MS-DOS 6Ah Greek MS-DOS (437G) 6Bh Turkish MS-DOS 96h
358
                 * Russian Macintosh 97h Eastern European Macintosh 98h Greek Macintosh
359
                 * C8h Windows EE code page 1250 C9h Russian Windows CAh Turkish Windows
360
                 * CBh Greek Windows
361
                 */
362
                try {
363
                if (file.canWrite()) {
364
                        try {
365
                                raf = new RandomAccessFile(file, "rw");
366
                                mode = FileChannel.MapMode.READ_WRITE;
367
                        } catch (FileNotFoundException e) {
368
                                raf = new RandomAccessFile(file, "r");
369
                                mode = FileChannel.MapMode.READ_ONLY;
370
                        }
371
                } else {
372
                        raf = new RandomAccessFile(file, "r");
373
                        mode = FileChannel.MapMode.READ_ONLY;
374
                }
375
                channel = raf.getChannel();
376

    
377
                // buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0,
378
                // channel.size());
379
                buffer = new BigByteBuffer2(channel, mode);
380

    
381
                // create the header to contain the header information.
382
                myHeader = new DbaseFileHeader();
383
                myHeader.readHeader(buffer);
384
                switch (myHeader.getLanguageID()) {
385
                case 0x01:
386
                        chars = Charset.forName("US-ASCII");
387
                        break;
388
                case 0x02:
389
                        chars = Charset.forName("ISO-8859-1");
390
                        break;
391
                case 0x03:
392
                        chars = Charset.forName("windows-1252");
393
                        break;
394
                case 0x04:
395
                        chars = Charset.forName("mac");
396
                        break;
397
                case 0x64:
398
                        chars = Charset.forName("ISO-8859-1");
399
                        break;
400
                case 0x65:
401
                        chars = Charset.forName("ISO-8859-1");
402
                        break;
403
                case 0x66:
404
                        chars = Charset.forName("ISO-8859-1");
405
                        break;
406
                case 0x67:
407
                        chars = Charset.forName("ISO-8859-1");
408
                        break;
409
                case 0x68:
410
                        chars = Charset.forName("greek");
411
                        break;
412
                case 0x69:
413
                        chars = Charset.forName("ISO-8859-1");
414
                        break;
415
                case 0x6A:
416
                        chars = Charset.forName("greek");
417
                        break;
418
                case 0x6B:
419
                        chars = Charset.forName("ISO-8859-1");
420
                        break;
421

    
422
                default:
423
                        chars = Charset.forName("ISO-8859-1");
424
                }
425
                bytesCachedRecord = new byte[myHeader.getRecordLength()];
426
                }catch (IOException e) {
427
                        throw new FileNotFoundDriverException("DBF",e,file.getAbsolutePath());
428
                }
429
        }
430

    
431
        /**
432
         * Removes all data from the dataset
433
         *
434
         * @throws IOException
435
         *             DOCUMENT ME!
436
         */
437
        public void close() throws IOException {
438
                raf.close();
439
                channel.close();
440
                buffer = null;
441
                posActual=-1;
442
        }
443

    
444
        public FileChannel getWriteChannel() {
445
                return channel;
446
        }
447

    
448
        private String fieldString(Object obj, final int col) {
449
                String o;
450
                final int fieldLen = myHeader.getFieldLength(col);
451
                switch (myHeader.getFieldType(col)) {
452
                case 'C':
453
                case 'c':
454
                        o = formatter.getFieldString(fieldLen, (obj == null) ? NULL_STRING
455
                                        : ((String) obj));
456
                        break;
457
                case 'L':
458
                case 'l':
459
                        o = (obj == null) ? "F"
460
                                        : ((Boolean) obj).booleanValue() == true ? "T" : "F";
461
                        break;
462
                case 'M':
463
                case 'G':
464
                        o = formatter.getFieldString(fieldLen, (obj == null) ? NULL_STRING
465
                                        : ((String) obj));
466
                        break;
467
                case 'N':
468
                case 'n':
469
                case 'F':
470
                case 'f':
471
                        Number number = null;
472
                        if (obj == null) {
473
                                number = NULL_NUMBER;
474
                        } else {
475
                                Number gVal = (Number) obj;
476
                                number = new Double(gVal.doubleValue());
477
                        }
478
                        o = formatter.getFieldString(fieldLen, myHeader
479
                                        .getFieldDecimalCount(col), number);
480
                        break;
481
                case 'D':
482
                case 'd':
483
                        if (obj == null)
484
                                o = NULL_DATE;
485
                        else
486
                                o = formatter.getFieldString(((Date) obj));
487
                        break;
488
                default:
489
                        throw new RuntimeException("Unknown type "
490
                                        + myHeader.getFieldType(col));
491
                }
492

    
493
                return o;
494
        }
495

    
496
}