All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.h2gis.drivers.dbf.internal.DbaseFileReader Maven / Gradle / Ivy

The newest version!
/**
 * H2GIS is a library that brings spatial support to the H2 Database Engine
 * .
 *
 * H2GIS is distributed under GPL 3 license. It is produced by CNRS
 * .
 *
 * H2GIS is free software: you can redistribute it and/or modify it under the
 * terms of the GNU General Public License as published by the Free Software
 * Foundation, either version 3 of the License, or (at your option) any later
 * version.
 *
 * H2GIS is distributed in the hope that it will be useful, but WITHOUT ANY
 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
 * A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * H2GIS. If not, see .
 *
 * For more information, please consult: 
 * or contact directly: info_at_h2gis.org
 */
/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2002-2006, Geotools Project Managment Committee (PMC)
 *    (C) 2002, Centre for Computational Geography
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation; either
 *    version 2.1 of the License, or (at your option) any later version.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    This file is based on an origional contained in the GISToolkit project:
 *    http://gistoolkit.sourceforge.net/
 */
package org.h2gis.drivers.dbf.internal;

import org.h2gis.drivers.utility.ReadBufferManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.CharBuffer;
import java.nio.channels.FileChannel;
import java.nio.charset.Charset;
import java.nio.charset.CharsetDecoder;
import java.util.Calendar;

/**
 * A DbaseFileReader is used to read a dbase III format file. 
* The general use of this class is:
 * 
 * FileChannel in = new FileInputStream("thefile.dbf").getChannel();
 * DbaseFileReader r = new DbaseFileReader( in ) Object[] fields = new
 * Object[r.getHeader().getNumFields()]; while (r.hasNext()) {
 * r.readEntry(fields); // do stuff } r.close();
 * 
 * 
For consumers who wish to be a bit more selective with their * reading of rows, the Row object has been added. The semantics are the same as * using the readEntry method, but remember that the Row object is always the * same. The values are parsed as they are read, so it pays to copy them out (as * each call to Row.read() will result in an expensive String parse).
* EACH CALL TO readEntry OR readRow ADVANCES THE FILE!
* An example of using the Row method of reading:
 * 
 * FileChannel in = new FileInputStream("thefile.dbf").getChannel();
 * DbaseFileReader r = new DbaseFileReader( in ) int fields =
 * r.getHeader().getNumFields(); while (r.hasNext()) { DbaseFileReader.Row row =
 * r.readRow(); for (int i = 0; i < fields; i++) { // do stuff Foo.bar(
 * row.read(i) ); } } r.close();
 * 
 * 
* * @author Ian Schneider * @see "http://svn.geotools.org/geotools/tags/2.3.1/plugin/shapefile/src/org/geotools/data/shapefile/dbf/DbaseFileReader.java" */ public class DbaseFileReader { private DbaseFileHeader header; private ReadBufferManager buffer; private FileChannel channel; private CharBuffer charBuffer; private CharsetDecoder decoder; private char[] fieldTypes; private int[] fieldLengths; private static final Logger LOG = LoggerFactory.getLogger(DbaseFileReader.class); /** * Creates a new instance of DBaseFileReader * * @param channel * The readable channel to use. * @throws java.io.IOException * If an error occurs while initializing. */ public DbaseFileReader(FileChannel channel,String forceEncoding) throws IOException { this.channel = channel; header = new DbaseFileHeader(); header.readHeader(channel, forceEncoding); init(); } private void init() throws IOException { buffer = new ReadBufferManager(channel); // The entire file is in little endian buffer.order(ByteOrder.LITTLE_ENDIAN); // Set up some buffers and lookups for efficiency fieldTypes = new char[header.getNumFields()]; fieldLengths = new int[header.getNumFields()]; for (int i = 0, ii = header.getNumFields(); i < ii; i++) { fieldTypes[i] = header.getFieldType(i); fieldLengths[i] = header.getFieldLength(i); } charBuffer = CharBuffer.allocate(header.getRecordLength() - 1); Charset chars = Charset.forName(header.getFileEncoding()); decoder = chars.newDecoder(); } /** * Get the header from this file. The header is read upon instantiation. * * @return The header associated with this file or null if an error * occurred. */ public DbaseFileHeader getHeader() { return header; } /** * Clean up all resources associated with this reader.Highly * recomended. * * @throws java.io.IOException * If an error occurs. */ public void close() throws IOException { if (channel != null && channel.isOpen()) { channel.close(); } buffer = null; channel = null; charBuffer = null; decoder = null; header = null; } /** * @param pos Pos index in the buffer * @param length Array length to extract * @return byte array extracted from the buffer * @throws IOException */ private byte[] getBytes(int pos, int length) throws IOException { byte[] bytes = new byte[length]; buffer.get(pos, bytes); return bytes; } public Object getFieldValue(int row, int column) throws IOException { int fieldPosition = getPositionFor(row, column); int fieldLength = getLengthFor(column); byte[] fieldBytes = getBytes(fieldPosition, fieldLength); ByteBuffer field = ByteBuffer.wrap(fieldBytes); charBuffer.clear(); decoder.decode(field, charBuffer, true); charBuffer.flip(); return readObject(0, column); } private int getLengthFor(int column) { return header.getFieldLength(column); } private int getPositionFor(int row, int column) { int recordOffset = header.getHeaderLength() + row * header.getRecordLength() + 1; int fieldOffset = 0; for (int i = 0; i < column; i++) { fieldOffset += header.getFieldLength(i); } return fieldOffset + recordOffset; } private Object readObject(final int fieldOffset, final int fieldNum) throws IOException { final char type = fieldTypes[fieldNum]; final int fieldLen = fieldLengths[fieldNum]; Object object = null; if (fieldLen > 0) { switch (type) { // (L)logical (T,t,F,f,Y,y,N,n) case 'l': case 'L': switch (charBuffer.charAt(fieldOffset)) { case 't': case 'T': case 'Y': case 'y': object = true; break; case 'f': case 'F': case 'N': case 'n': object = false; break; default: throw new IOException("Unknown logical value : '" + charBuffer.charAt(fieldOffset) + "'"); } break; // (C)character (String) case 'c': case 'C': // oh, this seems like a lot of work to parse strings...but, // For some reason if zero characters ( (int) char == 0 ) are // allowed // in these strings, they do not compare correctly later on down // the // line.... int start = fieldOffset; int end = Math.min(fieldOffset + fieldLen - 1, charBuffer.length() - 1); // trim off whitespace and 'zero' chars while (start < end) { char c = charBuffer.get(start); if (c == 0 || Character.isWhitespace(c)) { start++; } else { break; } } while (end > start) { try { char c = charBuffer.get(end); if (c == 0 || Character.isWhitespace(c)) { end--; } else { break; } } catch (IndexOutOfBoundsException ex) { throw new IndexOutOfBoundsException(); } } // set up the new indexes for start and end // this prevents one array copy (the one made by String) object = new String(charBuffer.array(), start, end + 1 - start); break; // (D)date (Date) case 'd': case 'D': if (charBuffer.toString().equals("00000000")) { object = null; } else { try { String tempString = charBuffer.subSequence(fieldOffset, fieldOffset + 4).toString(); if(!tempString.trim().isEmpty()) { int tempYear = Integer.parseInt(tempString); tempString = charBuffer.subSequence(fieldOffset + 4, fieldOffset + 6).toString(); int tempMonth = Integer.parseInt(tempString) - 1; tempString = charBuffer.subSequence(fieldOffset + 6, fieldOffset + 8).toString(); int tempDay = Integer.parseInt(tempString); Calendar cal = Calendar.getInstance(); cal.clear(); cal.set(Calendar.YEAR, tempYear); cal.set(Calendar.MONTH, tempMonth); cal.set(Calendar.DAY_OF_MONTH, tempDay); object = cal.getTime(); } else { object = null; } } catch (NumberFormatException nfe) { // todo: use progresslistener, this isn't a grave error. LOG.warn("There was an error parsing a date. Ignoring it.", nfe); } } break; case 'n': case 'N': try { if (header.getFieldDecimalCount(fieldNum) == 0) { String numberString = extractNumberString(charBuffer, fieldOffset, fieldLen); object = Integer.parseInt(numberString); // parsing successful --> exit break; } // else will fall through to the floating point number } catch (NumberFormatException e) { // todo: use progresslistener, this isn't a grave error. // don't do this!!! the Double parse will be attemted as we // fall // through, so no need to create a new Object. -IanS // object = new Integer(0); // Lets try parsing a long instead... try { String numberString = extractNumberString(charBuffer, fieldOffset, fieldLen); object = Long.parseLong(numberString); // parsing successful --> exit break; } catch (NumberFormatException e2) { // it is not a long either // so we do nothing. // this whole method screams for refactoring... } } // no break!! // this case falls through the following one if there is decimal count // I know, this is ugly... case 'f': case 'F': // floating point number String numberString = extractNumberString(charBuffer, fieldOffset, fieldLen); try { if(!numberString.trim().isEmpty()) { object = Double.parseDouble(numberString); } else { object = null; } } catch (NumberFormatException e) { // May be the decimal operator is exotic if(numberString.contains(",")) { object = Double.parseDouble(numberString.replace(",",".")); } } break; default: throw new IOException("Invalid field type : " + type); } } return object; } /** * @param charBuffer2 * @param fieldOffset * @param fieldLen */ private String extractNumberString(final CharBuffer charBuffer2, final int fieldOffset, final int fieldLen) { return charBuffer2.subSequence(fieldOffset, fieldOffset + fieldLen).toString().trim(); } public int getRecordCount() { return header.getNumRecords(); } /** * @return The number of columns */ public int getFieldCount() { return header.getNumFields(); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy