diff --git a/src/net/iryndin/jdbf/reader/DbfReader.java b/src/net/iryndin/jdbf/reader/DbfReader.java index 895eac6..8ff5985 100644 --- a/src/net/iryndin/jdbf/reader/DbfReader.java +++ b/src/net/iryndin/jdbf/reader/DbfReader.java @@ -4,65 +4,90 @@ import net.iryndin.jdbf.core.DbfRecord; import net.iryndin.jdbf.util.DbfMetadataUtils; -import java.io.EOFException; -import java.io.File; -import java.io.IOException; -import java.io.RandomAccessFile; +import java.io.*; public class DbfReader { - - private RandomAccessFile raf; - private DbfMetadata metadata; - private byte[] oneRecordBuffer; - - public DbfReader(File file) throws IOException { - this.raf = new RandomAccessFile(file, "r"); - readMetadata(); - } - - public DbfMetadata getMetadata() { - return metadata; - } - - private void readMetadata() throws IOException { - metadata = new DbfMetadata(); - readHeader(); - DbfMetadataUtils.readFields(metadata, raf); - - oneRecordBuffer = new byte[metadata.getOneRecordLength()]; + + private static final int DEFAULT_BUFFER_SIZE = 1024 * 4; + + private ByteArrayInputStream inputStream; + private DbfMetadata metadata; + private byte[] oneRecordBuffer; + + public DbfReader(File file) throws IOException { + this(new FileInputStream(file)); + } + + public DbfReader(InputStream inputStream) throws IOException { + this.inputStream = new ByteArrayInputStream(toByteArray(inputStream)); + + readMetadata(); + } + + public DbfMetadata getMetadata() { + return metadata; + } + + private void readMetadata() throws IOException { + metadata = new DbfMetadata(); + readHeader(); + DbfMetadataUtils.readFields(metadata, inputStream); + + oneRecordBuffer = new byte[metadata.getOneRecordLength()]; findFirstRecord(); - } - - private void readHeader() throws IOException { - // 1. Allocate buffer - byte[] bytes = new byte[16]; - // 2. Read 16 bytes - raf.readFully(bytes); - // 3. Fill header fields - DbfMetadataUtils.fillHeaderFields(metadata,bytes); - // 4. Read next 16 bytes (for most DBF types these are reserved bytes) - raf.readFully(bytes); - } - - public void close() throws IOException { - raf.close(); - } + } + + private void readHeader() throws IOException { + // 1. Allocate buffer + byte[] bytes = new byte[16]; + // 2. Read 16 bytes + inputStream.read(bytes); + // 3. Fill header fields + DbfMetadataUtils.fillHeaderFields(metadata, bytes); + // 4. Read next 16 bytes (for most DBF types these are reserved bytes) + inputStream.read(bytes); + } + + public void close() throws IOException { + inputStream.close(); + } public void findFirstRecord() throws IOException { - raf.seek(metadata.getFullHeaderLength()); + seek(inputStream, metadata.getFullHeaderLength()); + } + + private void seek(ByteArrayInputStream inputStream, int position) { + inputStream.reset(); + inputStream.skip(position); + } + + public DbfRecord read() throws IOException { + int readLength = inputStream.read(oneRecordBuffer); + + if (readLength == -1) { + return null; + } + + return createDbfRecord(); + } + + private DbfRecord createDbfRecord() { + return new DbfRecord(oneRecordBuffer, metadata); + } + + private static byte[] toByteArray(InputStream input) throws IOException { + byte[] result = new byte[input.available()]; + + byte[] buffer = new byte[DEFAULT_BUFFER_SIZE]; + int offset = 0; + int bufferLength; + while ((bufferLength = input.read(buffer)) > 0) { + System.arraycopy(buffer, 0, result, offset, bufferLength); + + offset += bufferLength; + } + + return result; } - - public DbfRecord read() throws IOException { - try { - raf.readFully(oneRecordBuffer); - return createDbfRecord(); - } catch (EOFException eofe) { - return null; - } - } - - private DbfRecord createDbfRecord() { - return new DbfRecord(oneRecordBuffer, metadata); - } } diff --git a/src/net/iryndin/jdbf/util/DbfMetadataUtils.java b/src/net/iryndin/jdbf/util/DbfMetadataUtils.java index 82cf9a0..4bb75da 100644 --- a/src/net/iryndin/jdbf/util/DbfMetadataUtils.java +++ b/src/net/iryndin/jdbf/util/DbfMetadataUtils.java @@ -1,180 +1,181 @@ package net.iryndin.jdbf.util; -import java.io.IOException; -import java.io.RandomAccessFile; -import java.util.Date; -import java.util.ArrayList; -import java.util.List; - import net.iryndin.jdbf.core.DbfField; import net.iryndin.jdbf.core.DbfFieldTypeEnum; import net.iryndin.jdbf.core.DbfFileTypeEnum; import net.iryndin.jdbf.core.DbfMetadata; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + public class DbfMetadataUtils { - - public static DbfMetadata fromFieldsString(String s) { - List fields = JdbfUtils.createFieldsFromString(s); - - DbfMetadata metadata = new DbfMetadata(); - - metadata.setType(DbfFileTypeEnum.FoxBASEPlus1); - metadata.setUpdateDate(new Date()); - //metadata.setRecordsQty(recordsQty); - int fullHeaderLength = calculateFullHeaderLength(fields); - metadata.setFullHeaderLength(fullHeaderLength); - int oneRecordLength = calculateOneRecordLength(fields); - metadata.setOneRecordLength(oneRecordLength); - - metadata.setFields(fields); - - return metadata; - } - - private static int calculateOneRecordLength(List fields) { - int result = 0; - for (DbfField field : fields) { - result += field.getLength(); - } - result++; - return result; - } - - private static int calculateFullHeaderLength(List fields) { - int result = 32; - result += 32*fields.size(); - result++; - return result; - } - + + public static DbfMetadata fromFieldsString(String s) { + List fields = JdbfUtils.createFieldsFromString(s); + + DbfMetadata metadata = new DbfMetadata(); + + metadata.setType(DbfFileTypeEnum.FoxBASEPlus1); + metadata.setUpdateDate(new Date()); + //metadata.setRecordsQty(recordsQty); + int fullHeaderLength = calculateFullHeaderLength(fields); + metadata.setFullHeaderLength(fullHeaderLength); + int oneRecordLength = calculateOneRecordLength(fields); + metadata.setOneRecordLength(oneRecordLength); + + metadata.setFields(fields); + + return metadata; + } + + private static int calculateOneRecordLength(List fields) { + int result = 0; + for (DbfField field : fields) { + result += field.getLength(); + } + result++; + return result; + } + + private static int calculateFullHeaderLength(List fields) { + int result = 32; + result += 32 * fields.size(); + result++; + return result; + } + // public static byte[] toByteArray(DbfMetadata metadata) { // // } - - public static void fillHeaderFields(DbfMetadata metadata, byte[] headerBytes) { - metadata.setType(DbfFileTypeEnum.fromInt(headerBytes[0])); - metadata.setUpdateDate(JdbfUtils.createDate(headerBytes[1], headerBytes[2], headerBytes[3])); - metadata.setRecordsQty(BitUtils.makeInt(headerBytes[4], headerBytes[5], headerBytes[6], headerBytes[7])); - metadata.setFullHeaderLength(BitUtils.makeInt(headerBytes[8], headerBytes[9])); - metadata.setOneRecordLength(BitUtils.makeInt(headerBytes[10], headerBytes[11])); - metadata.setUncompletedTxFlag(headerBytes[14]); - metadata.setEcnryptionFlag(headerBytes[15]); - } - - public static void readFields(DbfMetadata metadata, RandomAccessFile raf) throws IOException { - List fields = new ArrayList(); - byte[] fieldBytes = new byte[JdbfUtils.FIELD_RECORD_LENGTH]; - int headerLength = 0; - int fieldLength = 0; - while(true) { - raf.readFully(fieldBytes); - DbfField field = createDbfField(fieldBytes); - fields.add(field); - - fieldLength += field.getLength(); - headerLength += fieldBytes.length; - - long currentPosition = raf.getFilePointer(); - int terminator = raf.read(); - if (terminator == JdbfUtils.HEADER_TERMINATOR) { - break; - } else { - raf.seek(currentPosition); - } - } - fieldLength += 1; - headerLength += 32; - headerLength += 1; - - if (headerLength != metadata.getFullHeaderLength()) { - // can throw Exception here - } - if (fieldLength != metadata.getOneRecordLength()) { - // can throw Exception here - } - - metadata.setFields(fields); - } - - public static DbfField createDbfField(byte[] fieldBytes) { - DbfField field = new DbfField(); - // 1. Set name - { - int i = 0; - for (i=0; i<11 && fieldBytes[i]>0; i++); - field.setName(new String(fieldBytes, 0, i)); - } - // 2. Set type - field.setType(DbfFieldTypeEnum.fromChar((char)fieldBytes[11])); - // 3. Set length - { - int length = fieldBytes[16]; - if (length < 0) { - length = 256+length; - } - field.setLength(length); - } - // 4. Set number of decimal places - field.setNumberOfDecimalPlaces(fieldBytes[17]); - - return field; - } - - public static void writeDbfField(DbfField field, byte[] fieldBytes) { - BitUtils.memset(fieldBytes, 0); - byte[] nameBytes = field.getName().getBytes(); - int nameLength = nameBytes.length; - if (nameLength > 11) { - // throw error here! - } - System.arraycopy(nameBytes, 0, fieldBytes, 0, nameBytes.length); - fieldBytes[11] = field.getType().toByte(); - int length = field.getLength(); - fieldBytes[16] = (byte)(length & 0xff); - fieldBytes[17] = (byte)(field.getNumberOfDecimalPlaces() & 0xff); - } - - @SuppressWarnings("deprecation") - public static byte[] toByteArrayHeader(DbfMetadata metadata) { - byte[] headerBytes = new byte[16]; - BitUtils.memset(headerBytes, 0); - - - headerBytes[0] = metadata.getType().toByte(); - - Date updateDate = metadata.getUpdateDate(); - // date - if (updateDate == null) { - updateDate = new Date(); - } - // write date bytes - { - byte[] dateBytes = JdbfUtils.writeDateForHeader(updateDate); - headerBytes[1] = dateBytes[0]; - headerBytes[2] = dateBytes[1]; - headerBytes[3] = dateBytes[2]; - } - - byte[] b = BitUtils.makeByte4(metadata.getRecordsQty()); - headerBytes[4] = b[0]; - headerBytes[5] = b[1]; - headerBytes[6] = b[2]; - headerBytes[7] = b[3]; - - b = BitUtils.makeByte2(metadata.getFullHeaderLength()); - headerBytes[8] = b[0]; - headerBytes[9] = b[1]; - - b = BitUtils.makeByte2(metadata.getOneRecordLength()); - headerBytes[10] = b[0]; - headerBytes[11] = b[1]; - - headerBytes[12]=0; - headerBytes[13]=0; - - headerBytes[14] = metadata.getUncompletedTxFlag(); - headerBytes[15] = metadata.getEcnryptionFlag(); - - return headerBytes; - } + + public static void fillHeaderFields(DbfMetadata metadata, byte[] headerBytes) { + metadata.setType(DbfFileTypeEnum.fromInt(headerBytes[0])); + metadata.setUpdateDate(JdbfUtils.createDate(headerBytes[1], headerBytes[2], headerBytes[3])); + metadata.setRecordsQty(BitUtils.makeInt(headerBytes[4], headerBytes[5], headerBytes[6], headerBytes[7])); + metadata.setFullHeaderLength(BitUtils.makeInt(headerBytes[8], headerBytes[9])); + metadata.setOneRecordLength(BitUtils.makeInt(headerBytes[10], headerBytes[11])); + metadata.setUncompletedTxFlag(headerBytes[14]); + metadata.setEcnryptionFlag(headerBytes[15]); + } + + public static void readFields(DbfMetadata metadata, ByteArrayInputStream inputStream) throws IOException { + List fields = new ArrayList(); + byte[] fieldBytes = new byte[JdbfUtils.FIELD_RECORD_LENGTH]; + int headerLength = 0; + int fieldLength = 0; + while (true) { + inputStream.read(fieldBytes); + DbfField field = createDbfField(fieldBytes); + fields.add(field); + + fieldLength += field.getLength(); + headerLength += fieldBytes.length; + + long oldAvailable = inputStream.available(); + int terminator = inputStream.read(); + if (terminator == JdbfUtils.HEADER_TERMINATOR) { + break; + } else { + inputStream.reset(); + inputStream.skip(inputStream.available() - oldAvailable); + } + } + fieldLength += 1; + headerLength += 32; + headerLength += 1; + + if (headerLength != metadata.getFullHeaderLength()) { + // can throw Exception here + } + if (fieldLength != metadata.getOneRecordLength()) { + // can throw Exception here + } + + metadata.setFields(fields); + } + + public static DbfField createDbfField(byte[] fieldBytes) { + DbfField field = new DbfField(); + // 1. Set name + { + int i = 0; + for (i = 0; i < 11 && fieldBytes[i] > 0; i++) ; + field.setName(new String(fieldBytes, 0, i)); + } + // 2. Set type + field.setType(DbfFieldTypeEnum.fromChar((char) fieldBytes[11])); + // 3. Set length + { + int length = fieldBytes[16]; + if (length < 0) { + length = 256 + length; + } + field.setLength(length); + } + // 4. Set number of decimal places + field.setNumberOfDecimalPlaces(fieldBytes[17]); + + return field; + } + + public static void writeDbfField(DbfField field, byte[] fieldBytes) { + BitUtils.memset(fieldBytes, 0); + byte[] nameBytes = field.getName().getBytes(); + int nameLength = nameBytes.length; + if (nameLength > 11) { + // throw error here! + } + System.arraycopy(nameBytes, 0, fieldBytes, 0, nameBytes.length); + fieldBytes[11] = field.getType().toByte(); + int length = field.getLength(); + fieldBytes[16] = (byte) (length & 0xff); + fieldBytes[17] = (byte) (field.getNumberOfDecimalPlaces() & 0xff); + } + + @SuppressWarnings("deprecation") + public static byte[] toByteArrayHeader(DbfMetadata metadata) { + byte[] headerBytes = new byte[16]; + BitUtils.memset(headerBytes, 0); + + + headerBytes[0] = metadata.getType().toByte(); + + Date updateDate = metadata.getUpdateDate(); + // date + if (updateDate == null) { + updateDate = new Date(); + } + // write date bytes + { + byte[] dateBytes = JdbfUtils.writeDateForHeader(updateDate); + headerBytes[1] = dateBytes[0]; + headerBytes[2] = dateBytes[1]; + headerBytes[3] = dateBytes[2]; + } + + byte[] b = BitUtils.makeByte4(metadata.getRecordsQty()); + headerBytes[4] = b[0]; + headerBytes[5] = b[1]; + headerBytes[6] = b[2]; + headerBytes[7] = b[3]; + + b = BitUtils.makeByte2(metadata.getFullHeaderLength()); + headerBytes[8] = b[0]; + headerBytes[9] = b[1]; + + b = BitUtils.makeByte2(metadata.getOneRecordLength()); + headerBytes[10] = b[0]; + headerBytes[11] = b[1]; + + headerBytes[12] = 0; + headerBytes[13] = 0; + + headerBytes[14] = metadata.getUncompletedTxFlag(); + headerBytes[15] = metadata.getEcnryptionFlag(); + + return headerBytes; + } }