Display conditions - Implemented expression tree creation

Display conditions - Use the most appropriate number input
UI general - Use current localisation for decimal seperators and enfore in inputs
This commit is contained in:
Robert committed 2020-07-08 17:26:17 +02:00
1 parent b869078f08
commit 3da2fbcf0a
21 files changed
+282 -131

No files matched your search

+26 -1
View File
@@ -28,7 +28,7 @@ namespace Artemis.Core
public static readonly PluginInfo CorePluginInfo = new PluginInfo {Guid = Guid.Parse("ffffffff-ffff-ffff-ffff-ffffffffffff"), Name = "Artemis Core"};
/// <summary>
/// A read-only collection containing all primitive number types
/// A read-only collection containing all primitive numeric types
/// </summary>
public static IReadOnlyCollection<Type> NumberTypes = new List<Type>
{
@@ -44,5 +44,30 @@ namespace Artemis.Core
typeof(double),
typeof(decimal)
};
/// <summary>
/// A read-only collection containing all primitive integral numeric types
/// </summary>
public static IReadOnlyCollection<Type> IntegralNumberTypes = new List<Type>
{
typeof(sbyte),
typeof(byte),
typeof(short),
typeof(ushort),
typeof(int),
typeof(uint),
typeof(long),
typeof(ulong)
};
/// <summary>
/// A read-only collection containing all primitive floating-point numeric types
/// </summary>
public static IReadOnlyCollection<Type> FloatNumberTypes = new List<Type>
{
typeof(float),
typeof(double),
typeof(decimal)
};
}
}
@@ -34,14 +34,6 @@ namespace Artemis.Core.Models.Profile.Conditions
/// </summary>
public abstract string Icon { get; }
/// <summary>
/// Creates a binary expression comparing two types
/// </summary>
/// <param name="leftSideType">The type of parameter passed to the left side of the expression</param>
/// <param name="rightSideType">The type of parameter passed to the right side of the expression</param>
/// <returns></returns>
public abstract BinaryExpression CreateExpression(Type leftSideType, Type rightSideType);
internal void Register(PluginInfo pluginInfo, IDataModelService dataModelService)
{
if (_registered)
@@ -78,5 +70,13 @@ namespace Artemis.Core.Models.Profile.Conditions
return true;
return CompatibleTypes.Any(t => t.IsCastableFrom(type));
}
/// <summary>
/// Creates a binary expression comparing two types
/// </summary>
/// <param name="leftSide">The parameter on the left side of the expression</param>
/// <param name="rightSide">The parameter on the right side of the expression</param>
/// <returns></returns>
public abstract BinaryExpression CreateExpression(Expression leftSide, Expression rightSide);
}
}
@@ -1,5 +1,9 @@
using System;
using System.Linq;
using System.Linq.Expressions;
using Artemis.Core.Models.Profile.Conditions.Abstract;
using Artemis.Core.Plugins.Abstract.DataModels;
using Artemis.Core.Services.Interfaces;
namespace Artemis.Core.Models.Profile.Conditions
{
@@ -10,10 +14,88 @@ namespace Artemis.Core.Models.Profile.Conditions
public Guid LeftDataModelGuid { get; set; }
public string LeftPropertyPath { get; set; }
public Guid RightDataModelGuid { get; set; }
public string RightPropertyPath { get; set; }
// TODO: Implement type-checking or perhaps convert it here
public object RightStaticValue { get; set; }
public Expression<Func<DataModel, DataModel, bool>> DynamicConditionLambda { get; private set; }
public Func<DataModel, DataModel, bool> CompiledDynamicConditionLambda { get; private set; }
public Expression<Func<DataModel, bool>> StaticConditionLambda { get; private set; }
public Func<DataModel, bool> CompiledStaticConditionLambda { get; private set; }
public void CreateExpression(IDataModelService dataModelService)
{
if (PredicateType == PredicateType.Dynamic)
CreateDynamicExpression(dataModelService);
else
CreateStaticExpression(dataModelService);
}
private void CreateDynamicExpression(IDataModelService dataModelService)
{
if (LeftDataModelGuid == Guid.Empty || string.IsNullOrWhiteSpace(LeftPropertyPath))
return;
if (RightDataModelGuid == Guid.Empty || string.IsNullOrWhiteSpace(RightPropertyPath))
return;
var leftDataModel = dataModelService.GetPluginDataModelByGuid(LeftDataModelGuid);
if (leftDataModel == null)
return;
var rightDataModel = dataModelService.GetPluginDataModelByGuid(RightDataModelGuid);
if (rightDataModel == null)
return;
var leftSideParameter = Expression.Parameter(typeof(DataModel), "leftDataModel");
var leftSideAccessor = LeftPropertyPath.Split('.').Aggregate<string, Expression>(
Expression.Convert(leftSideParameter, leftDataModel.GetType()), // Cast to the appropriate type
Expression.Property
);
var rightSideParameter = Expression.Parameter(typeof(DataModel), "rightDataModel");
var rightSideAccessor = RightPropertyPath.Split('.').Aggregate<string, Expression>(
Expression.Convert(rightSideParameter, rightDataModel.GetType()), // Cast to the appropriate type
Expression.Property
);
// A conversion may be required if the types differ
// This can cause issues if the DisplayConditionOperator wasn't accurate in it's supported types but that is not a concern here
if (rightSideAccessor.Type != leftSideAccessor.Type)
rightSideAccessor = Expression.Convert(rightSideAccessor, leftSideAccessor.Type);
var dynamicConditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideAccessor);
DynamicConditionLambda = Expression.Lambda<Func<DataModel, DataModel, bool>>(dynamicConditionExpression, leftSideParameter, rightSideParameter);
CompiledDynamicConditionLambda = DynamicConditionLambda.Compile();
}
private void CreateStaticExpression(IDataModelService dataModelService)
{
if (LeftDataModelGuid == Guid.Empty || string.IsNullOrWhiteSpace(LeftPropertyPath))
return;
var leftDataModel = dataModelService.GetPluginDataModelByGuid(LeftDataModelGuid);
if (leftDataModel == null)
return;
var leftSideParameter = Expression.Parameter(typeof(DataModel), "leftDataModel");
var leftSideAccessor = LeftPropertyPath.Split('.').Aggregate<string, Expression>(
Expression.Convert(leftSideParameter, leftDataModel.GetType()), // Cast to the appropriate type
Expression.Property
);
// If the left side is a value type but the input is empty, this isn't a valid expression
if (leftSideAccessor.Type.IsValueType && RightStaticValue == null)
return;
var rightSideConstant = Expression.Constant(RightStaticValue);
var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideConstant);
StaticConditionLambda = Expression.Lambda<Func<DataModel, bool>>(conditionExpression, leftSideParameter);
CompiledStaticConditionLambda = StaticConditionLambda.Compile();
}
}
public enum PredicateType
@@ -11,11 +11,9 @@ namespace Artemis.Core.Models.Profile.Conditions.Operators
public override string Description => "Equals";
public override string Icon => "Equal";
public override BinaryExpression CreateExpression(Type leftSideType, Type rightSideType)
public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
{
var leftSideParameter = Expression.Parameter(leftSideType, "a");
var rightSideParameter = Expression.Parameter(rightSideType, "b");
return Expression.Equal(leftSideParameter, rightSideParameter);
return Expression.Equal(leftSide, rightSide);
}
}
}
@@ -11,11 +11,9 @@ namespace Artemis.Core.Models.Profile.Conditions.Operators
public override string Description => "Is greater than";
public override string Icon => "GreaterThan";
public override BinaryExpression CreateExpression(Type leftSideType, Type rightSideType)
public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
{
var leftSideParameter = Expression.Parameter(leftSideType, "a");
var rightSideParameter = Expression.Parameter(rightSideType, "b");
return Expression.GreaterThan(leftSideParameter, rightSideParameter);
return Expression.GreaterThan(leftSide, rightSide);
}
}
}
@@ -11,11 +11,9 @@ namespace Artemis.Core.Models.Profile.Conditions.Operators
public override string Description => "Is greater than or equal to";
public override string Icon => "GreaterThanOrEqual";
public override BinaryExpression CreateExpression(Type leftSideType, Type rightSideType)
public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
{
var leftSideParameter = Expression.Parameter(leftSideType, "a");
var rightSideParameter = Expression.Parameter(rightSideType, "b");
return Expression.GreaterThanOrEqual(leftSideParameter, rightSideParameter);
return Expression.GreaterThanOrEqual(leftSide, rightSide);
}
}
}
@@ -11,11 +11,9 @@ namespace Artemis.Core.Models.Profile.Conditions.Operators
public override string Description => "Is less than";
public override string Icon => "LessThan";
public override BinaryExpression CreateExpression(Type leftSideType, Type rightSideType)
public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
{
var leftSideParameter = Expression.Parameter(leftSideType, "a");
var rightSideParameter = Expression.Parameter(rightSideType, "b");
return Expression.LessThan(leftSideParameter, rightSideParameter);
return Expression.LessThan(leftSide, rightSide);
}
}
}
@@ -11,11 +11,9 @@ namespace Artemis.Core.Models.Profile.Conditions.Operators
public override string Description => "Is less than or equal to";
public override string Icon => "LessThanOrEqual";
public override BinaryExpression CreateExpression(Type leftSideType, Type rightSideType)
public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
{
var leftSideParameter = Expression.Parameter(leftSideType, "a");
var rightSideParameter = Expression.Parameter(rightSideType, "b");
return Expression.LessThanOrEqual(leftSideParameter, rightSideParameter);
return Expression.LessThanOrEqual(leftSide, rightSide);
}
}
}
@@ -11,11 +11,9 @@ namespace Artemis.Core.Models.Profile.Conditions.Operators
public override string Description => "Does not equal";
public override string Icon => "NotEqualVariant";
public override BinaryExpression CreateExpression(Type leftSideType, Type rightSideType)
public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
{
var leftSideParameter = Expression.Parameter(leftSideType, "a");
var rightSideParameter = Expression.Parameter(rightSideType, "b");
return Expression.NotEqual(leftSideParameter, rightSideParameter);
return Expression.NotEqual(leftSide, rightSide);
}
}
}
@@ -93,6 +93,15 @@ namespace Artemis.Core.Services
return null;
}
public DataModel GetPluginDataModelByGuid(Guid pluginGuid)
{
var pluginInfo = _pluginService.GetAllPluginInfo().FirstOrDefault(i => i.Guid == pluginGuid);
if (pluginInfo == null || !pluginInfo.Enabled)
return null;
return GetPluginDataModel(pluginInfo.Instance);
}
public bool GetPluginExtendsDataModel(Plugin plugin)
{
if (plugin is Module module)
@@ -31,6 +31,12 @@ namespace Artemis.Core.Services.Interfaces
/// <param name="plugin">Should be a module with a data model or a data model expansion</param>
DataModel GetPluginDataModel(Plugin plugin);
/// <summary>
/// If found, returns the data model of the provided plugin
/// </summary>
/// <param name="pluginGuid">Should be a module with a data model or a data model expansion</param>
DataModel GetPluginDataModelByGuid(Guid pluginGuid);
/// <summary>
/// Determines whether the given plugin expands the main data model
/// </summary>