123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471 |
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Linq.Expressions;
- using System.Reflection;
- using System.Runtime.Serialization;
- using Newtonsoft.Json;
- namespace InABox.Core
- {
- public interface IColumn
- {
- string Property { get; }
- }
- public class Column<T> : SerializableExpression<T>, IColumn
- {
- public bool IsEqualTo(String name) =>
- !String.IsNullOrWhiteSpace(name) && String.Equals(Property, name);
- public bool IsParentOf(String name) =>
- !String.IsNullOrWhiteSpace(name) && name.StartsWith(Property + ".");
-
- public Column()
- {
- }
- public Column(Expression<Func<T, object?>> expression) : base(expression)
- {
- //String[] parts = expression.ToString().Split(new String[] { "=>" }, StringSplitOptions.RemoveEmptyEntries);
- //string property = String.Join(".", parts.Last().Split('.').Skip(1));
- //property = property.Replace("Convert(", "").Replace("(","").Replace(")", "");
- Property = CoreUtils.GetFullPropertyName(expression, ".");
- }
- public Column(string property)
- {
- Property = property;
- var iprop = DatabaseSchema.Property(typeof(T), property);
- if (iprop != null)
- Expression = iprop.Expression();
- else
- Expression = CoreUtils.CreateMemberExpression(typeof(T), property);
- }
- public string Property { get; private set; }
- public override void Deserialize(SerializationInfo info, StreamingContext context)
- {
- }
- public override void Serialize(SerializationInfo info, StreamingContext context)
- {
- }
- public static explicit operator Column<T>(Column<Entity> v)
- {
- var result = new Column<T>();
- var exp = CoreUtils.ExpressionToString(typeof(T), v.Expression, true);
- result.Expression = CoreUtils.StringToExpression(exp);
- result.Property = v.Property;
- return result;
- }
- public override string ToString()
- {
- var name = Expression.ToString().Replace("x => ", "").Replace("x.", "");
- if (Expression.NodeType == System.Linq.Expressions.ExpressionType.Index)
- {
- var chars = name.SkipWhile(x => !x.Equals('[')).TakeWhile(x => !x.Equals(']'));
- name = string.Join("", chars).Replace("[", "").Replace("]", "").Replace("\"", "");
- }
- return name;
- //return Property.ToString();
- }
- public Type ExpressionType()
- {
- if (Expression == null)
- throw new Exception(string.Format("Expression [{0}] may not be null!", Property));
- if (Expression is IndexExpression)
- return DatabaseSchema.Property(typeof(T), Property).PropertyType;
- return Expression.Type;
- }
- }
- public interface IColumns
- {
- IEnumerable<string> ColumnNames();
- Dictionary<String, Type> AsDictionary();
- IColumns Add(string column);
- IColumns Add<T>(Expression<Func<T, object>> column);
- IColumns DefaultColumns(params ColumnType[] types);
- }
- public enum ColumnType
- {
- ExcludeVisible,
- IncludeOptional,
- IncludeForeignKeys,
- IncludeLinked,
- IncludeAggregates,
- IncludeFormulae,
- IncludeUserProperties,
- IncludeNestedLinks,
- IncludeEditable,
- All
- }
- public static class Columns
- {
- public static IColumns Create<T>(Type concrete)
- {
- if (!typeof(T).IsAssignableFrom(concrete))
- throw new Exception($"Columns: {concrete.EntityName()} does not implement {typeof(T).EntityName()}");
- var type = typeof(Columns<>).MakeGenericType(concrete);
- var result = Activator.CreateInstance(type);
- return result as IColumns;
- }
- public static IColumns Create(Type concrete)
- {
- var type = typeof(Columns<>).MakeGenericType(concrete);
- var result = Activator.CreateInstance(type) as IColumns;
- return result!;
- }
- }
- public class Columns<T> : IColumns
- {
- private readonly List<Column<T>> columns;
- public Columns()
- {
- columns = new List<Column<T>>();
- }
- public Columns(params Expression<Func<T, object?>>[] expressions) : this()
- {
- foreach (var expression in expressions)
- columns.Add(new Column<T>(expression));
- }
- public Columns(IEnumerable<string> properties) : this()
- {
- foreach (var property in properties)
- columns.Add(new Column<T>(property));
- }
- public Column<T>[] Items
- {
- get { return columns != null ? columns.ToArray() : new Column<T>[] { }; }
- set
- {
- columns.Clear();
- columns.AddRange(value);
- }
- }
- public IColumns Add(string column)
- {
- if(CoreUtils.TryGetProperty(typeof(T), column, out var propertyInfo))
- {
- if (!propertyInfo.PropertyType.GetInterfaces().Contains(typeof(IEnclosedEntity)) &&
- !propertyInfo.PropertyType.GetInterfaces().Any(x => x == typeof(IEntityLink)))
- {
- var exists = columns.Any(x => x.Expression.ToString().Replace("x.", "").Equals(column));
- if (!exists)
- columns.Add(new Column<T>(column));
- }
- }
- else
- {
- var prop = DatabaseSchema.Property(typeof(T), column);
- if (prop != null)
- {
- var exists = columns.Any(x => x.Expression.Equals(prop.Expression()));
- if (!exists)
- columns.Add(new Column<T>(column));
- }
- }
- return this;
- }
- public IColumns Add<TEntity>(Expression<Func<TEntity, object>> expression)
- {
- return Add(CoreUtils.GetFullPropertyName(expression, "."));
- }
- public IEnumerable<string> ColumnNames()
- {
- return Items.Select(c => c.Property);
- //List<String> result = new List<string>();
- //foreach (var col in Items)
- // result.Add(col.Property);
- //return result;
- }
- public Dictionary<String, Type> AsDictionary()
- {
- Dictionary< String, Type> result = new Dictionary< String, Type>();
- foreach (var column in Items)
- result[column.Property] = column.ExpressionType();
- return result;
- }
- public IColumns DefaultColumns(params ColumnType[] types)
- {
- return Default(types);
- }
- public Columns<T> Add(params string[] columnnames)
- {
- foreach (var name in columnnames)
- Add(name);
- return this;
- }
- public Columns<T> Add(IEnumerable<string> columnnames)
- {
- foreach (var name in columnnames)
- Add(name);
- return this;
- }
-
- public Columns<T> Add(Expression<Func<T, object>> expression)
- {
- try
- {
- var exists = columns.Any(x => x.Expression.ToString().Replace("x.", "").Equals(CoreUtils.GetFullPropertyName(expression, ".")));
- if (!exists)
- columns.Add(new Column<T>(expression));
- }
- catch (Exception e)
- {
- Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
- }
- return this;
- }
- public Columns<T> Add<TType>(Expression<Func<T, TType>> expression)
- {
- try
- {
- var exists = columns.Any(x => x.Expression.ToString().Replace("x.", "").Equals(CoreUtils.GetFullPropertyName(expression, ".")));
- if (!exists)
- {
- var property = CoreUtils.GetFullPropertyName(expression, ".");
- columns.Add(new Column<T>(property));
- }
- }
- catch (Exception e)
- {
- Logger.Send(LogType.Error, "", string.Format("*** Unknown Error: {0}\n{1}", e.Message, e.StackTrace));
- }
- return this;
- }
- public Columns<T> Remove(string column)
- {
- var col = new Column<T>(column);
- columns.RemoveAll(x => x.ToString() == col.ToString());
- return this;
- }
- public static explicit operator Columns<T>(Columns<Entity> vs)
- {
- var result = new Columns<T>();
- var items = vs.Items.Cast<Column<T>>().ToArray();
- result.Items = items;
- //List<Column<T>> cols = new List<Column<T>>();
- //foreach (var v in vs.Items)
- // cols.Add((Column<T>)v);
- //result.Items = cols.ToArray();
- return result;
- }
- public Columns<T> Default(params ColumnType[] types)
- {
- columns.Clear();
-
- var props = DatabaseSchema.Properties(typeof(T)).Where(x=>x.Setter() != null).OrderBy(x => CoreUtils.GetPropertySequence(typeof(T), x.Name)).ToList();
- if (types.Contains(ColumnType.All))
- {
- foreach (var prop in props)
- columns.Add(new Column<T>(prop.Name));
- return this;
- }
- if (typeof(T).IsSubclassOf(typeof(Entity)))
- columns.Add(new Column<T>("ID"));
-
- for (int iCol=0; iCol<props.Count; iCol++)
- {
- var prop = props[iCol];
- var bOK = true;
- var bIsForeignKey = false;
- var bNullEditor = prop.Editor is NullEditor;
- if (prop is CustomProperty)
- {
- if (!types.Any(x => x.Equals(ColumnType.IncludeUserProperties)))
- bOK = false;
- else
- columns.Add(new Column<T>(prop.Name));
- }
- if (bOK)
- if (prop.Name.Contains(".") && !(prop is CustomProperty))
- {
- var ancestors = prop.Name.Split('.');
- var anclevel = 2;
- for (var i = 1; i < ancestors.Length; i++)
- {
- var ancestor = string.Join(".", ancestors.Take(i));
- var ancprop = CoreUtils.GetProperty(typeof(T), ancestor);
-
- bNullEditor = bNullEditor || ancprop.GetCustomAttribute<NullEditor>() != null;
- if (ancprop.PropertyType.GetInterfaces().Contains(typeof(IEnclosedEntity)))
- anclevel++;
- else if (ancprop.PropertyType.GetInterfaces().Contains(typeof(IEntityLink)))
- {
- if (types.Contains(ColumnType.IncludeLinked) || types.Contains(ColumnType.IncludeForeignKeys))
- {
- if (types.Contains(ColumnType.IncludeNestedLinks) || ancestors.Length <= anclevel)
- {
- if (prop.Name.EndsWith(".ID") && types.Contains(ColumnType.IncludeForeignKeys))
- {
- bIsForeignKey = true;
- break;
- }
- if (!types.Contains(ColumnType.IncludeLinked))
- {
- bOK = false;
- break;
- }
- }
- else
- {
- bOK = false;
- break;
- }
- }
- else
- {
- bOK = false;
- break;
- }
- }
- }
- }
- if (bOK)
- {
- var visible = prop.Editor != null
- ? bNullEditor
- ? Visible.Hidden
- : prop.Editor.Visible
- : Visible.Optional;
- var editable = prop.Editor != null
- ? bNullEditor
- ? Editable.Hidden
- : prop.Editor.Editable
- : Editable.Enabled;
-
- bOK = (types.Any(x => x.Equals(ColumnType.IncludeForeignKeys)) && bIsForeignKey) ||
- (!types.Any(x => x.Equals(ColumnType.ExcludeVisible)) && visible.Equals(Visible.Default)) ||
- (types.Any(x => x.Equals(ColumnType.IncludeOptional)) && visible.Equals(Visible.Optional)) ||
- (types.Any(x => x.Equals(ColumnType.IncludeEditable)) && !editable.Equals(Editable.Hidden));
- }
- var property = bOK ? DatabaseSchema.Property(typeof(T), prop.Name) : null;
- if (property is StandardProperty)
- {
- if (bOK && !types.Any(x => x.Equals(ColumnType.IncludeAggregates)))
- bOK = CoreUtils.GetProperty(typeof(T), prop.Name).GetCustomAttribute<AggregateAttribute>() == null;
- if (bOK && !types.Any(x => x.Equals(ColumnType.IncludeFormulae)))
- bOK = CoreUtils.GetProperty(typeof(T), prop.Name).GetCustomAttribute<FormulaAttribute>() == null;
- }
- if (bOK && !columns.Any(x => string.Equals(x.Property?.ToUpper(), prop.Name?.ToUpper())))
- {
- if (bOK && prop.Editor is LookupEditor)
- {
- var le = prop.Editor as LookupEditor;
- if (le.OtherColumns != null)
- {
- var prefix = String.Join(".",prop.Name.Split('.').Reverse().Skip(1).Reverse());
- foreach (var col in le.OtherColumns)
- {
- String newcol = prefix + "." + col.Key;
- if (!columns.Any(x => String.Equals(newcol, x.Property)))
- columns.Add(new Column<T>(newcol));
- }
- }
- }
- if (!columns.Any(x => String.Equals(prop.Name, x.Property)))
- columns.Add(new Column<T>(prop.Name));
- }
- }
- return this;
- }
- }
- public class ColumnJsonConverter : JsonConverter
- {
- public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
- {
- var property = CoreUtils.GetPropertyValue(value, "Expression") as Expression;
- var prop = CoreUtils.ExpressionToString(value.GetType().GenericTypeArguments[0], property, true);
- var name = CoreUtils.GetPropertyValue(value, "Property") as string;
- writer.WriteStartObject();
- writer.WritePropertyName("Expression");
- writer.WriteValue(prop);
- writer.WritePropertyName("Property");
- writer.WriteValue(name);
- 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<string, object>();
- while (reader.TokenType != JsonToken.EndObject && reader.Read())
- if (reader.Value != null)
- {
- var key = reader.Value.ToString();
- reader.Read();
- data[key] = reader.Value;
- }
- var prop = data["Property"].ToString();
- var result = Activator.CreateInstance(objectType, prop);
- return result;
- }
- public override bool CanConvert(Type objectType)
- {
- if (objectType.IsConstructedGenericType)
- {
- var ot = objectType.GetGenericTypeDefinition();
- var tt = typeof(Column<>);
- if (ot == tt)
- return true;
- }
- return false;
- }
- }
- }
|