using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using FastReport.Utils;
using FastReport.Format;
using System.Globalization;
using System.Collections;
using System.Runtime.InteropServices;
namespace FastReport.Export.Dbf
{
///
/// Represents the export to DBF.
///
public partial class DBFExport : ExportBase
{
#region Constants
private const byte Ox00 = 0x00;
private const byte SIMPLE_TABLE = 0x03;
private const char FIELD_TYPE = 'C';
private const byte FILE_HEADER_END = 0x0D;
private const byte RECORD_NOT_REMOVED = 0x20;
private const byte RECORD_REMOVED = 0x2A;
private const byte DBF_FILE_END = 0x1A;
private const int RECORDS_COUNT_LENGTH = 4;
private const int HEADER_NULL_FILL = 20;
private const int FIELD_NULL_FILL = 15;
private const int FILE_HEADER_SIZE = 32;
private const int FIELD_HEADER_SIZE = 32;
private const char SPACE = ' ';
private const char UNDERSCORE = '_';
private const string DEFAULT_FIELD_NAME = "FIELD";
#endregion // Constants
#region Fields
private ExportMatrix matrix;
private Encoding encoding;
private bool dataOnly;
private bool exportTypes;
private string fieldNames;
private List header;
private List records;
private Record record;
private StringBuilder field;
private List maxFieldsSize;
private List fieldNameList;
private List fieldTypes;
#endregion // Fields
#region Properties
///
/// Gets or sets the encoding.
///
public Encoding Encoding
{
get { return encoding; }
set { encoding = value; }
}
///
/// Gets or sets a value that determines whether to export the databand rows only.
///
public bool DataOnly
{
get { return dataOnly; }
set { dataOnly = value; }
}
///
/// Gets or sets a value that determines whether to export the types of fields.
///
public bool ExportTypes
{
get { return exportTypes; }
set { exportTypes = value; }
}
///
/// Gets or sets the list of field names.
///
///
/// The field names must be separated by ";" symbol, for example: Column1;Column2;Column3
///
public string FieldNames
{
get { return fieldNames; }
set { fieldNames = value; }
}
#endregion // Properties
#region Constructors
///
/// Initializes a new instance of the class.
///
public DBFExport()
{
encoding = Encoding.Default;
fieldNames = "";
fieldTypes = new List();
dataOnly = false;
exportTypes = false;
}
#endregion // Constructors
#region Private Methods
private void PrepareRecords()
{
int i, x, y;
ExportIEMObject obj;
for (y = 0; y < matrix.Height - 1; y++)
{
for (x = 0; x < matrix.Width; x++)
{
i = matrix.Cell(x, y);
if (i != -1)
{
obj = matrix.ObjectById(i);
if (obj.Counter == 0)
{
obj.Counter = 1;
if (exportTypes)
AddField(obj);
else
{
record.Add(new StringBuilder(obj.Text), Record.FieldType.String);
}
}
}
}
if (record.Count != 0)
{
records.Add(record);
record = new Record();
}
}
}
private void AddField(ExportIEMObject obj)
{
Record.FieldType type = Record.FieldType.String;
field = new StringBuilder(obj.Text);
if (obj.IsNumeric)
{
if (obj.Style.Format is CurrencyFormat)
{
float number;
CurrencyFormat formatInfo = obj.Style.Format as CurrencyFormat;
// parse to float for save decimal part of a number
float.TryParse(obj.Text, NumberStyles.Currency, formatInfo.GetNumberFormatInfo(), out number);
type = Record.FieldType.Currency;
field = new StringBuilder((number * 10000).ToString()); // max number accuracy is 4
}
else if (obj.Style.Format is NumberFormat && (obj.Style.Format as NumberFormat).DecimalDigits == 0)
{
type = Record.FieldType.Integer;
}
else
type = Record.FieldType.Decimal;
}
else if (obj.IsDateTime)
{
type = Record.FieldType.DataTime;
DateTime dt;
if (DateTime.TryParse(obj.Text, out dt))
field = new StringBuilder(dt.ToString("yyyyMMddHHmmss"));
}
else if (obj.Style.Format is BooleanFormat)
{
BooleanFormat format = obj.Style.Format as BooleanFormat;
field = new StringBuilder(obj.Text == format.TrueText ? "T" : "F");
type = Record.FieldType.Boolean;
}
record.Add(field, type);
}
private int GetRecordSize()
{
int maxSize = 0;
foreach (int m in maxFieldsSize)
{
maxSize += m;
}
return ++maxSize;
}
private int GetMaxRecordCount()
{
int maxCount = 0;
foreach (Record rec in records)
{
if (rec.Count > maxCount)
{
maxCount = rec.Count;
fieldTypes.Clear();
for (int i = 0; i < rec.Count; i++)
fieldTypes.Add(rec[i].Type);
}
}
return maxCount;
}
private List GetMaxFieldsSize()
{
List maxFieldsSize = new List();
int maxCount = GetMaxRecordCount();
for (int i = 0; i < maxCount; i++)
{
int maxFieldSize = 0;
foreach (Record rec in records)
{
if (rec.Count < i + 1)
{
continue;
}
if (rec[i].Data.Length > maxFieldSize)
{
if (rec[i].Type == Record.FieldType.DataTime)
maxFieldSize = 14;
else if (rec[i].Type == Record.FieldType.Integer)
maxFieldSize = 4; // bytes
else if (rec[i].Type == Record.FieldType.Currency)
maxFieldSize = 8; // bytes
else
maxFieldSize = rec[i].Data.Length;
}
}
maxFieldsSize.Add(maxFieldSize);
}
return maxFieldsSize;
}
private int GetHeaderSize()
{
return (FILE_HEADER_SIZE + FIELD_HEADER_SIZE * maxFieldsSize.Count + 1);
}
private void CompleteRecordsEmptyFields()
{
maxFieldsSize = GetMaxFieldsSize();
int maxCount = maxFieldsSize.Count;
StringBuilder emptyField = new StringBuilder();
foreach (Record rec in records)
{
for (int i = 0; i < maxCount; i++)
{
if (i < rec.Count)
{
if (rec[i].Data.Length < maxFieldsSize[i])
{
rec[i].Data.Append(SPACE, maxFieldsSize[i] - rec[i].Data.Length);
}
}
else
{
emptyField.Append(SPACE, maxFieldsSize[i]);
rec.Add(emptyField, Record.FieldType.String);
emptyField = new StringBuilder();
}
}
}
}
private void CompleteByteListNulls(List list, int n)
{
for (int i = 0; i < n; i++)
{
list.Add(Ox00);
}
}
private void PrepareFileHeader()
{
header.Clear();
header.Add(SIMPLE_TABLE);
DateTime date = SystemFake.DateTime.Now;
int year = date.Year % 100;
header.Add((byte)year);
header.Add((byte)date.Month);
header.Add((byte)date.Day);
byte[] bytes = BitConverter.GetBytes(records.Count);
if (!BitConverter.IsLittleEndian)
{
Array.Reverse(bytes);
}
header.AddRange(bytes);
bytes = BitConverter.GetBytes((short)GetHeaderSize());
if (!BitConverter.IsLittleEndian)
{
Array.Reverse(bytes);
}
header.AddRange(bytes);
bytes = BitConverter.GetBytes((short)(GetRecordSize()));
if (!BitConverter.IsLittleEndian)
{
Array.Reverse(bytes);
}
header.AddRange(bytes);
CompleteByteListNulls(header, HEADER_NULL_FILL);
}
private void PrepareFieldNames()
{
fieldNameList.Clear();
string[] fieldNamesArr = FieldNames.Split(';');
for (int i = 0; i < maxFieldsSize.Count; i++)
{
string name = "";
if (i >= fieldNamesArr.Length || String.IsNullOrEmpty(fieldNamesArr[i]))
{
name = DEFAULT_FIELD_NAME + i.ToString();
}
else
{
name = fieldNamesArr[i];
name = name.Replace(SPACE, UNDERSCORE);
if (name.Length > Record.MAX_FIELD_NAME_CHARS)
name = name.Remove(Record.MAX_FIELD_NAME_CHARS);
}
fieldNameList.Add(name);
}
}
private void PrepareFieldHeaders()
{
for (int i = 0; i < maxFieldsSize.Count; i++)
{
string fieldName = fieldNameList[i];
byte[] bytes = encoding.GetBytes(fieldName);
header.AddRange(bytes);
CompleteByteListNulls(header, Record.MAX_FIELD_NAME_CHARS - fieldName.Length + 1);
header.Add((byte)fieldTypes[i]);
CompleteByteListNulls(header, 4);
header.Add((byte)maxFieldsSize[i]);
CompleteByteListNulls(header, FIELD_NULL_FILL);
}
header.Add(FILE_HEADER_END);
}
private void WriteHeader(Stream stream)
{
byte[] bytes = header.ToArray();
stream.Write(bytes, 0, header.Count);
}
private void WriteRecords(Stream stream)
{
List builder = new List();
foreach (Record rec in records)
{
for (int i = 0; i < rec.Count; i++)
{
switch (fieldTypes[i])
{
case Record.FieldType.Integer:
{
int number;
int.TryParse(rec[i].Data.ToString(), out number);
byte b0 = (byte)number;
byte b1 = (byte)(number >> 8);
byte b2 = (byte)(number >> 16);
byte b3 = (byte)(number >> 24);
builder.Add(b0);
builder.Add(b1);
builder.Add(b2);
builder.Add(b3);
break;
}
case Record.FieldType.Currency:
{
long number;
long.TryParse(rec[i].Data.ToString(), NumberStyles.Float, NumberFormatInfo.CurrentInfo, out number);
byte b0 = (byte)number;
byte b1 = (byte)(number >> 8);
byte b2 = (byte)(number >> 16);
byte b3 = (byte)(number >> 24);
byte b4 = (byte)(number >> 32);
byte b5 = (byte)(number >> 40);
byte b6 = (byte)(number >> 48);
byte b7 = (byte)(number >> 56);
builder.Add(b0);
builder.Add(b1);
builder.Add(b2);
builder.Add(b3);
builder.Add(b4);
builder.Add(b5);
builder.Add(b6);
builder.Add(b7);
break;
}
default:
builder.AddRange(encoding.GetBytes(rec[i].Data.ToString()));
break;
}
}
stream.WriteByte(RECORD_NOT_REMOVED);
stream.Write(builder.ToArray(), 0, builder.ToArray().Length);
builder.Clear();
}
}
private void Write(Stream stream)
{
PrepareRecords();
CompleteRecordsEmptyFields();
PrepareFileHeader();
PrepareFieldNames();
PrepareFieldHeaders();
WriteHeader(Stream);
WriteRecords(Stream);
Stream.WriteByte(DBF_FILE_END);
}
#endregion // Private Methods
#region Protected Methods
///
protected override void Start()
{
base.Start();
header = new List();
field = new StringBuilder("");
record = new Record();
records = new List();
maxFieldsSize = new List();
fieldNameList = new List();
matrix = new ExportMatrix();
matrix.Inaccuracy = 0.5f;
matrix.PlainRich = true;
matrix.AreaFill = false;
matrix.CropAreaFill = true;
matrix.Report = Report;
matrix.Images = false;
matrix.WrapText = false;
matrix.DataOnly = dataOnly;
matrix.ShowProgress = ShowProgress;
}
///
protected override void ExportPageBegin(ReportPage page)
{
base.ExportPageBegin(page);
matrix.AddPageBegin(page);
}
///
protected override void ExportBand(BandBase band)
{
base.ExportBand(band);
matrix.AddBand(band, this);
}
///
protected override void ExportPageEnd(ReportPage page)
{
matrix.AddPageEnd(page);
}
///
protected override void Finish()
{
matrix.Prepare();
Write(Stream);
}
///
protected override string GetFileFilter()
{
return new MyRes("FileFilters").Get("DbfFile");
}
#endregion // Protected Methods
#region Public Methods
///
public override void Serialize(FRWriter writer)
{
base.Serialize(writer);
writer.WriteStr("FieldNames", FieldNames);
writer.WriteBool("DataOnly", DataOnly);
writer.WriteBool("ExportTypes", ExportTypes);
}
#endregion // Public Methods
}
}