// Copyright 2009-2022 Josh Close
// This file is a part of CsvHelper and is dual licensed under MS-PL and Apache 2.0.
// See LICENSE.txt for details or visit http://www.opensource.org/licenses/ms-pl.html for MS-PL and http://opensource.org/licenses/Apache-2.0 for Apache 2.0.
// https://github.com/JoshClose/CsvHelper
using System;
using System.Data;
using System.Globalization;
using System.Linq;
namespace CsvHelper
{
///
/// Provides a means of reading a CSV file forward-only by using CsvReader.
///
///
public class CsvDataReader : IDataReader
{
private readonly CsvReader csv;
private readonly DataTable schemaTable;
private bool skipNextRead;
///
public object this[int i]
{
get
{
return csv[i] ?? string.Empty;
}
}
///
public object this[string name]
{
get
{
return csv[name] ?? string.Empty;
}
}
///
public int Depth
{
get
{
return 0;
}
}
///
public bool IsClosed { get; private set; }
///
public int RecordsAffected
{
get
{
return 0;
}
}
///
public int FieldCount
{
get
{
return csv?.Parser.Count ?? 0;
}
}
///
/// Initializes a new instance of the class.
///
/// The CSV.
/// The DataTable representing the file schema.
public CsvDataReader(CsvReader csv, DataTable? schemaTable = null)
{
this.csv = csv;
csv.Read();
if (csv.Configuration.HasHeaderRecord)
{
csv.ReadHeader();
}
else
{
skipNextRead = true;
}
this.schemaTable = schemaTable ?? GetSchemaTable();
}
///
public void Close()
{
Dispose();
}
///
public void Dispose()
{
csv.Dispose();
IsClosed = true;
}
///
public bool GetBoolean(int i)
{
return csv.GetField(i);
}
///
public byte GetByte(int i)
{
return csv.GetField(i);
}
///
public long GetBytes(int i, long fieldOffset, byte[]? buffer, int bufferoffset, int length)
{
var bytes = csv.GetField(i);
buffer ??= new byte[bytes.Length];
Array.Copy(bytes, fieldOffset, buffer, bufferoffset, length);
return bytes.Length;
}
///
public char GetChar(int i)
{
return csv.GetField(i);
}
///
public long GetChars(int i, long fieldoffset, char[]? buffer, int bufferoffset, int length)
{
var chars = csv.GetField(i).ToCharArray();
buffer ??= new char[chars.Length];
Array.Copy(chars, fieldoffset, buffer, bufferoffset, length);
return chars.Length;
}
///
public IDataReader GetData(int i)
{
throw new NotSupportedException();
}
///
public string GetDataTypeName(int i)
{
if (i >= schemaTable.Rows.Count)
{
throw new IndexOutOfRangeException($"SchemaTable does not contain a definition for field '{i}'.");
}
var row = schemaTable.Rows[i];
var field = row["DataType"] as Type;
if (field == null)
{
throw new InvalidOperationException($"SchemaTable does not contain a 'DataType' of type 'Type' for field '{i}'.");
}
return field.Name;
}
///
public DateTime GetDateTime(int i)
{
return csv.GetField(i);
}
///
public decimal GetDecimal(int i)
{
return csv.GetField(i);
}
///
public double GetDouble(int i)
{
return csv.GetField(i);
}
///
public Type GetFieldType(int i)
{
return typeof(string);
}
///
public float GetFloat(int i)
{
return csv.GetField(i);
}
///
public Guid GetGuid(int i)
{
return csv.GetField(i);
}
///
public short GetInt16(int i)
{
return csv.GetField(i);
}
///
public int GetInt32(int i)
{
return csv.GetField(i);
}
///
public long GetInt64(int i)
{
return csv.GetField(i);
}
///
public string GetName(int i)
{
return csv.Configuration.HasHeaderRecord
? csv.HeaderRecord[i]
: string.Empty;
}
///
public int GetOrdinal(string name)
{
var index = csv.GetFieldIndex(name, isTryGet: true);
if (index >= 0)
{
return index;
}
var args = new PrepareHeaderForMatchArgs(name, 0);
var namePrepared = csv.Configuration.PrepareHeaderForMatch(args);
var headerRecord = csv.HeaderRecord;
for (var i = 0; i < headerRecord.Length; i++)
{
args = new PrepareHeaderForMatchArgs(headerRecord[i], i);
var headerPrepared = csv.Configuration.PrepareHeaderForMatch(args);
if (csv.Configuration.CultureInfo.CompareInfo.Compare(namePrepared, headerPrepared, CompareOptions.IgnoreCase) == 0)
{
return i;
}
}
throw new IndexOutOfRangeException($"Field with name '{name}' and prepared name '{namePrepared}' was not found.");
}
///
public DataTable GetSchemaTable()
{
if (schemaTable != null)
{
return schemaTable;
}
// https://docs.microsoft.com/en-us/dotnet/api/system.data.datatablereader.getschematable?view=netframework-4.7.2
var dt = new DataTable("SchemaTable");
dt.Columns.Add("AllowDBNull", typeof(bool));
dt.Columns.Add("AutoIncrementSeed", typeof(long));
dt.Columns.Add("AutoIncrementStep", typeof(long));
dt.Columns.Add("BaseCatalogName");
dt.Columns.Add("BaseColumnName");
dt.Columns.Add("BaseColumnNamespace");
dt.Columns.Add("BaseSchemaName");
dt.Columns.Add("BaseTableName");
dt.Columns.Add("BaseTableNamespace");
dt.Columns.Add("ColumnName");
dt.Columns.Add("ColumnMapping", typeof(MappingType));
dt.Columns.Add("ColumnOrdinal", typeof(int));
dt.Columns.Add("ColumnSize", typeof(int));
dt.Columns.Add("DataType", typeof(Type));
dt.Columns.Add("DefaultValue", typeof(object));
dt.Columns.Add("Expression");
dt.Columns.Add("IsAutoIncrement", typeof(bool));
dt.Columns.Add("IsKey", typeof(bool));
dt.Columns.Add("IsLong", typeof(bool));
dt.Columns.Add("IsReadOnly", typeof(bool));
dt.Columns.Add("IsRowVersion", typeof(bool));
dt.Columns.Add("IsUnique", typeof(bool));
dt.Columns.Add("NumericPrecision", typeof(short));
dt.Columns.Add("NumericScale", typeof(short));
dt.Columns.Add("ProviderType", typeof(int));
if (csv.Configuration.HasHeaderRecord)
{
var header = csv.HeaderRecord;
for (var i = 0; i < header.Length; i++)
{
var row = dt.NewRow();
row["AllowDBNull"] = true;
row["AutoIncrementSeed"] = DBNull.Value;
row["AutoIncrementStep"] = DBNull.Value;
row["BaseCatalogName"] = null;
row["BaseColumnName"] = header[i];
row["BaseColumnNamespace"] = null;
row["BaseSchemaName"] = null;
row["BaseTableName"] = null;
row["BaseTableNamespace"] = null;
row["ColumnName"] = header[i];
row["ColumnMapping"] = MappingType.Element;
row["ColumnOrdinal"] = i;
row["ColumnSize"] = int.MaxValue;
row["DataType"] = typeof(string);
row["DefaultValue"] = null;
row["Expression"] = null;
row["IsAutoIncrement"] = false;
row["IsKey"] = false;
row["IsLong"] = false;
row["IsReadOnly"] = true;
row["IsRowVersion"] = false;
row["IsUnique"] = false;
row["NumericPrecision"] = DBNull.Value;
row["NumericScale"] = DBNull.Value;
row["ProviderType"] = DbType.String;
dt.Rows.Add(row);
}
}
return dt;
}
///
public string GetString(int i)
{
return csv.GetField(i) ?? string.Empty;
}
///
public object GetValue(int i)
{
return IsDBNull(i) ? DBNull.Value : (csv.GetField(i) ?? string.Empty);
}
///
public int GetValues(object[] values)
{
for (var i = 0; i < values.Length; i++)
{
values[i] = IsDBNull(i) ? DBNull.Value : (csv.GetField(i) ?? string.Empty);
}
return csv.Parser.Count;
}
///
public bool IsDBNull(int i)
{
var field = csv.GetField(i);
var nullValues = csv.Context.TypeConverterOptionsCache.GetOptions().NullValues;
return field == null || nullValues.Contains(field);
}
///
public bool NextResult()
{
return false;
}
///
public bool Read()
{
if (skipNextRead)
{
skipNextRead = false;
return true;
}
return csv.Read();
}
}
}