using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Runtime.Serialization; using System.Text.Json; namespace InABox.Core { public enum SortDirection { Ascending, Descending } public interface ISortOrder : ISerializeBinary { SortDirection Direction { get; set; } Expression Expression { get; set; } List Thens { get; } IEnumerable ColumnNames(); string AsOData(); void SerializeBinary(CoreBinaryWriter writer); void DeserializeBinary(CoreBinaryReader reader); } public static class SortOrder { public static ISortOrder Create(Type concrete, Expression> expression, SortDirection direction = SortDirection.Ascending) { if (!typeof(T).IsAssignableFrom(concrete)) throw new Exception($"Columns: {concrete.EntityName()} does not implement {typeof(T).EntityName()}"); var type = typeof(SortOrder<>).MakeGenericType(concrete); var property = CoreUtils.GetFullPropertyName(expression,"."); var result = Activator.CreateInstance(type, property, direction ); return (result as ISortOrder)!; } } public class SortOrder : SerializableExpression, ISortOrder // where T : Entity { public SortDirection Direction { get; set; } public List> Thens { get; private set; } List ISortOrder.Thens => Thens.OfType().ToList(); //public SortOrder Ascending() //{ // Direction = SortOrder.Ascending; // return this; //} //public SortOrder Descending() //{ // Direction = SortOrder.Descending; // return this; //} public SortOrder ThenBy(Expression> expression, SortDirection direction = SortDirection.Ascending) { var thenby = new SortOrder(expression, direction); Thens.Add(thenby); return this; } #region Constructors public SortOrder() { Thens = new List>(); Direction = SortDirection.Ascending; } public SortOrder(Expression> expression, SortDirection direction = SortDirection.Ascending) : base(expression) { Thens = new List>(); Direction = direction; } public SortOrder(string property, SortDirection direction = SortDirection.Ascending) { Thens = new List>(); Direction = direction; var iprop = DatabaseSchema.Property(typeof(T), property); Expression = iprop.Expression(); } public SortOrder(SerializationInfo info, StreamingContext context) { Deserialize(info, context); } public static explicit operator SortOrder(SortOrder v) { if (v == null) return null; var json = Serialization.Serialize(v); json = json.Replace(typeof(Entity).EntityName(), typeof(T).EntityName()); var result = Serialization.Deserialize>(json); return result; } #endregion #region Display Functions public string AsOData() { var orderby = new Dictionary { { SortDirection.Ascending, "asc" }, { SortDirection.Descending, "desc" } }; var prop = ""; if (CoreUtils.TryFindMemberExpression(Expression, out var mexp)) prop = CoreUtils.GetFullPropertyName(mexp, "/"); else prop = Expression.ToString(); var result = string.Format("{0} {1}", prop, orderby[Direction]); if (Thens != null && Thens.Count > 0) foreach (var then in Thens) { var ThenResult = then.AsOData(); if (!string.IsNullOrEmpty(ThenResult)) result = string.Format("{0}, {1}", result, ThenResult); } return result; } public override string ToString() { return AsOData(); } public IEnumerable ColumnNames() { yield return CoreUtils.ExpressionToString(typeof(T), Expression); foreach (var then in Thens) foreach (var x in then.ColumnNames()) yield return x; } #endregion //public Expression> AsExpression() //{ // var param = Expression.Parameter(typeof(T), "x"); // var result = Expression.Lambda>(Expression,param); // return result; //} #region Serialization public override void Serialize(SerializationInfo info, StreamingContext context) { info.AddValue("Direction", Direction.ToString()); if (Thens.Count > 0) info.AddValue("Thens", Thens, typeof(List>)); } public override void Deserialize(SerializationInfo info, StreamingContext context) { Direction = (SortDirection)Enum.Parse(typeof(SortDirection), (string)info.GetValue("Direction", typeof(string))); try { Thens = (List>)info.GetValue("Thens", typeof(List>)); } catch { Thens = new List>(); } } #endregion #region Binary Serialization public void SerializeBinary(CoreBinaryWriter writer) { writer.SerialiseExpression(typeof(T), Expression, false); writer.Write((byte)Direction); writer.Write(Thens.Count); foreach (var then in Thens) { then.SerializeBinary(writer); } } public void DeserializeBinary(CoreBinaryReader reader) { Expression = reader.DeserialiseExpression(typeof(T)); Direction = (SortDirection)reader.ReadByte(); Thens.Clear(); var nThens = reader.ReadInt32(); for(int i = 0; i < nThens; ++i) { var then = new SortOrder(); then.DeserializeBinary(reader); Thens.Add(then); } } #endregion } public static class SortOrderSerialization { /// /// Inverse of . /// /// /// public static SortOrder? ReadSortOrder(this CoreBinaryReader reader) { if (reader.ReadBoolean()) { var sortOrder = new SortOrder(); sortOrder.DeserializeBinary(reader); return sortOrder; } return null; } /// /// Inverse of . /// /// /// public static void Write(this CoreBinaryWriter writer, SortOrder? sortOrder) { if (sortOrder is null) { writer.Write(false); } else { writer.Write(true); sortOrder.SerializeBinary(writer); } } } public class SortOrderJsonConverter : CustomJsonConverter { public override bool CanConvert(Type objectType) { if (objectType.IsConstructedGenericType) { var ot = objectType.GetGenericTypeDefinition(); var tt = typeof(SortOrder<>); if (ot == tt) return true; } return false; } public override ISortOrder? Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options) { if (reader.TokenType == JsonTokenType.Null) return null; ISortOrder? result = null; var sType = ""; var sExpr = ""; var sDir = ""; while (reader.TokenType != JsonTokenType.EndObject && reader.Read()) { var tag = reader.GetString(); reader.Read(); if (string.Equals(tag, "$type")) { sType = reader.GetString(); if (!string.IsNullOrWhiteSpace(sType)) { var tType = Type.GetType(sType); if (tType != null) { var gType = typeof(SortOrder<>).MakeGenericType(tType); result = Activator.CreateInstance(gType) as ISortOrder; } } } else if (string.Equals(tag, "Expression") && result != null) sExpr = reader.GetString(); else if (string.Equals(tag, "Direction") && result != null) sDir = reader.GetString(); else if (string.Equals(tag, "Thens") && result != null) { reader.Read(); while (reader.TokenType != JsonTokenType.EndObject) { var then = Read(ref reader, typeof(ISortOrder), options); if (then != null) result.Thens.Add(then); } } } return result; } public override void Write(Utf8JsonWriter writer, ISortOrder value, JsonSerializerOptions options) { var type = value.GetType().GenericTypeArguments[0]; var property = CoreUtils.GetPropertyValue(value, "Expression") as MemberExpression; var prop = CoreUtils.ExpressionToString(type, property, true); var dir = CoreUtils.GetPropertyValue(value, "Direction")?.ToString() ?? nameof(SortDirection.Ascending); writer.WriteStartObject(); writer.WriteString("$type",value.GetType().FullName); writer.WriteString("Expression",prop); writer.WriteString("Direction",dir); if (CoreUtils.GetPropertyValue(value, "Thens") is IEnumerable thens) { writer.WritePropertyName("Thens"); writer.WriteStartArray(); foreach (var then in thens) Write(writer,then,options); writer.WriteEndArray(); } writer.WriteEndObject(); } } }