using System; using System.Collections; using System.Collections.Generic; using System.Data; using System.Linq; using System.Linq.Expressions; using System.Reflection; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace InABox.Core { // Equivalent to DataColumn [Serializable] public class CoreColumn { public CoreColumn() { DataType = typeof(object); } public Type DataType { get; set; } public string ColumnName { get; set; } public override string ToString() { return string.Format("{0} ({1})", ColumnName, DataType.EntityName().Split('.').Last()); } } [Serializable] public class CoreRow : ICoreRow { private static Dictionary _accessedcolumns = new Dictionary(); private Dictionary _columnindexes = new Dictionary(); protected internal CoreRow(CoreTable owner) { Table = owner; Values = new List(); } public static CoreRow[] None { get { return new CoreRow[] { }; } } [DoNotSerialize] public CoreTable Table { get; private set; } //private DynamicObject rowObject; public List Values { get; private set; } public Dictionary ToDictionary(string[] exclude) { var result = new Dictionary(); foreach (var column in Table.Columns.Where(x => !exclude.Contains(x.ColumnName))) result[column.ColumnName] = this[column.ColumnName]; return result; } [DoNotSerialize] public int Index => Table.Rows.IndexOf(this); [DoNotSerialize] public object? this[string columnName] { get => //return this.RowObject.GetValue(columnName); Get(columnName); set => //this.RowObject.SetValue(columnName, value); Set(columnName, value); } public object ToObject(Type t) { var entity = (Activator.CreateInstance(t) as BaseObject)!; entity.SetObserving(false); if (!Table.Setters.TryGetValue("", out var setters)) { setters = new List?>(); Table.Setters[""] = setters; } var bFirst = !setters.Any(); for (var i = 0; i < Table.Columns.Count; i++) { var column = Table.Columns[i].ColumnName; var value = this[column]; try { if (bFirst) { var prop = DatabaseSchema.Property(t, column); setters.Add(prop?.Setter()); } var setter = setters[i]; if (setter != null && value != null) setter.Invoke(entity, value); else CoreUtils.SetPropertyValue(entity, column, value); } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } } entity.CommitChanges(); entity.SetObserving(true); return entity; } public T ToObject() where T : BaseObject, new() { return (ToObject(typeof(T)) as T)!; } public T Get(string columnname, bool usedefault = true) { var col = GetColumn(columnname); if (col < 0 || col >= Values.Count) { if (usedefault) return CoreUtils.GetDefault(); throw new Exception(string.Format("Column [{0}] does not exist!", columnname)); } return Values[col] != null ? (T)CoreUtils.ChangeType(Values[col], typeof(T)) : CoreUtils.GetDefault(); } public TType Get(Expression> expression) { var colname = GetColName(expression); //String colname = CoreUtils.GetFullPropertyName(expression, "."); return Get(colname, false); } public void Set(Expression> expression, TType value) { var colname = GetColName(expression); //String colname = CoreUtils.GetFullPropertyName(expression, "."); Set(colname, value); } public void Set(string columnname, T value) { var col = GetColumn(columnname); if (col < 0) throw new Exception("Column not found: " + columnname); while (Values.Count <= col) Values.Add(Table.Columns[Values.Count].DataType.GetDefault()); Values[col] = value; //this.RowObject.SetValue(columnname, value); } public void LoadValues(IEnumerable values) { Values = values.ToList(); } public T ToObject(Expression> property) where TLink : IEntityLink where T : BaseObject, new() { var entity = new T(); entity.SetObserving(false); var prefix = CoreUtils.GetFullPropertyName(property, "."); if (!Table.Setters.TryGetValue(prefix, out var setters)) { setters = new List?>(); Table.Setters[prefix] = setters; } var bFirst = !setters.Any(); var cols = Table.Columns.Where(x => x.ColumnName.StartsWith(prefix + ".")).ToArray(); for (var i = 0; i < cols.Length; i++) { var column = cols[i].ColumnName; var prop = column.Substring((prefix + ".").Length); var value = this[column]; try { if (bFirst) { var p2 = DatabaseSchema.Property(typeof(T), prop); setters.Add(p2?.Setter()); } var setter = setters[i]; if (setter != null && value != null) setter.Invoke(entity, value); else CoreUtils.SetPropertyValue(entity, prop, value); } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } } entity.CommitChanges(); entity.SetObserving(true); return entity; } private string GetColName(Expression> expression) { //int hash = expression.GetHashCode(); //if (_accessedcolumns.ContainsKey(hash)) // return _accessedcolumns[hash]; var colname = CoreUtils.GetFullPropertyName(expression, "."); //_accessedcolumns[hash] = colname; return colname; } private int GetColumn(string columnname) { if (_columnindexes.ContainsKey(columnname)) return _columnindexes[columnname]; for (var i = 0; i < Table.Columns.Count; i++) if (Table.Columns[i].ColumnName.Equals(columnname)) { _columnindexes[columnname] = i; return i; } _columnindexes[columnname] = -1; return -1; } } public class CoreFieldMap { private List> _fields = new List>(); public CoreFieldMapPair[] Fields => _fields.ToArray(); public CoreFieldMap Add(Expression> from, Expression> to) { _fields.Add(new CoreFieldMapPair(from, to)); return this; } } public class CoreFieldMapPair { public Expression> From { get; private set; } public Expression> To { get; private set; } public CoreFieldMapPair(Expression> from, Expression> to) { From = from; To = to; } } [Serializable] public class CoreTable : ICoreTable //: IEnumerable, INotifyCollectionChanged { public CoreTable() : base() { TableName = ""; } public CoreTable(Type type) : this() { LoadColumns(type); } private IList? rows; public string TableName { get; set; } public IList Columns { get; } = new List(); public void AddColumn(Expression> column) { Columns.Add( new CoreColumn() { ColumnName = CoreUtils.GetFullPropertyName(column, "."), DataType = column.ReturnType } ); } public Dictionary?>> Setters { get; } = new Dictionary?>>(); public IList Rows { get { rows ??= new List(); //this.rows.CollectionChanged += OnRowsCollectionChanged; return rows; } } 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; } /* private void OnRowsCollectionChanged(object sender, NotifyCollectionChangedEventArgs e) { switch (e.Action) { case NotifyCollectionChangedAction.Add: this.InternalView.Insert(e.NewStartingIndex, ((DataRow)e.NewItems[0]).RowObject); break; case NotifyCollectionChangedAction.Remove: this.InternalView.RemoveAt(e.OldStartingIndex); break; case NotifyCollectionChangedAction.Replace: this.InternalView.Remove(((DataRow)e.OldItems[0]).RowObject); this.InternalView.Insert(e.NewStartingIndex, ((DataRow)e.NewItems[0]).RowObject); break; case NotifyCollectionChangedAction.Reset: default: this.InternalView.Clear(); this.Rows.Select(r => r.RowObject).ToList().ForEach(o => this.InternalView.Add(o)); break; } } private IList InternalView { get { if (this.internalView == null) { this.CreateInternalView(); } return this.internalView; } } private void CreateInternalView() { this.internalView = (IList)Activator.CreateInstance(typeof(ObservableCollection<>).MakeGenericType(this.ElementType)); ((INotifyCollectionChanged)internalView).CollectionChanged += (s, e) => { this.OnCollectionChanged(e); }; } internal Type ElementType { get { if (this.elementType == null) { this.InitializeElementType(); } return this.elementType; } } private void InitializeElementType() { this.Seal(); this.elementType = DynamicObjectBuilder.GetDynamicObjectBuilderType(this.Columns); } private void Seal() { this.columns = new ReadOnlyCollection(this.Columns); } public IEnumerator GetEnumerator() { return this.InternalView.GetEnumerator(); } public IList ToList() { return this.InternalView; } protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e) { var handler = this.CollectionChanged; if (handler != null) { handler(this, e); } } */ 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) { var types = columns.AsDictionary(); foreach (var col in types.Keys) Columns.Add(new CoreColumn { ColumnName = col, DataType = types[col] }); } public void LoadColumns(IEnumerable columns) { Columns.Clear(); foreach (var col in columns) Columns.Add(new CoreColumn() { ColumnName = col.ColumnName, DataType = col.DataType }); } public void LoadRows(IEnumerable objects) { foreach (var obj in objects) { var row = NewRow(); LoadRow(row, obj); Rows.Add(row); } } public void LoadRows(CoreRow[] rows) { foreach (var row in rows) { var newrow = NewRow(); LoadRow(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 LoadRow(CoreRow row, object obj) { foreach (var col in Columns) try { //var prop = DataModel.Property(obj.GetType(), col.ColumnName); //if (prop is CustomProperty) // prop is CustomProperty ? item.UserProperties[key] : CoreUtils.GetPropertyValue(item, key); var fieldvalue = CoreUtils.GetPropertyValue(obj, col.ColumnName); row[col.ColumnName] = fieldvalue; } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } } public void LoadRow(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); } } 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 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, type) in additionalColumns.AsDictionary()) result.Columns.Add(column.Replace('.', '_'), 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; } public IEnumerable ToObjects() where T : BaseObject, new() => Rows.Select(x => x.ToObject()); public List ToList() where T : BaseObject, new() => ToObjects().ToList(); 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); } } public IEnumerable ExtractValues(Expression> column, bool distinct = true) { var result = Rows.Select(r => r.Get(column)); if (distinct) result = result.Distinct(); return result; } public IEnumerable ExtractValues(string column, bool distinct = true) { var result = Rows.Select(r => r.Get(column)); if (distinct) result = result.Distinct(); return result; } public CoreTable LoadRow(object obj) { var row = NewRow(); LoadRow(row, obj); Rows.Add(row); return this; } 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; } 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; } } public class CoreTableAdapter : IEnumerable where T : BaseObject, new() { private List? _objects; private readonly CoreTable _table; public CoreTableAdapter(CoreTable table) { _table = table; } private List Objects { get => _objects ??= _table.Rows.Select(row => row.ToObject()).ToList(); } public T this[int index] => Objects[index]; public IEnumerator GetEnumerator() { return GetObjects(); } IEnumerator IEnumerable.GetEnumerator() { return GetObjects(); } private IEnumerator GetObjects() { return Objects.GetEnumerator(); } } public class DataTableJsonConverter : JsonConverter { public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { if(!(value is CoreTable table)) { writer.WriteNull(); return; } writer.WriteStartObject(); writer.WritePropertyName("Columns"); var cols = new Dictionary(); foreach (var column in table.Columns) cols[column.ColumnName] = column.DataType.EntityName(); serializer.Serialize(writer, cols); //writer.WriteEndObject(); //writer.WriteStartObject(); writer.WritePropertyName("Rows"); writer.WriteStartArray(); var size = 0; foreach (var row in table.Rows) //Console.WriteLine("- Serializing Row #"+row.Index.ToString()); try { writer.WriteStartArray(); foreach (var col in table.Columns) { var val = row[col.ColumnName]; if (val != null) size += val.ToString().Length; //Console.WriteLine(String.Format("Serializing Row #{0} Column [{1}] Length={2}", row.Index.ToString(), col.ColumnName, val.ToString().Length)); if (col.DataType.IsArray && val != null) { writer.WriteStartArray(); foreach (var val1 in (Array)val) writer.WriteValue(val1); writer.WriteEndArray(); } else if (col.DataType.GetInterfaces().Contains(typeof(IPackableList))) { writer.WriteStartArray(); foreach (var val1 in (IList)val) writer.WriteValue(val1); writer.WriteEndArray(); } else { writer.WriteValue(val); } } writer.WriteEndArray(); //Console.WriteLine(String.Format("[{0:D8}] Serializing Row #{1}", size, row.Index.ToString())); } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } writer.WriteEndArray(); writer.WriteEndObject(); } public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; var result = new CoreTable(); try { var json = JObject.Load(reader); if (json.TryGetValue("Columns", out var columns)) { foreach (JProperty column in columns.Children()) { var name = column.Name; var type = column.Value.ToObject(); result.Columns.Add(new CoreColumn { ColumnName = name, DataType = CoreUtils.GetEntity(type ?? "") }); } } if (json.TryGetValue("Rows", out var rows)) { foreach (JArray row in rows.Children()) if (row.Children().ToArray().Length == result.Columns.Count) { var newrow = result.NewRow(); var iCol = 0; foreach (var cell in row.Children()) { var column = result.Columns[iCol]; try { if (column.DataType.IsArray) { newrow[column.ColumnName] = cell.ToObject(column.DataType); } else if (column.DataType.GetInterfaces().Contains(typeof(IPackableList))) { } else { newrow[column.ColumnName] = cell.ToObject(column.DataType); } //if ((column.DataType == typeof(byte[])) && (value != null)) // newrow[column.ColumnName] = Convert.FromBase64String(value.ToString()); //else if (cell is JValue) // value = ((JValue)cell).Value; //else if (cell is JObject) // value = ((JObject)cell).ToObject(column.DataType); //else if (cell is JArray) // value = ((JArray)cell).ToObject(column.DataType); //else // value = null; } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } iCol++; } result.Rows.Add(newrow); } } } catch (Exception e) { Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace)); } return result; //String json = reader.Value.ToString(); //var table = new CoreTable(); //Dictionary dict = Serialization.Deserialize>(json); //Dictionary columns = Serialization.Deserialize>(dict["Columns"].ToString()); //List> rows = Serialization.Deserialize>>(dict["Rows"].ToString()); //String[] keys = columns.Keys.ToArray(); //foreach (String key in keys) // table.Columns.Add(new CoreColumn() { ColumnName = key, DataType = CoreUtils.GetEntity(columns[key]) }); //foreach (List row in rows) //{ // CoreRow newrow = table.NewRow(); // for (int i = 0; i < keys.Count(); i++) // { // if (row[i] is JObject) // newrow[keys[i]] = JsonConvert.DeserializeObject(row[i].ToString(), table.Columns[i].DataType); // else if (table.Columns[i].DataType == typeof(byte[])) // { // if (row[i] != null) // newrow.Set(keys[i], Convert.FromBase64String(row[i].ToString())); // } // else // { // try // { // object o = row[i]; // if (table.Columns[i].DataType != null) // o = CoreUtils.ChangeType(o, table.Columns[i].DataType); // newrow.Set(keys[i], o); // } // catch (Exception e) // { // } // } // //newrow[keys[i]] = row[i]; // } // table.Rows.Add(newrow); //} //return table; } public override bool CanConvert(Type objectType) { return typeof(CoreTable).GetTypeInfo().IsAssignableFrom(objectType); } } }