package org.geotools.dbffile;
import java.io.*;
import java.math.BigDecimal;
import java.util.*;
import org.geotools.misc.FormatedString;
import com.vividsolutions.jump.io.EndianDataOutputStream;
import java.nio.charset.Charset;
/** a class for writing dbf files
* @author Ian Turton
* modified by Michaël MICHAUD on 3 nov. 2004
*/
public class DbfFileWriter implements DbfConsts{
private final static boolean DEBUG=false;
private final static String DBC="DbFW>";
int NoFields =1;
int NoRecs = 0;
int recLength = 0;
DbfFieldDef fields[];
EndianDataOutputStream ls;
private boolean header = false;
private Charset charset = Charset.defaultCharset();
public DbfFileWriter(String file) throws IOException {
if(DEBUG)System.out.println("---->uk.ac.leeds.ccg.dbffile.DbfFileWriter constructed. Will identify itself as "+DBC);
ls = new EndianDataOutputStream(new BufferedOutputStream(new FileOutputStream(file)));
}
public void writeHeader(DbfFieldDef f[], int nrecs) throws IOException{
NoFields = f.length;
NoRecs = nrecs;
fields = new DbfFieldDef[NoFields];
for(int i=0;i<NoFields;i++){
fields[i]=f[i];
}
ls.writeByteLE(3); // ID - dbase III with out memo
// sort out the date
Calendar calendar = new GregorianCalendar();
Date trialTime = new Date();
calendar.setTime(trialTime);
ls.writeByteLE(calendar.get(Calendar.YEAR) - DBF_CENTURY);
ls.writeByteLE(calendar.get(Calendar.MONTH) + 1 ); // month is 0-indexed
ls.writeByteLE(calendar.get(Calendar.DAY_OF_MONTH));
int dataOffset = 32 * NoFields+32 + 1;
for(int i=0;i<NoFields;i++){
recLength+=fields[i].fieldlen;
}
recLength++; // delete flag
if(DEBUG)System.out.println(DBC+"rec length "+recLength);
ls.writeIntLE(NoRecs);
ls.writeShortLE(dataOffset); // length of header
ls.writeShortLE(recLength);
for(int i=0;i<20;i++) ls.writeByteLE(0); // 20 bytes of junk!
// field descriptions
for(int i=0;i<NoFields;i++){
//patch from Hisaji Ono for Double byte characters
ls.write(fields[i].fieldname.toString().getBytes(charset.name()), 0, 11 ); // [Matthias Scholz 04.Sept.2010] Charset added
ls.writeByteLE(fields[i].fieldtype);
for(int j=0;j<4;j++) ls.writeByteLE(0); // junk
ls.writeByteLE(fields[i].fieldlen);
ls.writeByteLE(fields[i].fieldnumdec);
for(int j=0;j<14;j++) ls.writeByteLE(0); // more junk
}
ls.writeByteLE(0xd);
header=true;
}
public void writeRecords(Vector[] recs) throws DbfFileException,IOException {
if(!header){
throw(new DbfFileException("Must write header before records"));
}
int i=0;
try {
if(DEBUG)System.out.println(DBC+":writeRecords writing "+recs.length+" records");
for(i=0;i<recs.length;i++){
if(recs[i].size()!=NoFields) throw new DbfFileException("wrong number of records in "+ i+"th record "+
recs[i].size()+" expected "+NoFields);
writeRecord(recs[i]);
}
}
catch(DbfFileException e) {throw new DbfFileException(DBC+"at rec "+i+"\n"+e);}
}
public void writeRecord(Vector rec)throws DbfFileException,IOException{
if(!header){
throw(new DbfFileException(DBC+"Must write header before records"));
}
if(rec.size()!=NoFields) throw new DbfFileException(DBC+"wrong number of fields "+
rec.size()+" expected "+NoFields);
String s;
ls.writeByteLE(' ');
int len;
StringBuffer tmps;
for(int i=0;i<NoFields;i++){
len = fields[i].fieldlen;
Object o = rec.elementAt(i);
switch(fields[i].fieldtype){
case 'C':
case 'c':
case 'D': //Added by [Jon Aquino]
//case 'L': moved to the end by mmichaud
case 'M':
case 'G':
//chars
String ss = (String) o;
while (ss.length() < fields[i].fieldlen) {
//need to fill it with ' ' chars
//this should converge quickly
ss = ss + " ";
}
tmps = new StringBuffer(ss);
tmps.setLength(fields[i].fieldlen);
//patch from Hisaji Ono for Double byte characters
ls.write(tmps.toString().getBytes(charset.name()), fields[i].fieldstart,fields[i].fieldlen); // [Matthias Scholz 04.Sept.2010] Charset added
break;
case 'N':
case 'n':
// int?
String fs="";
if(fields[i].fieldnumdec==0){
if (o instanceof Integer) {
fs = FormatedString.format(((Integer)o).intValue(),fields[i].fieldlen);
}
// case LONG added by mmichaud on 18 sept. 2004
else if (o instanceof Long) {
fs = FormatedString.format(((Long)o).toString(), 0, fields[i].fieldlen);
}
else if (o instanceof java.math.BigDecimal) {
fs = FormatedString.format(((BigDecimal)o).toString(), 0, fields[i].fieldlen);
}
else;
if (fs.length()>fields[i].fieldlen) fs = FormatedString.format(0,fields[i].fieldlen);
ls.writeBytesLE(fs);
break;
}
else {
if (o instanceof Double) {
fs = FormatedString.format(((Double)o).toString(), fields[i].fieldnumdec, fields[i].fieldlen);
}
else if (o instanceof java.math.BigDecimal) {
fs = FormatedString.format(((BigDecimal)o).toString(), fields[i].fieldnumdec, fields[i].fieldlen);
}
else;
if (fs.length()>fields[i].fieldlen) fs = FormatedString.format("0.0",fields[i].fieldnumdec,fields[i].fieldlen);
ls.writeBytesLE(fs);
break;
}
case 'F':
case 'f':
//double
s = ((Double)o).toString();
String x = FormatedString.format(s,fields[i].fieldnumdec,fields[i].fieldlen);
ls.writeBytesLE(x);
break;
// Case 'logical' added by mmichaud on 18 sept. 2004
case 'L':
//boolean
if (o==null || o.equals("") || o.equals(" ")) ls.writeBytesLE(" ");
else {
boolean b = ((Boolean)o).booleanValue();
ls.writeBytesLE(b?"T":"F");
}
break;
}// switch
}// fields
}
public void close() throws IOException {
ls.writeByteLE(0x1a); // eof mark
ls.close();
}
int dp = 2; // default number of decimals to write
/**
* @return the charset
*/
public Charset getCharset() {
return charset;
}
/**
* @param charset the charset to set
*/
public void setCharset(Charset charset) {
this.charset = charset;
}
}