using System; using System.Collections.Generic; using System.Dynamic; using System.Linq; using System.Runtime.CompilerServices; using ImpromptuInterface; using Dynamitey; namespace DotNetDBF.Enumerable { /// /// Interface to get the contents of the DBF Wrapper /// public interface IDBFInterceptor { /// /// Does field exist in row /// /// bool Exists(string fieldName); /// /// Gets the data row. /// /// object[] GetDataRow(); } #pragma warning disable 618 public class DBFInterceptor : DBFIntercepter #pragma warning restore 618 { public DBFInterceptor(object[] wrappedObj, string[] fieldNames) : base(wrappedObj, fieldNames) { } } /// /// DBF Dynamic Wrapper /// public abstract class BaseDBFInterceptor : Dynamitey.DynamicObjects.BaseObject, IDBFInterceptor { private readonly string[] _fieldNames; private readonly object[] _wrappedArray; protected BaseDBFInterceptor(object[] wrappedObj, string[] fieldNames) { _wrappedArray = wrappedObj; _fieldNames = fieldNames; } public override IEnumerable GetDynamicMemberNames() { return _fieldNames; } public bool Exists(string fieldName) { return _fieldNames.Contains(fieldName); } public override bool TryGetMember(GetMemberBinder binder, out object result) { result = null; var tLookup = binder.Name; var tIndex = Array.FindIndex(_fieldNames, it => it.Equals(tLookup, StringComparison.InvariantCultureIgnoreCase)); if (tIndex < 0) return false; result = _wrappedArray[tIndex]; if (TryTypeForName(tLookup, out var outType)) { result = Dynamic.CoerceConvert(result, outType); } return true; } public override bool TrySetMember(SetMemberBinder binder, object value) { var tLookup = binder.Name; var tIndex = Array.FindIndex(_fieldNames, it => it.Equals(tLookup, StringComparison.InvariantCultureIgnoreCase)); if (tIndex < 0) return false; if (TryTypeForName(tLookup, out var outType)) { value = Dynamic.CoerceConvert(value, outType); } _wrappedArray[tIndex] = value; return true; } public object[] GetDataRow() { return _wrappedArray; } } /// /// Enumerable API /// public static partial class DBFEnumerable { /// /// New Blank Row Dynamic object that matches writer; /// /// The writer. /// public static dynamic NewBlankRow(this DBFWriter writer) { var fields = writer.Fields.Select(it => it.Name).ToArray(); var obj = new object[fields.Length]; return new Enumerable.DBFInterceptor(obj, fields); } public static void CopyRecordTo(this IDBFInterceptor original, IDBFInterceptor dest) { foreach (var fieldName in Dynamitey.Dynamic.GetMemberNames(dest, true)) { try { var val = Dynamic.InvokeGet(original, fieldName); Dynamic.InvokeSet(dest, fieldName, val); } catch { // ignored } } } /// /// Writes the record. /// /// The writer. /// The value. public static void WriteRecord(this DBFWriter writer, Enumerable.IDBFInterceptor value) { writer.WriteRecord(value.GetDataRow()); } /// /// Adds the record. /// /// The writer. /// The value. public static void AddRecord(this DBFWriter writer, Enumerable.IDBFInterceptor value) { writer.AddRecord(value.GetDataRow()); } /// /// Return all the records. T should be interface with getter properties that match types and names of the database. /// Optionally instead of T being and interface you can pass in an anonymous object with properties that match that /// database and then you'll get an IEnumerable of that anonymous type with the data filled in. /// /// /// The reader. /// The prototype. Anonymous class instance /// Indicates if parsing error throws an exception. Default: true /// public static IEnumerable AllRecords(this DBFReader reader, T prototype = null, bool throwOnParsingError = true) where T : class { var tType = typeof(T); var tProperties = tType.GetProperties() .Where( it => Array.FindIndex(reader.Fields, f => f.Name.Equals(it.Name, StringComparison.InvariantCultureIgnoreCase)) >= 0) .ToList(); var tProps = tProperties .Select( it => Array.FindIndex(reader.Fields, jt => jt.Name.Equals(it.Name, StringComparison.InvariantCultureIgnoreCase))) .Where(it => it >= 0) .ToArray(); var tOrderedProps = tProps.OrderBy(it => it).ToArray(); var tReturn = new List(); if (tType.GetCustomAttributes(typeof(CompilerGeneratedAttribute), false).Any()) { var tAnon = reader.NextRecord(tProps, tOrderedProps, throwOnParsingError); while (tAnon != null) { tReturn.Add((T) Activator.CreateInstance(tType, tAnon)); tAnon = reader.NextRecord(tProps, tOrderedProps, throwOnParsingError); } return tReturn; } var t = reader.NextRecord(tProps, tOrderedProps, throwOnParsingError); while (t != null) { var interceptor = new Enumerable.DBFInterceptor(t, tProperties.Select(it => it.Name).ToArray()); tReturn.Add(interceptor.ActLike(typeof(Enumerable.IDBFInterceptor))); t = reader.NextRecord(tProps, tOrderedProps, throwOnParsingError); } return tReturn; } /// /// Returns a list of dynamic objects whose properties and types match up with that database name. /// /// The reader. /// The where column name. /// What the were column should equal. /// Indicates if parsing error throws an exception. Default: true /// public static IEnumerable DynamicAllRecords(this DBFReader reader, string whereColumn = null, dynamic whereColumnEquals = null, bool throwOnParsingError = true) { var props = reader.GetSelectFields().Select(it => it.Name).ToArray(); int? whereColumnIndex = null; if (!String.IsNullOrEmpty(whereColumn)) { whereColumnIndex = Array.FindIndex(props, it => it.Equals(whereColumn, StringComparison.InvariantCultureIgnoreCase)); } var tReturn = new List(); var t = reader.NextRecord(throwOnParsingError); while (t != null) { if (whereColumnIndex is int i) { dynamic tO = t[i]; if (!tO.Equals(whereColumnEquals)) { t = reader.NextRecord(throwOnParsingError); continue; } } var interceptor = new Enumerable.DBFInterceptor(t, props); tReturn.Add(interceptor); t = reader.NextRecord(throwOnParsingError); } return tReturn; } } }