using System; using System.Collections; using System.Collections.Generic; using System.Linq.Expressions; using System.Runtime.Serialization; using Newtonsoft.Json; using Newtonsoft.Json.Linq; namespace InABox.Core { public enum SortDirection { Ascending, Descending } public interface ISortOrder : ISerializeBinary { SortDirection Direction { get; set; } Expression Expression { get; set; } IEnumerable 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; } IEnumerable ISortOrder.Thens => Thens; //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() { List result = new List(); result.Add(CoreUtils.ExpressionToString(typeof(T), Expression)); foreach (var then in Thens) result.AddRange(then.ColumnNames()); return result; } #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 : JsonConverter { public override void WriteJson(JsonWriter writer, object? value, JsonSerializer serializer) { if(value is null) { writer.WriteNull(); return; } var property = CoreUtils.GetPropertyValue(value, "Expression") as MemberExpression; //MethodInfo mi = value.GetType().GetTypeInfo().GetMethod("ExpressionToString"); //String prop = mi.Invoke(value, new object[] { property, true }) as String; var prop = CoreUtils.ExpressionToString(value.GetType().GenericTypeArguments[0], property, true); var dir = CoreUtils.GetPropertyValue(value, "Direction"); writer.WriteStartObject(); writer.WritePropertyName("$type"); writer.WriteValue(value.GetType().FullName); writer.WritePropertyName("Expression"); writer.WriteValue(prop); writer.WritePropertyName("Direction"); writer.WriteValue(dir); var thens = CoreUtils.GetPropertyValue(value, "Thens") as IList; if (thens != null && thens.Count > 0) { writer.WritePropertyName("Thens"); serializer.Serialize(writer, thens); } writer.WriteEndObject(); } public override object? ReadJson(JsonReader reader, Type objectType, object? existingValue, JsonSerializer serializer) { if (reader.TokenType == JsonToken.Null) return null; var data = new Dictionary(); while (reader.TokenType != JsonToken.EndObject && reader.Read()) if (reader.Value != null) { var key = reader.Value.ToString(); reader.Read(); if (String.Equals(key, "$type")) objectType = Type.GetType(reader.Value.ToString()) ?? objectType; else if (string.Equals(key, "Thens")) { var array = JArray.Load(reader); var thens = new List(); foreach (var item in array) { var then = ReadJson(item.CreateReader(), objectType, existingValue, serializer); if(then != null) thens.Add(then); //String jexp = item["Expression"].Value(); //MemberExpression exp = CoreUtils.StringToExpression(jexp) as MemberExpression; //var then = CreateSortOrder( // objectType, // exp.Member.Name, // (SortDirection)item["Direction"].Value() //); //thens.Add(then); } data[key] = thens; } else { data[key] = reader.Value; } } var jprop = data["Expression"].ToString(); var prop = CoreUtils.StringToExpression(jprop) as MemberExpression; var direction = (SortDirection)int.Parse(data["Direction"].ToString()); var result = Activator.CreateInstance(objectType, CoreUtils.GetFullPropertyName(prop, "."), direction); if (data.ContainsKey("Thens")) { var source = (data["Thens"] as List)!; var target = (CoreUtils.GetPropertyValue(result, "Thens") as IList)!; foreach (var srcitem in source) target.Add(srcitem); } return result; } public override bool CanConvert(Type objectType) { if (objectType.IsConstructedGenericType) { var ot = objectType.GetGenericTypeDefinition(); var tt = typeof(SortOrder<>); if (ot == tt) return true; } return false; } } }