Skip to content

Commit 4eadef8

Browse files
committed
1.11 version commit. Added ability of append records to existed DBF file.
1 parent bef2ee0 commit 4eadef8

File tree

10 files changed

+306
-46
lines changed

10 files changed

+306
-46
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ Version history
33

44
|Date | Ver | Author | Description |
55
|----------|-----|-----------------------------|--------------------|
6+
|2020-08-31| 1.11| galisha. | Added ability of append records to existed DBF file. |
67
|2020-01-08| 1.10| frankvdh, galisha. | Fixed two issues: 1) Column names not upper case 2) Buffer reads are not stream-safe |
78
|2019-07-10| 1.09| galisha. | Added handling for such bad numeric as: 1219,.0000 |
89
|2016-10-16| 1.08| galisha. | Added validation rules for dbf header in case handling of non-dbf files. |

README.md

Lines changed: 70 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
# DbfEngine Java API
22

3-
DbfEngine - a Java API to read, write xBase(DBASE, Foxpro dbf files). Also API allows read memory files (.mem) of Foxpro.
4-
Samples for both operations you can see at [DbfEngine javadoc](https://www.smart-flex.ru/htm/de_api/index.html)
3+
DbfEngine - a Java API to read, write and append xBase(DBASE, Foxpro dbf files). Also API allows read memory files (.mem) of Foxpro.
4+
Samples for both operations you can see at [DbfEngine javadoc](https://smart-flex.ru/htm/de_api/index.html)
55

66
This API is pure lightweight library without memory consumption and any third party libraries (there are no java loggers and etc.)
7-
The DBF Java API is intended as engine for data exchange purposes.
7+
The DBF Java API is intended as a fast engine for data exchange purposes.
88

99
## Features
1010

@@ -24,7 +24,73 @@ This version was tested under MS Foxpro 2.6 without memo field support.
2424

2525
DbfEngine requires JDK 1.6 or higher.
2626

27-
## Licensing
27+
## Code samples
28+
```java
29+
public class Fp26Reader {
30+
private static void testRead() {
31+
DbfIterator dbfIterator = DbfEngine.getReader(
32+
Fp26Reader.class.getResourceAsStream("FP_26_SAMPLE.DBF"), null);
33+
34+
while (dbfIterator.hasMoreRecords()) {
35+
DbfRecord dbfRecord = dbfIterator.nextRecord();
36+
String string = dbfRecord.getString("string");
37+
float sumFloat = dbfRecord.getFloat("sum_f");
38+
BigDecimal sumNumeric = dbfRecord.getBigDecimal("sum_n");
39+
boolean bool = dbfRecord.getBoolean("bool_val");
40+
Date date = dbfRecord.getDate("date_val");
41+
42+
System.out.println(string + " " + sumFloat + " " + sumNumeric + " " +
43+
bool + " " + date);
44+
}
45+
}
46+
47+
public static void main(String[] args) {
48+
Fp26Reader.testRead();
49+
}
50+
}
51+
public class Fp26Writer {
52+
private static void testWrite() {
53+
DbfAppender dbfAppender = DbfEngine.getWriter("WRT_PERSON.DBF", DbfCodePages.Cp866);
54+
55+
DbfColumn dc01 = new DbfColumn("magic", DbfColumnTypes.Logical, 0, 0);
56+
DbfColumn dc02 = new DbfColumn("actor", DbfColumnTypes.Character, 60, 0);
57+
DbfColumn dc03 = new DbfColumn("currdate", DbfColumnTypes.Date, 0, 0);
58+
DbfColumn dc04 = new DbfColumn("hit", DbfColumnTypes.Numeric, 10, 2);
59+
DbfColumn dc05 = new DbfColumn("forever", DbfColumnTypes.Logical, 0, 0);
60+
dbfAppender.defineColumns(dc01, dc02, dc03, dc04, dc05);
61+
62+
DbfStatement statement = dbfAppender.getStatement();
63+
statement.setString("actor", "Chuck Norris");
64+
statement.setDate("currdate", new Date());
65+
statement.setBigDecimal("hit", new BigDecimal("500.5"));
66+
67+
statement.insertStatement();
68+
69+
statement.setBoolean("magic", Boolean.TRUE);
70+
statement.setString("actor", "Bruce Lee");
71+
statement.setBigDecimal("hit", new BigDecimal("1000.10"));
72+
statement.setBoolean("forever", Boolean.TRUE);
73+
74+
statement.insertStatement();
75+
76+
dbfAppender.writeDbfAndClose();
77+
}
78+
79+
public static void main(String[] args) {
80+
Fp26Writer.testWrite();
81+
}
82+
}
83+
```
84+
85+
#### Licensing
2886

2987
DbfEngine is issued on under the GNU Lesser General Public License.
3088

89+
#### Support
90+
91+
If you have any issues or questions or suggestions you can send me a letter by email: <gali.shaimardanov@gmail.com>
92+
93+
#### Related project
94+
95+
Djf is Desktop Java Forms, a compact master-detail UI library like FoxBase, but based on Swing: [You can see Djf here](https://github.com/smart-flex/Djf)
96+

pom.xml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,8 @@
66

77
<groupId>ru.smartflex.tools.dbf</groupId>
88
<artifactId>DbfEngine</artifactId>
9-
<version>1.10</version>
10-
<name>DbfEngine - a small Java library to read, write xBase(DBASE, Foxpro dbf/mem files)</name>
9+
<version>1.11</version>
10+
<name>DbfEngine - a small Java library to read, write, append xBase(DBASE, Foxpro dbf/mem files)</name>
1111

1212
<properties>
1313
<junit.version>4.12</junit.version>

src/main/java/ru/smartflex/tools/dbf/DbfAppender.java

Lines changed: 87 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,6 @@
11
package ru.smartflex.tools.dbf;
22

3-
import java.io.File;
4-
import java.io.FileNotFoundException;
5-
import java.io.FileOutputStream;
6-
import java.io.IOException;
7-
import java.io.InputStream;
8-
import java.io.OutputStream;
3+
import java.io.*;
94
import java.util.Calendar;
105
import java.util.Date;
116
import java.util.List;
@@ -29,18 +24,34 @@ public class DbfAppender {
2924
private OutputStream dbfStream = null;
3025
private DbfStatement dbfStatement = null;
3126

27+
private File dbfFileExisted = null;
28+
private boolean flagAppendExistedFile = false;
29+
3230
DbfAppender(File dbfFile, DbfCodePages dbfCodePage) {
3331
this.dbfCodePage = dbfCodePage;
3432
if (dbfCodePage == null) {
3533
throw new DbfEngineException(DbfConstants.EXCP_CP_MISSED);
3634
}
37-
createOutputStream(dbfFile);
35+
createOutputStream(dbfFile, false);
36+
}
37+
38+
/**
39+
* Constructor for existed file
40+
* @param dbfFile dbf file
41+
* @param dbfHeader header of dbf file
42+
* @since 1.11
43+
*/
44+
DbfAppender(File dbfFile, DbfHeader dbfHeader) {
45+
this.dbfFileExisted = dbfFile;
46+
this.dbfHeader = dbfHeader;
47+
this.dbfCodePage = dbfHeader.getDbfCodePages();
48+
flagAppendExistedFile = true;
3849
}
3950

40-
private void createOutputStream(File dbfFile) {
51+
private void createOutputStream(File dbfFile, boolean append) {
4152
if (dbfStream == null) {
4253
try {
43-
dbfStream = new FileOutputStream(dbfFile);
54+
dbfStream = new FileOutputStream(dbfFile, append);
4455
} catch (FileNotFoundException e) {
4556
throw new DbfEngineException(DbfConstants.EXCP_DBF_ERR_CREATE,
4657
e);
@@ -63,6 +74,9 @@ private void createOutputStream(File dbfFile) {
6374
* @since 1.00
6475
*/
6576
public void defineColumns(DbfColumn... dbfColumns) {
77+
if (flagAppendExistedFile) {
78+
throw new DbfEngineException(DbfConstants.EXCP_DEF_COLS_NOT_ALLOWED);
79+
}
6680
if (dbfColumns.length == 0) {
6781
throw new DbfEngineException(DbfConstants.EXCP_COLUMN_ADD);
6882
}
@@ -92,6 +106,14 @@ public void defineColumns(DbfColumn... dbfColumns) {
92106
* @since 1.00
93107
*/
94108
public void writeDbfAndClose() {
109+
if (flagAppendExistedFile) {
110+
writeDbfAndCloseForAppendMode();
111+
} else {
112+
writeDbfAndCloseForWriteMode();
113+
}
114+
}
115+
116+
private void writeDbfAndCloseForWriteMode() {
95117

96118
if (dbfHeader == null) {
97119
throw new DbfEngineException(DbfConstants.EXCP_COLUMN_ADD);
@@ -182,11 +204,63 @@ void registerNewRecord() {
182204
recordAmount++;
183205
}
184206

207+
private void writeDbfAndCloseForAppendMode() {
208+
if (recordAmount == 0) {
209+
// no one record were added. Therefore return.
210+
return;
211+
}
212+
213+
byte[] header = new byte[7];
214+
lockCalendar.lock();
215+
try {
216+
calendar.setTime(new Date());
217+
int day = calendar.get(Calendar.DAY_OF_MONTH);
218+
int month = calendar.get(Calendar.MONTH);
219+
int year = calendar.get(Calendar.YEAR) % 100;
220+
header[0] = (byte) year;
221+
header[1] = (byte) month;
222+
header[2] = (byte) day;
223+
} finally {
224+
lockCalendar.unlock();
225+
}
226+
int totalRecords = dbfHeader.getCountRecords() + recordAmount;
227+
header[3] = (byte) totalRecords;
228+
header[4] = (byte) (totalRecords >> 8);
229+
header[5] = (byte) (totalRecords >> 16);
230+
header[6] = (byte) (totalRecords >> 24);
231+
232+
// change dbf header of existed file
233+
RandomAccessFile randomFile = null;
234+
try {
235+
randomFile = new RandomAccessFile(dbfFileExisted, "rw");
236+
randomFile.seek(1);
237+
randomFile.write(header);
238+
} catch (FileNotFoundException e) {
239+
throw new DbfEngineException(DbfConstants.EXCP_DBF_NOT_EXISTS, e);
240+
} catch (IOException e) {
241+
throw new DbfEngineException(DbfConstants.EXCP_IO_ERROR, e);
242+
} finally {
243+
if (randomFile != null) {
244+
try {
245+
randomFile.close();
246+
} catch (Exception e) {
247+
throw new DbfEngineException(DbfConstants.EXCP_IO_ERROR, e);
248+
}
249+
}
250+
}
251+
252+
createOutputStream(dbfFileExisted, true);
253+
254+
writeDbf(null, null);
255+
}
256+
185257
private void writeDbf(byte[] header, byte[] columns) {
186258
try {
187-
dbfStream.write(header);
188-
dbfStream.write(columns);
189-
dbfStream.write(DbfConstants.DBF_END_HEADER);
259+
if (!flagAppendExistedFile) {
260+
dbfStream.write(header);
261+
dbfStream.write(columns);
262+
dbfStream.write(DbfConstants.DBF_END_HEADER);
263+
}
190264

191265
if (dbfStatement != null) {
192266
InputStream dbfBody = dbfStatement.getDbfBodyInputStream();
@@ -216,7 +290,7 @@ private void writeDbf(byte[] header, byte[] columns) {
216290
dbfStream.flush();
217291
dbfStream.close();
218292
} catch (IOException e) {
219-
throw new DbfEngineException(DbfConstants.EXCP_COLUMN_ADD, e);
293+
throw new DbfEngineException(DbfConstants.EXCP_IO_ERROR, e);
220294
}
221295
}
222296

src/main/java/ru/smartflex/tools/dbf/DbfCodePages.java

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ protected String getCharsetName() {
4646
return charsetName;
4747
}
4848

49-
protected DbfCodePages getByDbfCode(int code) {
49+
protected static DbfCodePages getByDbfCode(int code) {
5050
DbfCodePages dcp = null;
5151

5252
for (DbfCodePages dcps : DbfCodePages.values()) {
@@ -58,4 +58,23 @@ protected DbfCodePages getByDbfCode(int code) {
5858

5959
return dcp;
6060
}
61+
62+
/**
63+
* Defines DbfCodePage by charset name
64+
* @param charsetName charset name
65+
* @return DbfCodePages
66+
* @since 1.11
67+
*/
68+
protected static DbfCodePages getByCharsetName(String charsetName) {
69+
DbfCodePages dcp = null;
70+
71+
for (DbfCodePages dcps : DbfCodePages.values()) {
72+
if (dcps.getCharsetName().equals((charsetName))) {
73+
dcp = dcps;
74+
break;
75+
}
76+
}
77+
78+
return dcp;
79+
}
6180
}

src/main/java/ru/smartflex/tools/dbf/DbfConstants.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ public interface DbfConstants {
2626
String EXCP_DBF_EXISTS = "File is existed already";
2727
String EXCP_DBF_ERR_CREATE = "Error with file creating";
2828
String EXCP_CURR_REC_INFO = " on record: ";
29+
String EXCP_DBF_NOT_EXISTS = "File is not existed";
30+
String EXCP_DEF_COLS_NOT_ALLOWED = "Define columns is not allowed for append mode of existed file";
2931

3032
String EXCP_COLUMN_ADD = "There were no column added";
3133
String EXCP_COLUMN_EMPTY = "Column has to be named";

0 commit comments

Comments
 (0)