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

History | View | Annotate | Download (12 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.Buffer;
30
import java.nio.ByteBuffer;
31
import java.nio.channels.FileChannel;
32
import java.nio.charset.Charset;
33
import java.util.Date;
34

    
35
import org.gvsig.fmap.dal.exception.CloseException;
36
import org.gvsig.fmap.dal.exception.FileNotFoundException;
37
import org.gvsig.fmap.dal.exception.UnsupportedEncodingException;
38
import org.gvsig.fmap.dal.exception.UnsupportedVersionException;
39
import org.gvsig.fmap.dal.exception.WriteException;
40
import org.gvsig.fmap.dal.feature.exception.AttributeFeatureTypeNotSuportedException;
41
import org.gvsig.utils.bigfile.BigByteBuffer2;
42

    
43
import org.slf4j.Logger;
44
import org.slf4j.LoggerFactory;
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
    private static final Logger logger = LoggerFactory.getLogger(DbaseFile.class);
53

    
54
    public static final int MAX_FIELD_NAME_LENGTH = 10;
55

    
56
    // Header information for the DBase File
57
    private DbaseFileHeader myHeader;
58

    
59
    private File file;
60

    
61
    private RandomAccessFile raf;
62

    
63
    private FileChannel channel;
64

    
65
    private BigByteBuffer2 buffer;
66

    
67
    private FileChannel.MapMode mode;
68

    
69
    private FieldFormatter formatter = new FieldFormatter();
70

    
71
    private long posActual = -1;
72

    
73
    private long recordOffset;
74

    
75
    private ByteBuffer cachedRecord = null;
76

    
77
    private byte[] bytesCachedRecord = null;
78

    
79
    private final Number NULL_NUMBER = 0;
80

    
81
    private final String NULL_STRING = "";
82

    
83
    private final String NULL_DATE = "        ";
84

    
85
    private Charset chars = null;
86
    private Charset charsOriginal;
87

    
88
    private boolean isOpen = false;
89

    
90
    private boolean allowDuplicatedFieldNames;
91

    
92
    public DbaseFile(File afile) {
93
        this(afile, null);
94
    }
95

    
96
    public DbaseFile(File afile, Charset chars) {
97
        this(afile, chars, false);
98
    }
99

    
100
    public DbaseFile(File afile, Charset chars, boolean allowDuplicatedFieldNames) {
101
        this.file = afile;
102
        this.chars = chars;
103
        this.allowDuplicatedFieldNames = allowDuplicatedFieldNames;
104
    }
105

    
106
    public DbaseFileHeader getHeader() {
107
        return myHeader;
108
    }
109
//
110
//    /**
111
//     * @deprecated Use {@link #getCodePageInt()} instead
112
//     */
113
//    @Deprecated
114
//        public byte getCodePage() {
115
//                return (byte) myHeader.getLanguageID();
116
//        }
117

    
118
    public int getCodePageInt() {
119
        return myHeader.getLanguageID();
120
    }
121

    
122
    /**
123
     * Returns the charset used to read/write this dbf. Maybe different from the
124
     * declared in the file if we have forced a different one
125
     *
126
     * @return
127
     */
128
    public String getCharsetName() {
129
        return chars.name();
130
        //return myHeader.getCharsetName();
131
    }
132

    
133
    /**
134
     * Returns the charset declared on the dbf file (or the default one if none
135
     * is declared)
136
     *
137
     * @return
138
     */
139
    public String getOriginalCharsetName() {
140
        return myHeader.getOriginalCharset();
141
    }
142

    
143
    // Retrieve number of records in the DbaseFile
144
    public int getRecordCount() {
145
        return myHeader.getNumRecords();
146
    }
147

    
148
    /**
149
     * DOCUMENT ME!
150
     *
151
     * @return DOCUMENT ME!
152
     */
153
    public int getFieldCount() {
154
        return myHeader.getNumFields();
155
    }
156

    
157
    /**
158
     * DOCUMENT ME!
159
     *
160
     * @param rowIndex DOCUMENT ME!
161
     * @param fieldId DOCUMENT ME!
162
     *
163
     * @return DOCUMENT ME!
164
     */
165
    public boolean getBooleanFieldValue(long rowIndex, int fieldId) {
166
        DbaseFieldDescriptor descriptor = this.myHeader.getFieldDescription(fieldId);
167

    
168
        long recordOffset = (myHeader.getRecordLength() * rowIndex)
169
                + myHeader.getHeaderLength() + 1;
170

    
171
        // Se calcula el offset del campo
172
        int fieldOffset = 0;
173

    
174
        for (int i = 0; i < (fieldId - 1); i++) {
175
            fieldOffset += descriptor.getSize();
176
        }
177

    
178
        buffer.position(recordOffset + fieldOffset);
179

    
180
        char bool = (char) buffer.get();
181

    
182
        return ((bool == 't') || (bool == 'T') || (bool == 'Y') || (bool == 'y'));
183
    }
184

    
185
    /**
186
     * DOCUMENT ME!
187
     *
188
     * @param rowIndex DOCUMENT ME!
189
     * @param fieldId DOCUMENT ME!
190
     *
191
     * @return DOCUMENT ME!
192
     * @throws UnsupportedEncodingException
193
     */
194
    public String getStringFieldValue(long rowIndex, int fieldId)
195
            throws UnsupportedEncodingException {
196
        DbaseFieldDescriptor descriptor = this.myHeader.getFieldDescription(fieldId);
197

    
198
        int fieldOffset = descriptor.getOffsetInRecord();
199
        byte[] data = new byte[descriptor.getSize()];
200
        if (rowIndex != posActual) {
201
            recordOffset = (myHeader.getRecordLength() * rowIndex)
202
                    + myHeader.getHeaderLength() + 1;
203

    
204
            /*
205
                         * System.err.println("getStringFieldValue: rowIndex = " +
206
                         * rowIndex); System.err.println("recordOffset = " + recordOffset + "
207
                         * fieldOffset=" + fieldOffset);
208
             */
209
            buffer.position(recordOffset);
210
            buffer.get(bytesCachedRecord);
211
            cachedRecord = ByteBuffer.wrap(bytesCachedRecord);
212
            posActual = rowIndex;
213

    
214
        }
215
        ((Buffer) cachedRecord).position(fieldOffset);
216
        cachedRecord.get(data);
217

    
218
        return new String(data, chars);
219
    }
220

    
221
    public void setFieldValue(long rowIndex, int fieldId, Object obj) throws UnsupportedEncodingException, WriteException {
222
        try {
223
            DbaseFieldDescriptor descriptor = this.myHeader.getFieldDescription(fieldId);
224

    
225
            int fieldOffset = descriptor.getOffsetInRecord();
226
            String str = fieldString(obj, fieldId);
227
            byte[] data = new byte[descriptor.getSize()];
228
            recordOffset = (myHeader.getRecordLength() * rowIndex)
229
                    + myHeader.getHeaderLength() + 1;
230

    
231
            ByteBuffer aux = ByteBuffer.wrap(data);
232
            aux.put(str.getBytes(chars));
233
//                        raf.seek(recordOffset + fieldOffset);
234
//                        raf.writeBytes(str);
235
            aux.flip();
236
//                        int numBytesWritten = channel.write(aux, recordOffset + fieldOffset);
237
            channel.write(aux, recordOffset + fieldOffset);
238
            //channel.force(true);
239
        } catch (java.io.UnsupportedEncodingException e) {
240
            throw new UnsupportedEncodingException(e);
241
        } catch (IOException e) {
242
            throw new WriteException("DBF", e);
243
        }
244

    
245
    }
246

    
247
    /**
248
     * read the DBF file into memory.
249
     *
250
     * @throws FileNotFoundException
251
     * @throws UnsupportedVersionException
252
     * @throws IOException
253
     * @throws AttributeFeatureTypeNotSuportedException
254
     */
255
    public void open() throws FileNotFoundException,
256
            UnsupportedVersionException, IOException, AttributeFeatureTypeNotSuportedException {
257

    
258
        if (!file.exists()) {
259
            throw new FileNotFoundException(file);
260
        }
261
//                if (file.canWrite()) {
262
//                        try {
263
//                                raf = new RandomAccessFile(file, "rw");
264
//                                mode = FileChannel.MapMode.READ_WRITE;
265
//                        } catch (java.io.FileNotFoundException e) {
266
//                                raf = new RandomAccessFile(file, "r");
267
//                                mode = FileChannel.MapMode.READ_ONLY;
268
//                        }
269
//                } else {
270
        raf = new RandomAccessFile(file, "r");
271
        mode = FileChannel.MapMode.READ_ONLY;
272
//                }
273
        channel = raf.getChannel();
274

    
275
        // buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0,
276
        // channel.size());
277
        buffer = new BigByteBuffer2(channel, mode);
278

    
279
        // create the header to contain the header information.
280
        myHeader = new DbaseFileHeader();
281
        if (chars == null) {
282
            myHeader.read(buffer, null, allowDuplicatedFieldNames);
283
        } else {
284
            myHeader.read(buffer, chars.name(), allowDuplicatedFieldNames);
285
        }
286
        if (myHeader.getLanguageID() == 0x00) {
287
            // read from the .cpg file if ldid is 0x00
288
            DbaseCodepage cpReader = new DbaseCodepage(file);
289
            String charsetName = cpReader.read();
290
            if (charsetName == null) {
291
                charsetName = "ISO-8859-1"; // for compatibility with old gvSIG files
292
            }
293
            charsOriginal = Charset.forName(myHeader.mappingEncoding(charsetName));
294
        } else {
295
            charsOriginal = Charset.forName(myHeader.mappingEncoding(myHeader.getOriginalCharset()));
296
        }
297
        if (chars == null) {
298
            chars = charsOriginal;
299
        }
300
        bytesCachedRecord = new byte[myHeader.getRecordLength()];
301
        this.isOpen = true;
302
    }
303

    
304
    /**
305
     * Removes all data from the dataset
306
     *
307
     * @throws CloseException
308
     *
309
     * @throws IOException DOCUMENT ME!
310
     */
311
    public void close() throws CloseException {
312
        logger.debug("Closing dbf file '" + this.file.getAbsolutePath() + "'");
313

    
314
        try {
315
            raf.close();
316
            channel.close();
317
            buffer = null;
318
            posActual = -1;
319
            myHeader = null;
320
        } catch (Exception e) {
321
            throw new CloseException("DBF", e);
322
        }
323
        this.isOpen = false;
324
    }
325

    
326
    public FileChannel getWriteChannel() {
327
        return channel;
328
    }
329

    
330
    private String fieldString(Object obj, final int col) {
331
        DbaseFieldDescriptor descriptor = this.myHeader.getFieldDescription(col);
332

    
333
        String o;
334
        final int fieldLen = descriptor.getSize();
335
        switch (descriptor.getType()) {
336
            case 'C':
337
            case 'c':
338
                o = formatter.format(
339
                        (obj == null) ? NULL_STRING : ((String) obj),
340
                        fieldLen
341
                );
342
                break;
343
            case 'L':
344
            case 'l':
345
                o = (obj == null) ? "F" : ((Boolean) obj) == true ? "T" : "F";
346
                break;
347
            case 'M':
348
            case 'G':
349
                o = formatter.format(
350
                        (obj == null) ? NULL_STRING : (String) obj,
351
                        fieldLen
352
                );
353
                break;
354
            case 'N':
355
            case 'n':
356
            case 'F':
357
            case 'f':
358
                Number number;
359
                if (obj == null) {
360
                    number = NULL_NUMBER;
361
                } else {
362
                    Number gVal = (Number) obj;
363
                    number = gVal.doubleValue();
364
                }
365
                o = formatter.format(
366
                        (double) number,
367
                        fieldLen,
368
                        descriptor.getScale()
369
                );
370
                break;
371
            case 'D':
372
            case 'd':
373
                if (obj == null) {
374
                    o = NULL_DATE;
375
                } else {
376
                    o = formatter.formatDate(((Date) obj));
377
                }
378
                break;
379
            default:
380
                throw new RuntimeException("Unknown type " + descriptor.getType());
381
        }
382
        return o;
383
    }
384

    
385
    public boolean isOpen() {
386
        return this.isOpen;
387
    }
388

    
389
    public int getFieldIndex(String name) {
390
        return myHeader.getFieldIndex(name);
391
    }
392

    
393
    public Charset getCurrenCharset() {
394
        return chars;
395
    }
396

    
397
    public Charset getOriginalCharset() {
398
        return charsOriginal;
399
    }
400

    
401
    public void setCharset(Charset chars) {
402
        this.chars = chars;
403
    }
404

    
405
    public boolean isWritable() {
406
        return this.file.canWrite();
407
    }
408

    
409
}