Files
bodyshop-uploader/dotnetdbf/DBFWriter.cs
2022-02-11 15:06:05 -08:00

529 lines
17 KiB
C#

/*
DBFWriter
Class for defining a DBF structure and addin data to that structure and
finally writing it to an OutputStream.
This file is part of DotNetDBF packege.
original author (javadbf): anil@linuxense.com 2004/03/31
license: LGPL (http://www.gnu.org/copyleft/lesser.html)
ported to C# (DotNetDBF): Jay Tuley <jay+dotnetdbf@tuley.name> 6/28/2007
*/
using System;
using System.Collections;
using System.Collections.Generic;
using System.IO;
namespace DotNetDBF
{
public class DBFWriter : DBFBase, IDisposable
{
private DBFHeader header;
private Stream raf;
private int recordCount;
private List<object> v_records = new List<object>();
private Stream _dataMemo;
private string _dataMemoLoc;
/// Creates an empty Object.
public DBFWriter()
{
header = new DBFHeader();
}
/// Creates a DBFWriter which can append to records to an existing DBF file.
/// @param dbfFile. The file passed in should be a valid DBF file.
/// @exception Throws DBFException if the passed in file does exist but not a valid DBF file, or if an IO error occurs.
public DBFWriter(string dbfFile)
{
try
{
raf =
File.Open(dbfFile,
FileMode.OpenOrCreate,
FileAccess.ReadWrite);
DataMemoLoc = Path.ChangeExtension(dbfFile, "dbt");
/* before proceeding check whether the passed in File object
is an empty/non-existent file or not.
*/
if (raf.Length == 0)
{
header = new DBFHeader();
return;
}
header = new DBFHeader();
header.Read(new BinaryReader(raf));
/* position file pointer at the end of the raf */
raf.Seek(-1, SeekOrigin.End);
/* check whether the last byte is 0x1A (end of file marker for dbf files) - in this case move 1 byte back to ignore it when writing new records */
var lastByte = raf.ReadByte(); /* Advances to end of stream */
if (lastByte == DBFFieldType.EndOfData)
{
raf.Seek(-1, SeekOrigin.End);
}
}
catch (FileNotFoundException e)
{
throw new DBFException("Specified file is not found. ", e);
}
catch (IOException e)
{
throw new DBFException(" while reading header", e);
}
recordCount = header.NumberOfRecords;
}
public DBFWriter(Stream dbfFile)
{
raf = dbfFile;
/* before proceeding check whether the passed in File object
is an empty/non-existent file or not.
*/
if (raf.Length == 0)
{
header = new DBFHeader();
return;
}
header = new DBFHeader();
header.Read(new BinaryReader(raf));
/* position file pointer at the end of the raf */
raf.Seek(-1, SeekOrigin.End);
/* check whether the last byte is 0x1A (end of file marker for dbf files) - in this case move 1 byte back to ignore it when writing new records */
var lastByte = raf.ReadByte(); /* Advances to end of stream */
if (lastByte == DBFFieldType.EndOfData)
{
raf.Seek(-1, SeekOrigin.End);
}
recordCount = header.NumberOfRecords;
}
public byte Signature
{
get => header.Signature;
set => header.Signature = value;
}
public string DataMemoLoc
{
get => _dataMemoLoc;
set
{
_dataMemoLoc = value;
_dataMemo?.Close();
_dataMemo = File.Open(_dataMemoLoc,
FileMode.OpenOrCreate,
FileAccess.ReadWrite);
}
}
public Stream DataMemo
{
get => _dataMemo;
set => _dataMemo = value;
}
public byte LanguageDriver
{
set
{
if (header.LanguageDriver != 0x00)
{
throw new DBFException("LanguageDriver has already been set");
}
header.LanguageDriver = value;
}
}
public DBFField[] Fields
{
get => header.FieldArray;
set
{
if (header.FieldArray != null)
{
throw new DBFException("Fields has already been set");
}
if (value == null
|| value.Length == 0)
{
throw new DBFException("Should have at least one field");
}
for (var i = 0; i < value.Length; i++)
{
if (value[i] == null)
{
throw new DBFException("Field " + (i + 1) + " is null");
}
}
header.FieldArray = value;
try
{
if (raf != null
&& raf.Length == 0)
{
/*
this is a new/non-existent file. So write header before proceeding
*/
header.Write(new BinaryWriter(raf));
}
}
catch (IOException e)
{
throw new DBFException("Error accessing file", e);
}
}
}
#region IDisposable Members
/// <summary>Performs application-defined tasks associated with freeing, releasing,
/// or resetting unmanaged resources.</summary>
/// <filterpriority>2</filterpriority>
public void Dispose()
{
Close();
}
#endregion
/**
Add a record.
*/
public void WriteRecord(params object[] values)
{
if (raf == null)
{
throw new DBFException(
"Not initialized with file for WriteRecord use, use AddRecord instead");
}
AddRecord(values, true);
}
public void AddRecord(params object[] values)
{
if (raf != null)
{
throw new DBFException(
"Appending to a file, requires using WriteRecord instead");
}
AddRecord(values, false);
}
private void AddRecord(object[] values, bool writeImmediately)
{
if (header.FieldArray == null)
{
throw new DBFException(
"Fields should be set before adding records");
}
if (values == null)
{
throw new DBFException("Null cannot be added as row");
}
if (values.Length
!= header.FieldArray.Length)
{
throw new DBFException(
"Invalid record. Invalid number of fields in row");
}
for (var i = 0; i < header.FieldArray.Length; i++)
{
var fld = header.FieldArray[i];
var val = values[i];
if (val is null || val is DBNull)
{
continue;
}
void ThrowErrorIfNot<T>()
{
if (!(val is T))
{
throw new DBFRecordException($"Invalid value '{val}' for field {i}({fld.Name}{fld.DataType})", 0);
}
}
switch (fld.DataType)
{
case NativeDbType.Char:
//ignore all objects have ToString()
break;
case NativeDbType.Logical:
ThrowErrorIfNot<bool>();
break;
case NativeDbType.Numeric:
ThrowErrorIfNot<IConvertible>();
break;
case NativeDbType.Date:
ThrowErrorIfNot<DateTime>();
break;
case NativeDbType.Float:
ThrowErrorIfNot<IConvertible>();
break;
case NativeDbType.Memo:
ThrowErrorIfNot<MemoValue>();
break;
default:
throw new ArgumentOutOfRangeException();
}
}
if (!writeImmediately)
{
v_records.Add(values);
}
else
{
try
{
WriteRecord(new BinaryWriter(raf), values);
recordCount++;
}
catch (IOException e)
{
throw new DBFException(
"Error occured while writing record. ", e);
}
}
}
///Writes the set data to the OutputStream.
public void Write(Stream tOut)
{
try
{
var outStream = new BinaryWriter(tOut);
header.NumberOfRecords = v_records.Count;
header.Write(outStream);
/* Now write all the records */
var t_recCount = v_records.Count;
for (var i = 0; i < t_recCount; i++)
{
/* iterate through records */
var t_values = (object[]) v_records[i];
WriteRecord(outStream, t_values);
}
outStream.Write(DBFFieldType.EndOfData);
outStream.Flush();
}
catch (IOException e)
{
throw new DBFException("Error Writing", e);
}
}
public void Close()
{
/* everything is written already. just update the header for record count and the END_OF_DATA mark */
header.NumberOfRecords = recordCount;
if (raf != null)
{
raf.Seek(0, SeekOrigin.Begin);
header.Write(new BinaryWriter(raf));
raf.Seek(0, SeekOrigin.End);
raf.WriteByte(DBFFieldType.EndOfData);
raf.Close();
_dataMemo?.Close();
} else if (!string.IsNullOrEmpty(DataMemoLoc))
{
DataMemo.Close();
}
}
private void WriteRecord(BinaryWriter dataOutput, object[] objectArray)
{
dataOutput.Write((byte) ' ');
for (var j = 0; j < header.FieldArray.Length; j++)
{
/* iterate through fields */
switch (header.FieldArray[j].DataType)
{
case NativeDbType.Char:
if (objectArray[j] != null && objectArray[j] != DBNull.Value)
{
var str_value = objectArray[j].ToString();
dataOutput.Write(
Utils.textPadding(str_value,
CharEncoding,
header.FieldArray[j].
FieldLength
)
);
}
else
{
dataOutput.Write(
Utils.textPadding("",
CharEncoding,
header.FieldArray[j].
FieldLength
)
);
}
break;
case NativeDbType.Date:
if (objectArray[j] != null && objectArray[j] != DBNull.Value)
{
var tDate = (DateTime) objectArray[j];
dataOutput.Write(
CharEncoding.GetBytes(tDate.ToString("yyyyMMdd")));
}
else
{
dataOutput.Write(
Utils.FillArray(new byte[8], DBFFieldType.Space));
}
break;
case NativeDbType.Float:
if (objectArray[j] != null && objectArray[j] != DBNull.Value)
{
var tDouble = Convert.ToDecimal(objectArray[j]);
dataOutput.Write(
Utils.NumericFormating(
tDouble,
CharEncoding,
header.FieldArray[j].FieldLength,
header.FieldArray[j].DecimalCount
)
);
}
else
{
dataOutput.Write(
Utils.textPadding(
NullSymbol,
CharEncoding,
header.FieldArray[j].FieldLength,
Utils.ALIGN_RIGHT
)
);
}
break;
case NativeDbType.Numeric:
if (objectArray[j] != null && objectArray[j] != DBNull.Value)
{
var tDecimal = Convert.ToDecimal(objectArray[j]);
dataOutput.Write(
Utils.NumericFormating(
tDecimal,
CharEncoding,
header.FieldArray[j].FieldLength,
header.FieldArray[j].DecimalCount
)
);
}
else
{
dataOutput.Write(
Utils.textPadding(
NullSymbol,
CharEncoding,
header.FieldArray[j].FieldLength,
Utils.ALIGN_RIGHT
)
);
}
break;
case NativeDbType.Logical:
if (objectArray[j] != null && objectArray[j] != DBNull.Value)
{
if ((bool) objectArray[j])
{
dataOutput.Write(DBFFieldType.True);
}
else
{
dataOutput.Write(DBFFieldType.False);
}
}
else
{
dataOutput.Write(DBFFieldType.UnknownByte);
}
break;
case NativeDbType.Memo:
if (objectArray[j] != null && objectArray[j] != DBNull.Value)
{
var tMemoValue = ((MemoValue) objectArray[j]);
tMemoValue.Write(this);
dataOutput.Write(Utils.NumericFormating(tMemoValue.Block, CharEncoding, 10, 0));
}
else
{
dataOutput.Write(
Utils.textPadding("",
CharEncoding,
10
)
);
}
break;
default:
throw new DBFException("Unknown field type "
+ header.FieldArray[j].DataType);
}
} /* iterating through the fields */
}
}
}