using System;
using System.Collections;
using System.IO;
using System.Text;
using GisSharpBlog.NetTopologySuite.Utilities;
namespace GisSharpBlog.NetTopologySuite.IO
{
///
/// This class aids in the writing of Dbase IV files.
///
///
/// Attribute information of an ESRI Shapefile is written using Dbase IV files.
///
public class DbaseFileWriter
{
private readonly BinaryWriter _writer;
private bool headerWritten;
private bool recordsWritten;
private DbaseFileHeader _header;
///
/// Initializes a new instance of the DbaseFileWriter class.
///
///
public DbaseFileWriter(string filename)
{
if (filename == null)
{
throw new ArgumentNullException("filename");
}
FileStream filestream = new FileStream(filename, FileMode.Create, FileAccess.Write, FileShare.Write);
_writer = new BinaryWriter(filestream);
}
///
///
///
///
public void Write(DbaseFileHeader header)
{
if (header == null)
{
throw new ArgumentNullException("header");
}
if (recordsWritten)
{
throw new InvalidOperationException("Records have already been written. Header file needs to be written first.");
}
headerWritten = true;
header.WriteHeader(_writer);
_header = header;
}
///
///
///
///
public void Write(IList columnValues)
{
if (columnValues == null)
{
throw new ArgumentNullException("columnValues");
}
if (!headerWritten)
{
throw new InvalidOperationException("Header records need to be written first.");
}
int i = 0;
_writer.Write((byte) 0x20); // the deleted flag
foreach (object columnValue in columnValues)
{
DbaseFieldDescriptor headerField = _header.Fields[i];
if (columnValue == null)
{
// Don't corrupt the file by not writing if the value is null.
// Instead, treat it like an empty string.
Write(string.Empty, headerField.Length);
}
else if (headerField.Type == typeof(string))
{
// If the column is a character column, the values in that
// column should be treated as text, even if the column value
// is not a string.
Write(columnValue.ToString(), headerField.Length);
}
else if (IsRealType(columnValue.GetType()))
{
Write(Convert.ToDecimal(columnValue), headerField.Length, headerField.DecimalCount);
}
else if (IsIntegerType(columnValue.GetType()))
{
Write(Convert.ToDecimal(columnValue), headerField.Length, headerField.DecimalCount);
}
else if (columnValue is Decimal)
{
Write((decimal) columnValue, headerField.Length, headerField.DecimalCount);
}
else if (columnValue is Boolean)
{
Write((bool) columnValue);
}
else if (columnValue is string)
{
Write((string) columnValue, headerField.Length);
}
else if (columnValue is DateTime)
{
Write((DateTime) columnValue);
}
else if (columnValue is Char)
{
Write((Char) columnValue, headerField.Length);
}
i++;
}
}
///
/// Write a decimal value to the file.
///
/// The value to write.
/// The overall width of the column being written to.
/// The number of decimal places in the column.
public void Write(decimal number, int length, int decimalCount)
{
string outString;
int wholeLength = length;
if (decimalCount > 0)
{
wholeLength -= (decimalCount + 1);
}
// Force to use point as decimal separator
string strNum = Convert.ToString(number, Global.GetNfi());
int decimalIndex = strNum.IndexOf('.');
if (decimalIndex < 0)
{
decimalIndex = strNum.Length;
}
if (decimalIndex > wholeLength)
{
// Too many digits to the left of the decimal. Use the left
// most "wholeLength" number of digits. All values to the right
// of the decimal will be '0'.
StringBuilder sb = new StringBuilder();
sb.Append(strNum.Substring(0, wholeLength));
if (decimalCount > 0)
{
sb.Append('.');
for (int i = 0; i < decimalCount; ++i)
{
sb.Append('0');
}
}
outString = sb.ToString();
}
else
{
// Chop extra digits to the right of the decimal.
StringBuilder sb = new StringBuilder();
sb.Append("{0:0");
if (decimalCount > 0)
{
sb.Append('.');
for (int i = 0; i < decimalCount; ++i)
{
sb.Append('0');
}
}
sb.Append('}');
// Force to use point as decimal separator
outString = String.Format(Global.GetNfi(), sb.ToString(), number);
}
for (int i = 0; i < length - outString.Length; i++)
{
_writer.Write((byte) 0x20);
}
foreach (char c in outString)
{
_writer.Write(c);
}
}
///
///
///
///
///
///
public void Write(double number, int length, int decimalCount)
{
Write(Convert.ToDecimal(number), length, decimalCount);
}
///
///
///
///
///
///
public void Write(float number, int length, int decimalCount)
{
Write(Convert.ToDecimal(number), length, decimalCount);
}
///
///
///
///
///
public void Write(string text, int length)
{
// ensure string is not too big
text = text.PadRight(length, ' ');
string dbaseString = text.Substring(0, length);
// will extra chars get written??
foreach (char c in dbaseString)
{
_writer.Write(c);
}
int extraPadding = length - dbaseString.Length;
for (int i = 0; i < extraPadding; i++)
{
_writer.Write((byte) 0x20);
}
}
///
///
///
///
public void Write(DateTime date)
{
foreach (char c in date.Year.ToString())
{
_writer.Write(c);
}
if (date.Month < 10)
{
_writer.Write('0');
}
foreach (char c in date.Month.ToString())
{
_writer.Write(c);
}
if (date.Day < 10)
{
_writer.Write('0');
}
foreach (char c in date.Day.ToString())
{
_writer.Write(c);
}
}
///
///
///
///
public void Write(bool flag)
{
_writer.Write(flag ? 'T' : 'F');
}
///
/// Write a character to the file.
///
/// The character to write.
/// The length of the column to write in. Writes
/// left justified, filling with spaces.
public void Write(char c, int length)
{
string str = string.Empty;
str += c;
Write(str, length);
}
///
/// Write a byte to the file.
///
/// The byte.
public void Write(byte number)
{
_writer.Write(number);
}
///
///
///
public void Close()
{
_writer.Close();
}
///
/// Determine if the type provided is a "real" or "float" type.
///
///
///
private static bool IsRealType(Type type)
{
return ((type == typeof(Double)) || (type == typeof(Single)));
}
///
/// Determine if the type provided is a "whole" number type.
///
///
///
private static bool IsIntegerType(Type type)
{
return ((type == typeof(Int16)) || (type == typeof(Int32)) || (type == typeof(Int64)) ||
(type == typeof(UInt16)) || (type == typeof(UInt32)) || (type == typeof(UInt64)));
}
}
}