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 | 40559 | jjdelcerro | /**
|
---|---|---|---|
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 | 40435 | jjdelcerro | /*
|
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 | } |