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 @ 40435
History | View | Annotate | Download (12.9 KB)
1 | 40435 | jjdelcerro | /*
|
---|---|---|---|
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 org.gvsig.fmap.dal.store.dbf.utils; |
||
8 | |||
9 | /**
|
||
10 | */
|
||
11 | import java.io.File; |
||
12 | import java.io.IOException; |
||
13 | import java.io.RandomAccessFile; |
||
14 | import java.nio.ByteBuffer; |
||
15 | import java.nio.channels.FileChannel; |
||
16 | import java.nio.charset.Charset; |
||
17 | import java.text.FieldPosition; |
||
18 | import java.text.NumberFormat; |
||
19 | import java.util.Calendar; |
||
20 | import java.util.Date; |
||
21 | import java.util.Locale; |
||
22 | |||
23 | import org.gvsig.fmap.dal.exception.CloseException; |
||
24 | import org.gvsig.fmap.dal.exception.FileNotFoundException; |
||
25 | import org.gvsig.fmap.dal.exception.UnsupportedEncodingException; |
||
26 | import org.gvsig.fmap.dal.exception.UnsupportedVersionException; |
||
27 | import org.gvsig.fmap.dal.exception.WriteException; |
||
28 | import org.gvsig.utils.bigfile.BigByteBuffer2; |
||
29 | |||
30 | |||
31 | /**
|
||
32 | * Class to read and write data to a dbase III format file. Creation date:
|
||
33 | * (5/15/2001 5:15:13 PM)
|
||
34 | */
|
||
35 | public class DbaseFile { |
||
36 | |||
37 | public static final int MAX_FIELD_NAME_LENGTH = 11; |
||
38 | |||
39 | // Header information for the DBase File
|
||
40 | private DbaseFileHeader myHeader;
|
||
41 | |||
42 | private File file; |
||
43 | |||
44 | private RandomAccessFile raf; |
||
45 | |||
46 | private FileChannel channel; |
||
47 | |||
48 | private BigByteBuffer2 buffer;
|
||
49 | |||
50 | private FileChannel.MapMode mode; |
||
51 | |||
52 | private FieldFormatter formatter = new FieldFormatter(); |
||
53 | |||
54 | private long posActual = -1; |
||
55 | |||
56 | private int recordOffset; |
||
57 | |||
58 | private ByteBuffer cachedRecord = null; |
||
59 | |||
60 | private byte[] bytesCachedRecord = null; |
||
61 | |||
62 | private final Number NULL_NUMBER = new Integer(0); |
||
63 | |||
64 | private final String NULL_STRING = ""; |
||
65 | |||
66 | private final String NULL_DATE = " "; |
||
67 | |||
68 | private Charset chars = null; |
||
69 | private Charset charsOriginal; |
||
70 | |||
71 | private boolean isOpen = false; |
||
72 | |||
73 | |||
74 | /** Utility for formatting Dbase fields. */
|
||
75 | public static class FieldFormatter { |
||
76 | private StringBuffer buffer = new StringBuffer(255); |
||
77 | |||
78 | private NumberFormat numFormat = NumberFormat |
||
79 | .getNumberInstance(Locale.US);
|
||
80 | |||
81 | private Calendar calendar = Calendar.getInstance(Locale.US); |
||
82 | |||
83 | private String emtpyString; |
||
84 | |||
85 | private static final int MAXCHARS = 255; |
||
86 | |||
87 | public FieldFormatter() {
|
||
88 | // Avoid grouping on number format
|
||
89 | numFormat.setGroupingUsed(false);
|
||
90 | |||
91 | // build a 255 white spaces string
|
||
92 | StringBuffer sb = new StringBuffer(MAXCHARS); |
||
93 | sb.setLength(MAXCHARS); |
||
94 | for (int i = 0; i < MAXCHARS; i++) { |
||
95 | sb.setCharAt(i, ' ');
|
||
96 | } |
||
97 | |||
98 | emtpyString = sb.toString(); |
||
99 | } |
||
100 | |||
101 | public String getFieldString(int size, String s) { |
||
102 | buffer.replace(0, size, emtpyString);
|
||
103 | buffer.setLength(size); |
||
104 | |||
105 | if (s != null) { |
||
106 | buffer.replace(0, size, s);
|
||
107 | if (s.length() <= size) {
|
||
108 | for (int i = s.length(); i < size; i++) { |
||
109 | buffer.append(' ');
|
||
110 | } |
||
111 | } |
||
112 | } |
||
113 | |||
114 | buffer.setLength(size); |
||
115 | return buffer.toString();
|
||
116 | } |
||
117 | |||
118 | public String getFieldString(Date d) { |
||
119 | |||
120 | if (d != null) { |
||
121 | buffer.delete(0, buffer.length());
|
||
122 | |||
123 | calendar.setTime(d); |
||
124 | int year = calendar.get(Calendar.YEAR); |
||
125 | int month = calendar.get(Calendar.MONTH) + 1; // returns 0 |
||
126 | // based month?
|
||
127 | int day = calendar.get(Calendar.DAY_OF_MONTH); |
||
128 | |||
129 | if (year < 1000) { |
||
130 | if (year >= 100) { |
||
131 | buffer.append("0");
|
||
132 | } else if (year >= 10) { |
||
133 | buffer.append("00");
|
||
134 | } else {
|
||
135 | buffer.append("000");
|
||
136 | } |
||
137 | } |
||
138 | buffer.append(year); |
||
139 | |||
140 | if (month < 10) { |
||
141 | buffer.append("0");
|
||
142 | } |
||
143 | buffer.append(month); |
||
144 | |||
145 | if (day < 10) { |
||
146 | buffer.append("0");
|
||
147 | } |
||
148 | buffer.append(day); |
||
149 | } else {
|
||
150 | buffer.setLength(8);
|
||
151 | buffer.replace(0, 8, emtpyString); |
||
152 | } |
||
153 | |||
154 | buffer.setLength(8);
|
||
155 | return buffer.toString();
|
||
156 | } |
||
157 | |||
158 | public String getFieldString(int size, int decimalPlaces, Number n) { |
||
159 | buffer.delete(0, buffer.length());
|
||
160 | |||
161 | if (n != null) { |
||
162 | numFormat.setMaximumFractionDigits(decimalPlaces); |
||
163 | numFormat.setMinimumFractionDigits(decimalPlaces); |
||
164 | numFormat.format(n, buffer, new FieldPosition( |
||
165 | NumberFormat.INTEGER_FIELD));
|
||
166 | } |
||
167 | |||
168 | int diff = size - buffer.length();
|
||
169 | if (diff >= 0) { |
||
170 | while (diff-- > 0) { |
||
171 | buffer.insert(0, ' '); |
||
172 | } |
||
173 | } else {
|
||
174 | buffer.setLength(size); |
||
175 | } |
||
176 | return buffer.toString();
|
||
177 | } |
||
178 | } |
||
179 | |||
180 | public DbaseFile(File afile){ |
||
181 | this(afile, null); |
||
182 | } |
||
183 | |||
184 | public DbaseFile(File afile, Charset chars) { |
||
185 | this.file = afile;
|
||
186 | this.chars = chars;
|
||
187 | } |
||
188 | |||
189 | public byte getCodePage() { |
||
190 | return myHeader.getLanguageID();
|
||
191 | } |
||
192 | |||
193 | // Retrieve number of records in the DbaseFile
|
||
194 | public int getRecordCount() { |
||
195 | return myHeader.getNumRecords();
|
||
196 | } |
||
197 | |||
198 | /**
|
||
199 | * DOCUMENT ME!
|
||
200 | *
|
||
201 | * @return DOCUMENT ME!
|
||
202 | */
|
||
203 | public int getFieldCount() { |
||
204 | return myHeader.getNumFields();
|
||
205 | } |
||
206 | |||
207 | /**
|
||
208 | * DOCUMENT ME!
|
||
209 | *
|
||
210 | * @param rowIndex
|
||
211 | * DOCUMENT ME!
|
||
212 | * @param fieldId
|
||
213 | * DOCUMENT ME!
|
||
214 | *
|
||
215 | * @return DOCUMENT ME!
|
||
216 | */
|
||
217 | public boolean getBooleanFieldValue(int rowIndex, int fieldId) { |
||
218 | int recordOffset = (myHeader.getRecordLength() * rowIndex)
|
||
219 | + myHeader.getHeaderLength() + 1;
|
||
220 | |||
221 | // Se calcula el offset del campo
|
||
222 | int fieldOffset = 0; |
||
223 | |||
224 | for (int i = 0; i < (fieldId - 1); i++) { |
||
225 | fieldOffset += myHeader.getFieldLength(i); |
||
226 | } |
||
227 | |||
228 | buffer.position(recordOffset + fieldOffset); |
||
229 | |||
230 | char bool = (char) buffer.get(); |
||
231 | |||
232 | return ((bool == 't') || (bool == 'T') || (bool == 'Y') || (bool == 'y')); |
||
233 | } |
||
234 | |||
235 | /**
|
||
236 | * DOCUMENT ME!
|
||
237 | *
|
||
238 | * @param rowIndex
|
||
239 | * DOCUMENT ME!
|
||
240 | * @param fieldId
|
||
241 | * DOCUMENT ME!
|
||
242 | *
|
||
243 | * @return DOCUMENT ME!
|
||
244 | * @throws UnsupportedEncodingException
|
||
245 | */
|
||
246 | public String getStringFieldValue(int rowIndex, int fieldId) |
||
247 | throws UnsupportedEncodingException { |
||
248 | int fieldOffset = myHeader.getFieldDescription(fieldId).myFieldDataAddress;
|
||
249 | byte[] data = new byte[myHeader.getFieldLength(fieldId)]; |
||
250 | if (rowIndex != posActual) {
|
||
251 | recordOffset = (myHeader.getRecordLength() * rowIndex) |
||
252 | + myHeader.getHeaderLength() + 1;
|
||
253 | |||
254 | /*
|
||
255 | * System.err.println("getStringFieldValue: rowIndex = " +
|
||
256 | * rowIndex); System.err.println("recordOffset = " + recordOffset + "
|
||
257 | * fieldOffset=" + fieldOffset);
|
||
258 | */
|
||
259 | |||
260 | buffer.position(recordOffset); |
||
261 | buffer.get(bytesCachedRecord); |
||
262 | cachedRecord = ByteBuffer.wrap(bytesCachedRecord);
|
||
263 | posActual = rowIndex; |
||
264 | |||
265 | } |
||
266 | cachedRecord.position(fieldOffset); |
||
267 | cachedRecord.get(data); |
||
268 | |||
269 | try {
|
||
270 | return new String(data, chars.name()); |
||
271 | } catch (java.io.UnsupportedEncodingException e) {
|
||
272 | throw new UnsupportedEncodingException( |
||
273 | e); |
||
274 | } |
||
275 | |||
276 | } |
||
277 | |||
278 | public void setFieldValue(int rowIndex, int fieldId, Object obj) throws UnsupportedEncodingException, WriteException { |
||
279 | try{
|
||
280 | int fieldOffset = myHeader.getFieldDescription(fieldId).myFieldDataAddress;
|
||
281 | String str = fieldString(obj, fieldId);
|
||
282 | byte[] data = new byte[myHeader.getFieldLength(fieldId)]; |
||
283 | recordOffset = (myHeader.getRecordLength() * rowIndex) |
||
284 | + myHeader.getHeaderLength() + 1;
|
||
285 | |||
286 | ByteBuffer aux = ByteBuffer.wrap(data); |
||
287 | aux.put(str.getBytes(chars.name())); |
||
288 | // raf.seek(recordOffset + fieldOffset);
|
||
289 | // raf.writeBytes(str);
|
||
290 | aux.flip(); |
||
291 | // int numBytesWritten = channel.write(aux, recordOffset + fieldOffset);
|
||
292 | channel.write(aux, recordOffset + fieldOffset); |
||
293 | //channel.force(true);
|
||
294 | }catch (java.io.UnsupportedEncodingException e) {
|
||
295 | throw new UnsupportedEncodingException(e); |
||
296 | }catch (IOException e) { |
||
297 | throw new WriteException("DBF",e); |
||
298 | } |
||
299 | |||
300 | } |
||
301 | |||
302 | |||
303 | /**
|
||
304 | * Retrieve the name of the given column.
|
||
305 | *
|
||
306 | * @param inIndex
|
||
307 | * DOCUMENT ME!
|
||
308 | *
|
||
309 | * @return DOCUMENT ME!
|
||
310 | */
|
||
311 | public String getFieldName(int inIndex) { |
||
312 | return myHeader.getFieldName(inIndex).trim();
|
||
313 | } |
||
314 | |||
315 | /**
|
||
316 | * Retrieve the type of the given column.
|
||
317 | *
|
||
318 | * @param inIndex
|
||
319 | * DOCUMENT ME!
|
||
320 | *
|
||
321 | * @return DOCUMENT ME!
|
||
322 | */
|
||
323 | public char getFieldType(int inIndex) { |
||
324 | return myHeader.getFieldType(inIndex);
|
||
325 | } |
||
326 | |||
327 | /**
|
||
328 | * Retrieve the length of the given column.
|
||
329 | *
|
||
330 | * @param inIndex
|
||
331 | * DOCUMENT ME!
|
||
332 | *
|
||
333 | * @return DOCUMENT ME!
|
||
334 | */
|
||
335 | public int getFieldLength(int inIndex) { |
||
336 | return myHeader.getFieldLength(inIndex);
|
||
337 | } |
||
338 | |||
339 | /*
|
||
340 | * Retrieve the value of the given column as string.
|
||
341 | *
|
||
342 | * @param idField DOCUMENT ME! @param idRecord DOCUMENT ME!
|
||
343 | *
|
||
344 | * @return DOCUMENT ME!
|
||
345 | *
|
||
346 | * public Object getFieldValue(int idField, long idRecord) throws
|
||
347 | * IOException { Object[] tmpReg = getRecord(idRecord); return
|
||
348 | * tmpReg[idField]; }
|
||
349 | */
|
||
350 | /*
|
||
351 | * DOCUMENT ME!
|
||
352 | *
|
||
353 | * @param idField DOCUMENT ME! @param idRecord DOCUMENT ME!
|
||
354 | *
|
||
355 | * @return DOCUMENT ME!
|
||
356 | *
|
||
357 | * public double getFieldValueAsDouble(int idField, int idRecord) throws
|
||
358 | * IOException { Object[] tmpReg = getRecord(idRecord); return (double)
|
||
359 | * Double.parseDouble(tmpReg[idField].toString()); }
|
||
360 | */
|
||
361 | |||
362 | /**
|
||
363 | * Retrieve the location of the decimal point.
|
||
364 | *
|
||
365 | * @param inIndex
|
||
366 | * DOCUMENT ME!
|
||
367 | *
|
||
368 | * @return DOCUMENT ME!
|
||
369 | */
|
||
370 | public int getFieldDecimalLength(int inIndex) { |
||
371 | return myHeader.getFieldDecimalCount(inIndex);
|
||
372 | } |
||
373 | |||
374 | /**
|
||
375 | * read the DBF file into memory.
|
||
376 | *
|
||
377 | * @param file
|
||
378 | * DOCUMENT ME!
|
||
379 | * @throws FileNotFoundException
|
||
380 | * @throws UnsupportedVersionException
|
||
381 | * @throws IOException
|
||
382 | *
|
||
383 | * @throws IOException
|
||
384 | * DOCUMENT ME!
|
||
385 | */
|
||
386 | public void open() throws FileNotFoundException, |
||
387 | UnsupportedVersionException, IOException {
|
||
388 | /*
|
||
389 | * 01h DOS USA code page 437 02h DOS Multilingual code page 850 03h
|
||
390 | * Windows ANSI code page 1252 04h Standard Macintosh 64h EE MS-DOS code
|
||
391 | * page 852 65h Nordic MS-DOS code page 865 66h Russian MS-DOS code page
|
||
392 | * 866 67h Icelandic MS-DOS 68h Kamenicky (Czech) MS-DOS 69h Mazovia
|
||
393 | * (Polish) MS-DOS 6Ah Greek MS-DOS (437G) 6Bh Turkish MS-DOS 96h
|
||
394 | * Russian Macintosh 97h Eastern European Macintosh 98h Greek Macintosh
|
||
395 | * C8h Windows EE code page 1250 C9h Russian Windows CAh Turkish Windows
|
||
396 | * CBh Greek Windows
|
||
397 | */
|
||
398 | if (!file.exists()) {
|
||
399 | throw new FileNotFoundException(file); |
||
400 | } |
||
401 | // if (file.canWrite()) {
|
||
402 | // try {
|
||
403 | // raf = new RandomAccessFile(file, "rw");
|
||
404 | // mode = FileChannel.MapMode.READ_WRITE;
|
||
405 | // } catch (java.io.FileNotFoundException e) {
|
||
406 | // raf = new RandomAccessFile(file, "r");
|
||
407 | // mode = FileChannel.MapMode.READ_ONLY;
|
||
408 | // }
|
||
409 | // } else {
|
||
410 | raf = new RandomAccessFile(file, "r"); |
||
411 | mode = FileChannel.MapMode.READ_ONLY;
|
||
412 | // }
|
||
413 | channel = raf.getChannel(); |
||
414 | |||
415 | // buffer = channel.map(FileChannel.MapMode.READ_ONLY, 0,
|
||
416 | // channel.size());
|
||
417 | buffer = new BigByteBuffer2(channel, mode);
|
||
418 | |||
419 | // create the header to contain the header information.
|
||
420 | myHeader = new DbaseFileHeader();
|
||
421 | if (chars == null) { |
||
422 | myHeader.readHeader(buffer, null);
|
||
423 | } else {
|
||
424 | myHeader.readHeader(buffer, chars.name()); |
||
425 | } |
||
426 | charsOriginal = Charset.forName(myHeader.mappingEncoding(myHeader.getCharsetName()));
|
||
427 | if (chars == null) { |
||
428 | chars = charsOriginal; |
||
429 | } |
||
430 | bytesCachedRecord = new byte[myHeader.getRecordLength()]; |
||
431 | this.isOpen = true; |
||
432 | } |
||
433 | |||
434 | /**
|
||
435 | * Removes all data from the dataset
|
||
436 | * @throws CloseException
|
||
437 | *
|
||
438 | * @throws IOException
|
||
439 | * DOCUMENT ME!
|
||
440 | */
|
||
441 | public void close() throws CloseException { |
||
442 | try{
|
||
443 | raf.close(); |
||
444 | channel.close(); |
||
445 | buffer = null;
|
||
446 | posActual=-1;
|
||
447 | myHeader = null;
|
||
448 | }catch (Exception e) { |
||
449 | throw new CloseException("DBF",e); |
||
450 | } |
||
451 | this.isOpen = false; |
||
452 | } |
||
453 | |||
454 | public FileChannel getWriteChannel() { |
||
455 | return channel;
|
||
456 | } |
||
457 | |||
458 | private String fieldString(Object obj, final int col) { |
||
459 | String o;
|
||
460 | final int fieldLen = myHeader.getFieldLength(col); |
||
461 | switch (myHeader.getFieldType(col)) {
|
||
462 | case 'C': |
||
463 | case 'c': |
||
464 | o = formatter.getFieldString(fieldLen, (obj == null) ? NULL_STRING
|
||
465 | : ((String) obj));
|
||
466 | break;
|
||
467 | case 'L': |
||
468 | case 'l': |
||
469 | o = (obj == null) ? "F" |
||
470 | : ((Boolean) obj).booleanValue() == true ? "T" : "F"; |
||
471 | break;
|
||
472 | case 'M': |
||
473 | case 'G': |
||
474 | o = formatter.getFieldString(fieldLen, (obj == null) ? NULL_STRING
|
||
475 | : ((String) obj));
|
||
476 | break;
|
||
477 | case 'N': |
||
478 | case 'n': |
||
479 | case 'F': |
||
480 | case 'f': |
||
481 | Number number = null; |
||
482 | if (obj == null) { |
||
483 | number = NULL_NUMBER; |
||
484 | } else {
|
||
485 | Number gVal = (Number) obj; |
||
486 | number = new Double(gVal.doubleValue()); |
||
487 | } |
||
488 | o = formatter.getFieldString(fieldLen, myHeader |
||
489 | .getFieldDecimalCount(col), number); |
||
490 | break;
|
||
491 | case 'D': |
||
492 | case 'd': |
||
493 | if (obj == null) { |
||
494 | o = NULL_DATE; |
||
495 | } else {
|
||
496 | o = formatter.getFieldString(((Date) obj));
|
||
497 | } |
||
498 | break;
|
||
499 | default:
|
||
500 | throw new RuntimeException("Unknown type " |
||
501 | + myHeader.getFieldType(col)); |
||
502 | } |
||
503 | |||
504 | return o;
|
||
505 | } |
||
506 | |||
507 | public boolean isOpen() { |
||
508 | return this.isOpen; |
||
509 | } |
||
510 | |||
511 | public int getFieldIndex(String name) { |
||
512 | return myHeader.getFieldIndex(name);
|
||
513 | } |
||
514 | |||
515 | public Charset getCurrenCharset() { |
||
516 | return chars;
|
||
517 | } |
||
518 | |||
519 | public Charset getOriginalCharset() { |
||
520 | return charsOriginal;
|
||
521 | } |
||
522 | |||
523 | public void setCharset(Charset chars) { |
||
524 | this.chars = chars;
|
||
525 | } |
||
526 | |||
527 | } |