From 96617000308124599ebc80a70de9b0248de620d2 Mon Sep 17 00:00:00 2001 From: Maurice Muschalik Date: Fri, 28 Jul 2017 22:32:19 +1000 Subject: [PATCH] add support to view deleted records --- .../java/com/linuxense/javadbf/DBFReader.java | 59 +++++++++++++++--- .../com/linuxense/javadbf/DBFReaderTest.java | 12 +++- .../com/linuxense/javadbf/ReadDBFAssert.java | 34 +++++++++- src/test/resources/test_delete.dbf | Bin 0 -> 301 bytes 4 files changed, 92 insertions(+), 13 deletions(-) create mode 100644 src/test/resources/test_delete.dbf diff --git a/src/main/java/com/linuxense/javadbf/DBFReader.java b/src/main/java/com/linuxense/javadbf/DBFReader.java index 8541ef0..90c2ebc 100644 --- a/src/main/java/com/linuxense/javadbf/DBFReader.java +++ b/src/main/java/com/linuxense/javadbf/DBFReader.java @@ -142,6 +142,7 @@ public class DBFReader extends DBFBase implements Closeable { private DataInputStream dataInputStream; private DBFHeader header; private boolean trimRightSpaces = true; + private boolean showDeletedRows = false; private DBFMemoFile memoFile = null; @@ -159,26 +160,55 @@ public class DBFReader extends DBFBase implements Closeable { * @param in the InputStream where the data is read from. */ public DBFReader(InputStream in) { - this(in,null); + this(in,null,false); } - + + /** + * Intializes a DBFReader object. + * + * Tries to detect charset from file, if failed uses default charset ISO-8859-1 + * When this constructor returns the object will have completed reading the + * header (meta date) and header information can be queried there on. And it + * will be ready to return the first row. + * + * @param in the InputStream where the data is read from. + * @param showDeletedRows can be used to identify records that have been deleted. + */ + public DBFReader(InputStream in, Boolean showDeletedRows) { + this(in,null, showDeletedRows); + } + /** * Initializes a DBFReader object. - * + * * When this constructor returns the object will have completed reading the * header (meta date) and header information can be queried there on. And it * will be ready to return the first row. - * + * * @param in the InputStream where the data is read from. * @param charset charset used to decode field names and field contents. If null, then is autedetected from dbf file */ - public DBFReader(InputStream in,Charset charset) { + public DBFReader(InputStream in,Charset charset) { this(in, charset, false); } + + /** + * Initializes a DBFReader object. + * + * When this constructor returns the object will have completed reading the + * header (meta date) and header information can be queried there on. And it + * will be ready to return the first row. + * + * @param in the InputStream where the data is read from. + * @param charset charset used to decode field names and field contents. If null, then is autedetected from dbf file + * @param showDeletedRows can be used to identify records that have been deleted. + */ + public DBFReader(InputStream in,Charset charset,Boolean showDeletedRows) { try { this.dataInputStream = new DataInputStream(in); this.header = new DBFHeader(); this.header.read(this.dataInputStream, charset); setCharset(this.header.getUsedCharset()); + this.showDeletedRows = showDeletedRows; /* it might be required to leap to the start of records at times */ int fieldSize = this.header.getFieldDescriptorSize(); @@ -234,6 +264,15 @@ public Date getLastModificationDate() { * @param index Index of the field. Index of the first field is zero. */ public DBFField getField(int index) { + + // if showing deleted rows, ensure first column is the deleted flag field + if(showDeletedRows) { + if(index == 0) + return new DBFField("DELETED", DBFDataType.LOGICAL); + else + return new DBFField(this.header.fieldArray[index-1]); + } + return new DBFField(this.header.fieldArray[index]); } @@ -241,7 +280,7 @@ public DBFField getField(int index) { * Returns the number of field in the DBF. */ public int getFieldCount() { - return this.header.userFieldArray.length; + return this.header.userFieldArray.length + (this.showDeletedRows ? 1 : 0); } /** @@ -257,8 +296,9 @@ public Object[] nextRecord() { List recordObjects = new ArrayList<>(this.getFieldCount()); try { boolean isDeleted = false; + do { - if (isDeleted) { + if (isDeleted && !showDeletedRows) { skip(this.header.recordLength - 1); } int t_byte = this.dataInputStream.readByte(); @@ -266,7 +306,10 @@ public Object[] nextRecord() { return null; } isDeleted = t_byte == '*'; - } while (isDeleted); + } while (isDeleted && !showDeletedRows); + + if(showDeletedRows) + recordObjects.add(isDeleted); for (int i = 0; i < this.header.fieldArray.length; i++) { DBFField field = this.header.fieldArray[i]; diff --git a/src/test/java/com/linuxense/javadbf/DBFReaderTest.java b/src/test/java/com/linuxense/javadbf/DBFReaderTest.java index 350c39f..47280ff 100644 --- a/src/test/java/com/linuxense/javadbf/DBFReaderTest.java +++ b/src/test/java/com/linuxense/javadbf/DBFReaderTest.java @@ -121,8 +121,16 @@ public void testToString () throws IOException { DBFUtils.close(reader); } } - - + + @Test + public void testReadBooksAddedDeleteField() throws IOException { + ReadDBFAssert.testReadDBFFile("books", 12, 10, true); + } + + @Test + public void testReadDBFFileDeletedRecords() throws IOException { + ReadDBFAssert.testReadDBFFileDeletedRecords("test_delete",3,1); + } @Test(expected=DBFException.class) public void testFailStream() throws DBFException, IOException{ diff --git a/src/test/java/com/linuxense/javadbf/ReadDBFAssert.java b/src/test/java/com/linuxense/javadbf/ReadDBFAssert.java index da4a3f4..0d4b525 100644 --- a/src/test/java/com/linuxense/javadbf/ReadDBFAssert.java +++ b/src/test/java/com/linuxense/javadbf/ReadDBFAssert.java @@ -33,13 +33,17 @@ private ReadDBFAssert() { } public static void testReadDBFFile(String fileName, int expectedColumns, int expectedRows) throws DBFException, IOException { - testReadDBFFile(new File("src/test/resources/" + fileName + ".dbf"), expectedColumns, expectedRows); + testReadDBFFile(fileName, expectedColumns, expectedRows, false); } - public static void testReadDBFFile(File file, int expectedColumns, int expectedRows) throws DBFException, IOException { + public static void testReadDBFFile(String fileName, int expectedColumns, int expectedRows, Boolean showDeletedRows) throws DBFException, IOException { + testReadDBFFile(new File("src/test/resources/" + fileName + ".dbf"), expectedColumns, expectedRows, showDeletedRows); + } + + public static void testReadDBFFile(File file, int expectedColumns, int expectedRows, Boolean showDeletedRows) throws DBFException, IOException { DBFReader reader = null; try { - reader = new DBFReader(new BufferedInputStream(new FileInputStream(file))); + reader = new DBFReader(new BufferedInputStream(new FileInputStream(file)), showDeletedRows); testReadDBFFile(reader, expectedColumns, expectedRows); } finally { DBFUtils.close(reader); @@ -68,4 +72,28 @@ public static void testReadDBFFile(DBFReader reader, int expectedColumns, int ex Assert.assertEquals(expectedRows, countedRows); Assert.assertEquals(expectedRows, reader.getRecordCount()); } + + public static void testReadDBFFileDeletedRecords(String fileName, int expectedRows, int expectedDeleted) throws DBFException, IOException { + + DBFReader reader = null; + try { + reader = new DBFReader(new BufferedInputStream(new FileInputStream(new File("src/test/resources/" + fileName + ".dbf"))), true); + Object[] rowObject; + + int countedRows = 0; + int countedDeletes = 0; + while ((rowObject = reader.nextRecord()) != null) { + if(rowObject[0] == Boolean.TRUE) + countedDeletes++; + countedRows++; + } + + Assert.assertEquals(countedRows, expectedRows); + Assert.assertEquals(countedDeletes, expectedDeleted); + } finally { + DBFUtils.close(reader); + } + + + } } diff --git a/src/test/resources/test_delete.dbf b/src/test/resources/test_delete.dbf new file mode 100644 index 0000000000000000000000000000000000000000..e2544a8e123cba10419a69634b9489a031894de3 GIT binary patch literal 301 zcmZQ6WtU-QU|=X^umzHsz{SY`QxHwW&Ck)-6-^A8>uiI}V_@J_$jZ;iQy>#)