using System; using System.Collections; using System.Collections.Generic; using System.Data; using System.Linq; using System.Linq.Expressions; using System.Net.Http.Headers; using System.Reflection; namespace InABox.Core { [Serializable] public class CoreTable : ICoreTable, ISerializeBinary //: IEnumerable, INotifyCollectionChanged { #region Fields private List? rows; private List columns = new List(); private string tableName; #endregion #region Properties public string TableName { get => tableName; } public int TotalRecords { get; set; } public int Offset { get; set; } = 0; public IList Columns { get => columns; } public IList Rows { get { rows ??= new List(); //this.rows.CollectionChanged += OnRowsCollectionChanged; return rows; } } [field: NonSerialized] public IList?> Getters { get; } = new List?>(); [field: NonSerialized] public Dictionary?>> Setters { get; } = new Dictionary?>>(); #endregion public CoreTable() : this("") { } public CoreTable(string tableName): base() { this.tableName = tableName; } public CoreTable(Type type) : this() { LoadColumns(type); } public void AddColumn(Expression> column) { Columns.Add( new CoreColumn() { ColumnName = CoreUtils.GetFullPropertyName(column, "."), DataType = column.ReturnType } ); } public CoreRow NewRow(bool populate = false) { var result = new CoreRow(this); if (populate) foreach (var column in Columns) result[column.ColumnName] = column.DataType.GetDefault(); return result; } public void LoadColumns(Type T) { var iprops = DatabaseSchema.Properties(T); foreach (var iprop in iprops) Columns.Add(new CoreColumn { ColumnName = iprop.Name, DataType = iprop.PropertyType }); } public void LoadColumns(IColumns columns) { foreach (var col in columns) Columns.Add(new CoreColumn { ColumnName = col.Property, DataType = col.Type }); } public void LoadColumns(IEnumerable columns) { Columns.Clear(); foreach (var col in columns) Columns.Add(new CoreColumn() { ColumnName = col.ColumnName, DataType = col.DataType }); } /// /// Add the rows from the given to this table. /// /// /// This method assumes that the columns of this table and are the same; it is intended for paging functionality. /// /// public void AddPage(CoreTable table) { foreach(var row in table.Rows) { var newRow = NewRow(populate: false); newRow.LoadValues(row.Values); Rows.Add(newRow); } } public void LoadRows(IEnumerable objects) { foreach (var obj in objects) { LoadRow(obj); } } public void LoadRows(IEnumerable rows) { foreach (var row in rows) { var newrow = NewRow(); FillRow(newrow, row); Rows.Add(newrow); } } public void LoadFrom(CoreTable table, CoreFieldMap mappings, Action? customization = null) { foreach (var row in table.Rows) { var newrow = NewRow(); foreach (var map in mappings.Fields) newrow.Set(map.To, row.Get(map.From)); customization?.Invoke(newrow); Rows.Add(newrow); } } public void FillRow(CoreRow row, object obj) { var bFirst = Getters.Count == 0; for(var i = 0; i < Columns.Count; ++i) { try { if (bFirst) { var prop = DatabaseSchema.Property(obj.GetType(), Columns[i].ColumnName); Getters.Add(prop?.Getter()); } var getter = Getters[i]; if (getter != null) row.Set(i, getter(obj)); else row.Set(i, CoreUtils.GetPropertyValue(obj, Columns[i].ColumnName)); } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } } } public void FillRow(CoreRow row, CoreRow from) { foreach (var col in Columns) try { if (from.Table.Columns.Any(x => x.ColumnName.Equals(col.ColumnName))) row[col.ColumnName] = from[col.ColumnName]; } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } } public void Filter(Func predicate) { for (var i = Rows.Count - 1; i > -1; i--) { var row = Rows[i]; var predresult = predicate.Invoke(row); if (!predresult) Rows.Remove(row); } } #region ToDictionary public IDictionary ToDictionary(string keycol, string displaycol, string sortcol = "") { var kc = Columns.FirstOrDefault(x => x.ColumnName.Equals(keycol)); var dc = Columns.FirstOrDefault(x => x.ColumnName.Equals(displaycol)); var dt = typeof(Dictionary<,>).MakeGenericType(kc.DataType, dc.DataType); var id = (Activator.CreateInstance(dt) as IDictionary)!; var sorted = string.IsNullOrWhiteSpace(sortcol) ? Rows : Rows.OrderBy(x => x.Get(sortcol)).ToList(); foreach (var row in sorted) id[row[keycol]] = row[displaycol]; return id; } public Dictionary ToDictionary(Expression> key, Expression> value, Expression>? sort = null) { var result = new Dictionary(); var sorted = sort == null ? Rows : Rows.OrderBy(x => x.Get(sort)).ToList(); foreach (var row in Rows) result[row.Get(key)] = row.Get(value); return result; } public Dictionary ToDictionary(Expression> key, Expression>[] values, Expression>? sort = null) { var result = new Dictionary(); var sorted = sort == null ? Rows : Rows.OrderBy(x => x.Get(sort)).ToList(); foreach (var row in Rows) { var display = new List(); foreach (var value in values) display.Add(row.Get(value)); result[row.Get(key)] = string.Join(" : ", display); } return result; } public Dictionary ToDictionary(Expression> key, Func value, Expression>? sort = null) { var result = new Dictionary(); var sorted = sort == null ? Rows : Rows.OrderBy(x => x.Get(sort)).ToList(); foreach (var row in Rows) result[row.Get(key)] = value(row); return result; } public void LoadDictionary(Dictionary dictionary, Expression> key, Expression> value) { foreach (var row in Rows) dictionary[row.Get(key)] = row.Get(value); } public Dictionary IntoDictionary(Dictionary result, Expression> key, params Expression>[] values) { foreach (var row in Rows) { var display = new List(); foreach (var value in values) display.Add(row.Get(value)); result[row.Get(key)] = string.Join(" : ", display); } return result; } public Dictionary IntoDictionary(Dictionary result, Expression> key, Func value) { foreach (var row in Rows) result[row.Get(key)] = value(row); return result; } #endregion public DataTable ToDataTable(string name = "", IColumns? additionalColumns = null) { var result = new DataTable(name); foreach (var column in Columns) result.Columns.Add(column.ColumnName.Replace('.', '_'), column.DataType); if(additionalColumns != null) { foreach (var column in additionalColumns) result.Columns.Add(column.Property.Replace('.', '_'), column.Type); } //result.Columns["ID"].Unique = true; //result.PrimaryKey = new DataColumn[] { result.Columns["ID"] }; foreach (var row in Rows) { //result.Rows.Add(row.Values.ToArray()); var newrow = result.NewRow(); newrow.ItemArray = row.Values.ToArray(); result.Rows.Add(newrow); } return result; } #region ToObjects public IEnumerable ToObjects(Type T) => Rows.Select(x => x.ToObject(T)); public IEnumerable ToObjects() where T : BaseObject, new() => Rows.Select(x => x.ToObject()); public List ToList() where T : BaseObject, new() { var list = new List(Rows.Count); for(int i = 0; i < Rows.Count; ++i) { list.Add(Rows[i].ToObject()); } return list; } public T[] ToArray() where T : BaseObject, new() { var arr = new T[Rows.Count]; for(int i = 0; i < Rows.Count; ++i) { arr[i] = Rows[i].ToObject(); } return arr; } #endregion #region ToTuples public IEnumerable> ToTuples( Expression> item1, Expression> item2 ) { return Rows.Select(row => new Tuple ( row.Get(item1), row.Get(item2) ) ); } public IEnumerable> ToTuples( Expression> item1, Expression> item2, Expression> item3) { return Rows.Select(row => new Tuple ( row.Get(item1), row.Get(item2), row.Get(item3) ) ); } public IEnumerable> ToTuples( Expression> item1, Expression> item2, Expression> item3, Expression> item4 ) { return Rows.Select(row => new Tuple ( row.Get(item1), row.Get(item2), row.Get(item3), row.Get(item4) ) ); } public IEnumerable> ToTuples( Expression> item1, Expression> item2, Expression> item3, Expression> item4, Expression> item5 ) { return Rows.Select(row => new Tuple ( row.Get(item1), row.Get(item2), row.Get(item3), row.Get(item4), row.Get(item5) ) ); } #endregion public void CopyTo(DataTable table) { var columns = new List(); foreach (var column in Columns) columns.Add(column.ColumnName.Replace('.', '_')); foreach (var row in Rows) { var newrow = table.NewRow(); for (var i = 0; i < columns.Count; i++) newrow[columns[i]] = row.Values[i]; table.Rows.Add(newrow); } } public void CopyTo(CoreTable table) { var columns = new List(); //foreach (var column in Columns) // columns.Add(column.ColumnName.Replace('.', '_')); foreach (var row in Rows) { var newrow = table.NewRow(); foreach (var column in Columns) if (table.Columns.Any(x => x.ColumnName.Equals(column.ColumnName))) newrow[column.ColumnName] = row[column.ColumnName]; //for (int i = 0; i < columns.Count; i++) // newrow.Set(columns[i], row.Values[i]); table.Rows.Add(newrow); } } #region Extract Values public TValue[] ExtractValues(Expression> column, bool distinct = true) { var columnIdx = GetColumnIndex(column); if (distinct) { return Rows.Select(r => r.Get(columnIdx)).Distinct().ToArray(); } else { return Rows.ToArray(r => r.Get(columnIdx)); } } public TValue[] ExtractValues(string column, bool distinct = true) { var columnIdx = GetColumnIndex(column); if (distinct) { return Rows.Select(r => r.Get(columnIdx)).Distinct().ToArray(); } else { return Rows.ToArray(r => r.Get(columnIdx)); } } #endregion /// /// Create a new row with data from , and adds it to the table. /// /// /// public CoreTable LoadRow(object obj) { var row = NewRow(); FillRow(row, obj); Rows.Add(row); return this; } public int GetColumnIndex(Expression> column) => GetColumnIndex(CoreUtils.GetFullPropertyName(column, ".")); public int GetColumnIndex(Expression> column) => GetColumnIndex(CoreUtils.GetFullPropertyName(column, ".")); public int GetColumnIndex(string columnname) { for (var i = 0; i < Columns.Count; i++) if (Columns[i].ColumnName.Equals(columnname)) { return i; } return -1; } #region ToLookup public IEnumerable> ToGroups(Expression> key, Expression> value) { return Rows.GroupBy(r => r.Get(key), r => r.Get(value)); } public IMutableLookup ToLookup(Expression> key, Expression> value, Expression>? sort = null) { IMutableLookup result = new MutableLookup( Rows.ToLookup( r => r.Get(key), r => r.Get(value) ) ); return result; } public IMutableLookup ToLookup(Expression> key, Func value, Expression>? sort = null) { IMutableLookup result = new MutableLookup( Rows.ToLookup( r => r.Get(key), r => value(r) ) ); return result; } #endregion #region Serialize Binary public void WriteBinary(CoreBinaryWriter writer, bool includeColumns) { writer.Write(TableName); if (includeColumns) { foreach (var column in Columns) { writer.Write(true); writer.Write(column.ColumnName); writer.Write(column.DataType.EntityName()); } writer.Write(false); } writer.Write(Rows.Count); foreach (var row in Rows) { foreach (var col in Columns) { var val = row[col.ColumnName]; writer.WriteBinaryValue(col.DataType, val); } } } public void SerializeBinary(CoreBinaryWriter writer) => WriteBinary(writer, true); public void ReadBinary(CoreBinaryReader reader, IList? columns) { tableName = reader.ReadString(); Columns.Clear(); if (columns is null) { while (reader.ReadBoolean()) { var columnName = reader.ReadString(); var dataType = CoreUtils.GetEntity(reader.ReadString()); Columns.Add(new CoreColumn(dataType, columnName)); } } else { foreach (var column in columns) { Columns.Add(column); } } Rows.Clear(); var nRows = reader.ReadInt32(); for (int i = 0; i < nRows; ++i) { var row = NewRow(); foreach (var column in Columns) { var value = reader.ReadBinaryValue(column.DataType); row.Values.Add(value); } Rows.Add(row); } } public void DeserializeBinary(CoreBinaryReader reader) => ReadBinary(reader, null); #endregion } }