Condition operators - Marked built-in operators as internal

Display conditions - Added docs to the service and profile types
Color gradient - Added docs
Storage - Moved profile entities to separate namespaces
Data bindings - Added entities to storage
Data bindings - Started implementing in the core
This commit is contained in:
SpoinkyNL committed 2020-08-30 23:09:38 +02:00
1 parent 8d88b14d78
commit 68b61cbcb2
43 files changed
+1059 -324

No files matched your search

@@ -1,22 +1,30 @@
using System.Collections.Generic;
using System.Linq.Expressions;
using Artemis.Core.Services.Interfaces;
using Artemis.Storage.Entities.Profile.Abstract;
namespace Artemis.Core.Models.Profile.Conditions.Abstract
{
/// <summary>
/// An abstract class for display condition parts
/// </summary>
public abstract class DisplayConditionPart
{
private readonly List<DisplayConditionPart> _children;
private readonly List<DisplayConditionPart> _children = new List<DisplayConditionPart>();
/// <summary>
/// Gets the parent of this part
/// </summary>
public DisplayConditionPart Parent { get; internal set; }
protected DisplayConditionPart()
{
_children = new List<DisplayConditionPart>();
}
public DisplayConditionPart Parent { get; set; }
/// <summary>
/// Gets the children of this part
/// </summary>
public IReadOnlyList<DisplayConditionPart> Children => _children.AsReadOnly();
/// <summary>
/// Adds a child to the display condition part's <see cref="Children" /> collection
/// </summary>
/// <param name="displayConditionPart"></param>
public void AddChild(DisplayConditionPart displayConditionPart)
{
if (!_children.Contains(displayConditionPart))
@@ -26,6 +34,10 @@ namespace Artemis.Core.Models.Profile.Conditions.Abstract
}
}
/// <summary>
/// Removes a child from the display condition part's <see cref="Children" /> collection
/// </summary>
/// <param name="displayConditionPart">The child to remove</param>
public void RemoveChild(DisplayConditionPart displayConditionPart)
{
if (_children.Contains(displayConditionPart))
@@ -36,13 +48,13 @@ namespace Artemis.Core.Models.Profile.Conditions.Abstract
}
/// <summary>
/// Evaluates the condition part on the data model
/// Evaluates the condition part on the data model
/// </summary>
/// <returns></returns>
public abstract bool Evaluate();
/// <summary>
/// Evaluates the condition part on the given target (currently only for lists)
/// Evaluates the condition part on the given target (currently only for lists)
/// </summary>
/// <param name="target"></param>
/// <returns></returns>
@@ -1,12 +0,0 @@
using System;
using System.Linq.Expressions;
using Artemis.Core.Plugins.DataModelExpansions;
namespace Artemis.Core.Models.Profile.Conditions
{
public class DisplayCondition
{
public Expression<Func<DataModel, bool>> ExpressionTree { get; set; }
public DisplayConditionGroup RootGroup { get; set; }
}
}
@@ -4,17 +4,31 @@ using Artemis.Core.Models.Profile.Conditions.Abstract;
using Artemis.Core.Services.Interfaces;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.Storage.Entities.Profile.Conditions;
namespace Artemis.Core.Models.Profile.Conditions
{
/// <summary>
/// A group containing zero to many <see cref="DisplayConditionPart" />s which it evaluates using a boolean specific
/// operator
/// </summary>
public class DisplayConditionGroup : DisplayConditionPart
{
/// <summary>
/// Creates a new instance of the <see cref="DisplayConditionGroup" /> class
/// </summary>
/// <param name="parent"></param>
public DisplayConditionGroup(DisplayConditionPart parent)
{
Parent = parent;
Entity = new DisplayConditionGroupEntity();
}
/// <summary>
/// Creates a new instance of the <see cref="DisplayConditionGroup" /> class
/// </summary>
/// <param name="parent"></param>
/// <param name="entity"></param>
public DisplayConditionGroup(DisplayConditionPart parent, DisplayConditionGroupEntity entity)
{
Parent = parent;
@@ -34,9 +48,14 @@ namespace Artemis.Core.Models.Profile.Conditions
}
}
/// <summary>
/// Gets or sets the boolean operator of this group
/// </summary>
public BooleanOperator BooleanOperator { get; set; }
public DisplayConditionGroupEntity Entity { get; set; }
internal DisplayConditionGroupEntity Entity { get; set; }
/// <inheritdoc />
public override bool Evaluate()
{
// Empty groups are always true
@@ -61,6 +80,7 @@ namespace Artemis.Core.Models.Profile.Conditions
}
}
/// <inheritdoc />
public override bool EvaluateObject(object target)
{
// Empty groups are always true
@@ -8,6 +8,7 @@ using Artemis.Core.Plugins.DataModelExpansions;
using Artemis.Core.Services.Interfaces;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.Storage.Entities.Profile.Conditions;
namespace Artemis.Core.Models.Profile.Conditions
{
@@ -6,15 +6,15 @@ using Artemis.Core.Extensions;
using Artemis.Core.Models.Profile.Conditions.Abstract;
using Artemis.Core.Plugins.DataModelExpansions;
using Artemis.Core.Services.Interfaces;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.Storage.Entities.Profile.Conditions;
using Newtonsoft.Json;
namespace Artemis.Core.Models.Profile.Conditions
{
public class DisplayConditionListPredicate : DisplayConditionPart
{
public DisplayConditionListPredicate(DisplayConditionPart parent, PredicateType predicateType)
public DisplayConditionListPredicate(DisplayConditionPart parent, ProfileRightSideType predicateType)
{
Parent = parent;
PredicateType = predicateType;
@@ -27,14 +27,14 @@ namespace Artemis.Core.Models.Profile.Conditions
{
Parent = parent;
Entity = entity;
PredicateType = (PredicateType) entity.PredicateType;
PredicateType = (ProfileRightSideType) entity.PredicateType;
ApplyParentList();
}
public DisplayConditionListPredicateEntity Entity { get; set; }
public PredicateType PredicateType { get; set; }
public ProfileRightSideType PredicateType { get; set; }
public DisplayConditionOperator Operator { get; private set; }
public Type ListType { get; private set; }
@@ -57,6 +57,7 @@ namespace Artemis.Core.Models.Profile.Conditions
UpdateList(parentList.ListDataModel, parentList.ListPropertyPath);
return;
}
current = current.Parent;
}
}
@@ -77,9 +78,7 @@ namespace Artemis.Core.Models.Profile.Conditions
ListType = listType;
}
else
{
ListType = null;
}
ListDataModel = dataModel;
ListPropertyPath = path;
@@ -109,7 +108,7 @@ namespace Artemis.Core.Models.Profile.Conditions
if (!ListContainsInnerPath(path))
throw new ArtemisCoreException($"List type {ListType.Name} does not contain path {path}");
PredicateType = PredicateType.Dynamic;
PredicateType = ProfileRightSideType.Dynamic;
RightPropertyPath = path;
CreateExpression();
@@ -117,7 +116,7 @@ namespace Artemis.Core.Models.Profile.Conditions
public void UpdateRightSideStatic(object staticValue)
{
PredicateType = PredicateType.Static;
PredicateType = ProfileRightSideType.Static;
RightPropertyPath = null;
SetStaticValue(staticValue);
@@ -145,18 +144,52 @@ namespace Artemis.Core.Models.Profile.Conditions
CreateExpression();
}
private void CreateExpression()
public override bool Evaluate()
{
CompiledListPredicate = null;
return false;
}
if (Operator == null)
return;
public override bool EvaluateObject(object target)
{
return CompiledListPredicate != null && CompiledListPredicate(target);
}
// If the operator does not support a right side, create a static expression because the right side will simply be null
if (PredicateType == PredicateType.Dynamic && Operator.SupportsRightSide)
CreateDynamicExpression();
public bool ListContainsInnerPath(string path)
{
if (ListType == null)
return false;
CreateStaticExpression();
var parts = path.Split('.');
var current = ListType;
foreach (var part in parts)
{
var property = current.GetProperty(part);
current = property?.PropertyType;
if (property == null)
return false;
}
return true;
}
public Type GetTypeAtInnerPath(string path)
{
if (!ListContainsInnerPath(path))
return null;
var parts = path.Split('.');
var current = ListType;
Type result = null;
foreach (var part in parts)
{
var property = current.GetProperty(part);
current = property.PropertyType;
result = property.PropertyType;
}
return result;
}
internal override void ApplyToEntity()
@@ -173,16 +206,6 @@ namespace Artemis.Core.Models.Profile.Conditions
Entity.OperatorType = Operator?.GetType().Name;
}
public override bool Evaluate()
{
return false;
}
public override bool EvaluateObject(object target)
{
return CompiledListPredicate != null && CompiledListPredicate(target);
}
internal override void Initialize(IDataModelService dataModelService)
{
// Left side
@@ -198,13 +221,13 @@ namespace Artemis.Core.Models.Profile.Conditions
}
// Right side dynamic
if (PredicateType == PredicateType.Dynamic && Entity.RightPropertyPath != null)
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightPropertyPath != null)
{
if (ListContainsInnerPath(Entity.RightPropertyPath))
UpdateLeftSide(Entity.LeftPropertyPath);
}
// Right side static
else if (PredicateType == PredicateType.Static && Entity.RightStaticValue != null)
else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null)
{
try
{
@@ -245,6 +268,20 @@ namespace Artemis.Core.Models.Profile.Conditions
return Entity;
}
private void CreateExpression()
{
CompiledListPredicate = null;
if (Operator == null)
return;
// If the operator does not support a right side, create a static expression because the right side will simply be null
if (PredicateType == ProfileRightSideType.Dynamic && Operator.SupportsRightSide)
CreateDynamicExpression();
CreateStaticExpression();
}
private void ValidateOperator()
{
if (LeftPropertyPath == null || Operator == null)
@@ -258,7 +295,7 @@ namespace Artemis.Core.Models.Profile.Conditions
private void ValidateRightSide()
{
var leftSideType = GetTypeAtInnerPath(LeftPropertyPath);
if (PredicateType == PredicateType.Dynamic)
if (PredicateType == ProfileRightSideType.Dynamic)
{
if (RightPropertyPath == null)
return;
@@ -349,43 +386,5 @@ namespace Artemis.Core.Models.Profile.Conditions
Expression.Property
);
}
public bool ListContainsInnerPath(string path)
{
if (ListType == null)
return false;
var parts = path.Split('.');
var current = ListType;
foreach (var part in parts)
{
var property = current.GetProperty(part);
current = property?.PropertyType;
if (property == null)
return false;
}
return true;
}
public Type GetTypeAtInnerPath(string path)
{
if (!ListContainsInnerPath(path))
return null;
var parts = path.Split('.');
var current = ListType;
Type result = null;
foreach (var part in parts)
{
var property = current.GetProperty(part);
current = property.PropertyType;
result = property.PropertyType;
}
return result;
}
}
}
@@ -8,6 +8,9 @@ using Artemis.Core.Services.Interfaces;
namespace Artemis.Core.Models.Profile.Conditions
{
/// <summary>
/// A display condition operator is used by the conditions system to perform a specific boolean check
/// </summary>
public abstract class DisplayConditionOperator
{
private IDataModelService _dataModelService;
@@ -39,6 +42,9 @@ namespace Artemis.Core.Models.Profile.Conditions
/// </summary>
public bool SupportsRightSide { get; protected set; } = true;
/// <summary>
/// Returns whether the given type is supported by the operator
/// </summary>
public bool SupportsType(Type type)
{
if (type == null)
@@ -54,16 +60,6 @@ namespace Artemis.Core.Models.Profile.Conditions
/// <returns></returns>
public abstract BinaryExpression CreateExpression(Expression leftSide, Expression rightSide);
/// <summary>
/// Returns an expression that checks the given expression for null
/// </summary>
/// <param name="expression"></param>
/// <returns></returns>
protected Expression CreateNullCheckExpression(Expression expression)
{
return Expression.NotEqual(expression, Expression.Constant(null));
}
internal void Register(PluginInfo pluginInfo, IDataModelService dataModelService)
{
if (_registered)
@@ -90,7 +86,7 @@ namespace Artemis.Core.Models.Profile.Conditions
private void InstanceOnPluginDisabled(object sender, EventArgs e)
{
// Profile editor service will call Unsubscribe
// The service will call Unsubscribe
_dataModelService.RemoveConditionOperator(this);
}
}
@@ -6,45 +6,95 @@ using Artemis.Core.Extensions;
using Artemis.Core.Models.Profile.Conditions.Abstract;
using Artemis.Core.Plugins.DataModelExpansions;
using Artemis.Core.Services.Interfaces;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.Storage.Entities.Profile.Conditions;
using Newtonsoft.Json;
namespace Artemis.Core.Models.Profile.Conditions
{
/// <summary>
/// A predicate in a display condition using either two data model values or one data model value and a
/// static value
/// </summary>
public class DisplayConditionPredicate : DisplayConditionPart
{
public DisplayConditionPredicate(DisplayConditionPart parent, PredicateType predicateType)
/// <summary>
/// Creates a new instance of the <see cref="DisplayConditionPredicate" /> class
/// </summary>
/// <param name="parent"></param>
/// <param name="predicateType"></param>
public DisplayConditionPredicate(DisplayConditionPart parent, ProfileRightSideType predicateType)
{
Parent = parent;
PredicateType = predicateType;
Entity = new DisplayConditionPredicateEntity();
}
/// <summary>
/// Creates a new instance of the <see cref="DisplayConditionPredicate" /> class
/// </summary>
/// <param name="parent"></param>
/// <param name="entity"></param>
public DisplayConditionPredicate(DisplayConditionPart parent, DisplayConditionPredicateEntity entity)
{
Parent = parent;
Entity = entity;
PredicateType = (PredicateType) entity.PredicateType;
PredicateType = (ProfileRightSideType) entity.PredicateType;
}
public DisplayConditionPredicateEntity Entity { get; set; }
/// <summary>
/// Gets or sets the predicate type
/// </summary>
public ProfileRightSideType PredicateType { get; set; }
public PredicateType PredicateType { get; set; }
/// <summary>
/// Gets the operator
/// </summary>
public DisplayConditionOperator Operator { get; private set; }
/// <summary>
/// Gets the currently used instance of the left data model
/// </summary>
public DataModel LeftDataModel { get; private set; }
/// <summary>
/// Gets the path of the left property in the <see cref="LeftDataModel" />
/// </summary>
public string LeftPropertyPath { get; private set; }
/// <summary>
/// Gets the currently used instance of the right data model
/// </summary>
public DataModel RightDataModel { get; private set; }
/// <summary>
/// Gets the path of the right property in the <see cref="RightDataModel" />
/// </summary>
public string RightPropertyPath { get; private set; }
/// <summary>
/// Gets the right static value, only used it <see cref="PredicateType" /> is
/// <see cref="ProfileRightSideType.Static" />
/// </summary>
public object RightStaticValue { get; private set; }
public DataModel ListDataModel { get; private set; }
public string ListPropertyPath { get; private set; }
/// <summary>
/// Gets the compiled function that evaluates this predicate if it of a dynamic <see cref="PredicateType" />
/// </summary>
public Func<DataModel, DataModel, bool> CompiledDynamicPredicate { get; private set; }
public Func<DataModel, bool> CompiledStaticPredicate { get; private set; }
public Func<object, bool> CompiledListPredicate { get; private set; }
/// <summary>
/// Gets the compiled function that evaluates this predicate if it is of a static <see cref="PredicateType" />
/// </summary>
public Func<DataModel, bool> CompiledStaticPredicate { get; private set; }
internal DisplayConditionPredicateEntity Entity { get; set; }
/// <summary>
/// Updates the left side of the predicate
/// </summary>
/// <param name="dataModel">The data model of the left side value</param>
/// <param name="path">The path pointing to the left side value inside the data model</param>
public void UpdateLeftSide(DataModel dataModel, string path)
{
if (dataModel != null && path == null)
@@ -67,6 +117,11 @@ namespace Artemis.Core.Models.Profile.Conditions
CreateExpression();
}
/// <summary>
/// Updates the right side of the predicate, makes the predicate dynamic and re-compiles the expression
/// </summary>
/// <param name="dataModel">The data model of the right side value</param>
/// <param name="path">The path pointing to the right side value inside the data model</param>
public void UpdateRightSide(DataModel dataModel, string path)
{
if (dataModel != null && path == null)
@@ -80,32 +135,61 @@ namespace Artemis.Core.Models.Profile.Conditions
throw new ArtemisCoreException($"Data model of type {dataModel.GetType().Name} does not contain a property at path '{path}'");
}
PredicateType = PredicateType.Dynamic;
PredicateType = ProfileRightSideType.Dynamic;
RightDataModel = dataModel;
RightPropertyPath = path;
CreateExpression();
}
/// <summary>
/// Updates the right side of the predicate, makes the predicate static and re-compiles the expression
/// </summary>
/// <param name="staticValue">The right side value to use</param>
public void UpdateRightSide(object staticValue)
{
PredicateType = PredicateType.Static;
PredicateType = ProfileRightSideType.Static;
RightDataModel = null;
RightPropertyPath = null;
SetStaticValue(staticValue);
// If the left side is empty simply apply the value, any validation will wait
if (LeftDataModel == null)
{
RightStaticValue = staticValue;
return;
}
var leftSideType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
// If not null ensure the types match and if not, convert it
if (staticValue != null && staticValue.GetType() == leftSideType)
RightStaticValue = staticValue;
else if (staticValue != null)
RightStaticValue = Convert.ChangeType(staticValue, leftSideType);
// If null create a default instance for value types or simply make it null for reference types
else if (leftSideType.IsValueType)
RightStaticValue = Activator.CreateInstance(leftSideType);
else
RightStaticValue = null;
CreateExpression();
}
/// <summary>
/// Updates the operator of the predicate and re-compiles the expression
/// </summary>
/// <param name="displayConditionOperator"></param>
public void UpdateOperator(DisplayConditionOperator displayConditionOperator)
{
// Calling CreateExpression will clear compiled expressions
if (displayConditionOperator == null)
{
Operator = null;
CreateExpression();
return;
}
// No need to clear compiled expressions, without a left data model they are already null
if (LeftDataModel == null)
{
Operator = displayConditionOperator;
@@ -113,26 +197,37 @@ namespace Artemis.Core.Models.Profile.Conditions
}
var leftType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
if (displayConditionOperator.SupportsType(leftType))
Operator = displayConditionOperator;
if (!displayConditionOperator.SupportsType(leftType))
throw new ArtemisCoreException($"Cannot apply operator {displayConditionOperator.GetType().Name} to this predicate because " +
$"it does not support left side type {leftType.Name}");
Operator = displayConditionOperator;
CreateExpression();
}
private void CreateExpression()
/// <inheritdoc />
public override bool Evaluate()
{
CompiledDynamicPredicate = null;
CompiledStaticPredicate = null;
CompiledListPredicate = null;
if (CompiledDynamicPredicate != null)
return CompiledDynamicPredicate(LeftDataModel, RightDataModel);
if (CompiledStaticPredicate != null)
return CompiledStaticPredicate(LeftDataModel);
if (Operator == null)
return;
return false;
}
// If the operator does not support a right side, create a static expression because the right side will simply be null
if (PredicateType == PredicateType.Dynamic && Operator.SupportsRightSide)
CreateDynamicExpression();
/// <inheritdoc />
public override bool EvaluateObject(object target)
{
return false;
}
CreateStaticExpression();
/// <inheritdoc />
public override string ToString()
{
if (PredicateType == ProfileRightSideType.Dynamic)
return $"[Dynamic] {LeftPropertyPath} {Operator.Description} {RightPropertyPath}";
return $"[Static] {LeftPropertyPath} {Operator.Description} {RightStaticValue}";
}
internal override void ApplyToEntity()
@@ -149,24 +244,6 @@ namespace Artemis.Core.Models.Profile.Conditions
Entity.OperatorType = Operator?.GetType().Name;
}
public override bool Evaluate()
{
if (CompiledDynamicPredicate != null)
return CompiledDynamicPredicate(LeftDataModel, RightDataModel);
if (CompiledStaticPredicate != null)
return CompiledStaticPredicate(LeftDataModel);
return false;
}
public override bool EvaluateObject(object target)
{
if (CompiledListPredicate != null)
return CompiledListPredicate(target);
return false;
}
internal override void Initialize(IDataModelService dataModelService)
{
// Left side
@@ -186,14 +263,14 @@ namespace Artemis.Core.Models.Profile.Conditions
}
// Right side dynamic
if (PredicateType == PredicateType.Dynamic && Entity.RightDataModelGuid != null)
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightDataModelGuid != null)
{
var dataModel = dataModelService.GetPluginDataModelByGuid(Entity.RightDataModelGuid.Value);
if (dataModel != null && dataModel.ContainsPath(Entity.RightPropertyPath))
UpdateRightSide(dataModel, Entity.RightPropertyPath);
}
// Right side static
else if (PredicateType == PredicateType.Static && Entity.RightStaticValue != null)
else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null)
{
try
{
@@ -235,6 +312,21 @@ namespace Artemis.Core.Models.Profile.Conditions
return Entity;
}
private void CreateExpression()
{
CompiledDynamicPredicate = null;
CompiledStaticPredicate = null;
if (Operator == null)
return;
// If the operator does not support a right side, create a static expression because the right side will simply be null
if (PredicateType == ProfileRightSideType.Dynamic && Operator.SupportsRightSide)
CreateDynamicExpression();
else
CreateStaticExpression();
}
private void ValidateOperator()
{
if (LeftDataModel == null || Operator == null)
@@ -248,7 +340,7 @@ namespace Artemis.Core.Models.Profile.Conditions
private void ValidateRightSide()
{
var leftSideType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
if (PredicateType == PredicateType.Dynamic)
if (PredicateType == ProfileRightSideType.Dynamic)
{
if (RightDataModel == null)
return;
@@ -266,70 +358,22 @@ namespace Artemis.Core.Models.Profile.Conditions
}
}
private void SetStaticValue(object staticValue)
{
// If the left side is empty simply apply the value, any validation will wait
if (LeftDataModel == null)
{
RightStaticValue = staticValue;
return;
}
var leftSideType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
// If not null ensure the types match and if not, convert it
if (staticValue != null && staticValue.GetType() == leftSideType)
RightStaticValue = staticValue;
else if (staticValue != null)
RightStaticValue = Convert.ChangeType(staticValue, leftSideType);
// If null create a default instance for value types or simply make it null for reference types
else if (leftSideType.IsValueType)
RightStaticValue = Activator.CreateInstance(leftSideType);
else
RightStaticValue = null;
}
private void CreateDynamicExpression()
{
if (LeftDataModel == null || RightDataModel == null || Operator == null)
return;
var isListExpression = LeftDataModel.GetListTypeInPath(LeftPropertyPath) != null;
var leftSideAccessor = CreateAccessor(LeftDataModel, LeftPropertyPath, "left", out var leftSideParameter);
var rightSideAccessor = CreateAccessor(RightDataModel, RightPropertyPath, "right", out var rightSideParameter);
Expression leftSideAccessor;
Expression rightSideAccessor;
ParameterExpression leftSideParameter;
ParameterExpression rightSideParameter = null;
if (isListExpression)
{
// List accessors share the same parameter because a list always contains one item per entry
leftSideParameter = Expression.Parameter(typeof(object), "listItem");
leftSideAccessor = CreateListAccessor(LeftDataModel, LeftPropertyPath, leftSideParameter);
rightSideAccessor = CreateListAccessor(RightDataModel, RightPropertyPath, leftSideParameter);
}
else
{
leftSideAccessor = CreateAccessor(LeftDataModel, LeftPropertyPath, "left", out leftSideParameter);
rightSideAccessor = CreateAccessor(RightDataModel, RightPropertyPath, "right", out rightSideParameter);
}
// 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 conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideAccessor);
if (isListExpression)
{
var lambda = Expression.Lambda<Func<object, bool>>(conditionExpression, leftSideParameter);
CompiledListPredicate = lambda.Compile();
}
else
{
var lambda = Expression.Lambda<Func<DataModel, DataModel, bool>>(conditionExpression, leftSideParameter, rightSideParameter);
CompiledDynamicPredicate = lambda.Compile();
}
var lambda = Expression.Lambda<Func<DataModel, DataModel, bool>>(conditionExpression, leftSideParameter, rightSideParameter);
CompiledDynamicPredicate = lambda.Compile();
}
private void CreateStaticExpression()
@@ -337,18 +381,7 @@ namespace Artemis.Core.Models.Profile.Conditions
if (LeftDataModel == null || Operator == null)
return;
var isListExpression = LeftDataModel.GetListTypeInPath(LeftPropertyPath) != null;
Expression leftSideAccessor;
ParameterExpression leftSideParameter;
if (isListExpression)
{
// List accessors share the same parameter because a list always contains one item per entry
leftSideParameter = Expression.Parameter(typeof(object), "listItem");
leftSideAccessor = CreateListAccessor(LeftDataModel, LeftPropertyPath, leftSideParameter);
}
else
leftSideAccessor = CreateAccessor(LeftDataModel, LeftPropertyPath, "left", out leftSideParameter);
var leftSideAccessor = CreateAccessor(LeftDataModel, LeftPropertyPath, "left", out var leftSideParameter);
// 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)
@@ -360,19 +393,10 @@ namespace Artemis.Core.Models.Profile.Conditions
: Expression.Constant(null, leftSideAccessor.Type);
var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideConstant);
if (isListExpression)
{
var lambda = Expression.Lambda<Func<object, bool>>(conditionExpression, leftSideParameter);
CompiledListPredicate = lambda.Compile();
}
else
{
var lambda = Expression.Lambda<Func<DataModel, bool>>(conditionExpression, leftSideParameter);
CompiledStaticPredicate = lambda.Compile();
}
var lambda = Expression.Lambda<Func<DataModel, bool>>(conditionExpression, leftSideParameter);
CompiledStaticPredicate = lambda.Compile();
}
private Expression CreateAccessor(DataModel dataModel, string path, string parameterName, out ParameterExpression parameter)
{
var listType = dataModel.GetListTypeInPath(path);
@@ -398,18 +422,5 @@ namespace Artemis.Core.Models.Profile.Conditions
Expression.Property
);
}
public override string ToString()
{
if (PredicateType == PredicateType.Dynamic)
return $"[Dynamic] {LeftPropertyPath} {Operator.Description} {RightPropertyPath}";
return $"[Static] {LeftPropertyPath} {Operator.Description} {RightStaticValue}";
}
}
public enum PredicateType
{
Static,
Dynamic
}
}
@@ -4,7 +4,7 @@ using System.Linq.Expressions;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
public class EqualsConditionOperator : DisplayConditionOperator
internal class EqualsConditionOperator : DisplayConditionOperator
{
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(object)};
@@ -4,7 +4,7 @@ using System.Linq.Expressions;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
public class GreaterThanConditionOperator : DisplayConditionOperator
internal class GreaterThanConditionOperator : DisplayConditionOperator
{
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
@@ -4,7 +4,7 @@ using System.Linq.Expressions;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
public class GreaterThanOrEqualConditionOperator : DisplayConditionOperator
internal class GreaterThanOrEqualConditionOperator : DisplayConditionOperator
{
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
@@ -4,7 +4,7 @@ using System.Linq.Expressions;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
public class LessThanConditionOperator : DisplayConditionOperator
internal class LessThanConditionOperator : DisplayConditionOperator
{
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
@@ -4,7 +4,7 @@ using System.Linq.Expressions;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
public class LessThanOrEqualConditionOperator : DisplayConditionOperator
internal class LessThanOrEqualConditionOperator : DisplayConditionOperator
{
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
@@ -4,7 +4,7 @@ using System.Linq.Expressions;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
public class NotEqualConditionOperator : DisplayConditionOperator
internal class NotEqualConditionOperator : DisplayConditionOperator
{
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> { typeof(object) };
@@ -5,7 +5,7 @@ using System.Reflection;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
public class StringContainsConditionOperator : DisplayConditionOperator
internal class StringContainsConditionOperator : DisplayConditionOperator
{
private readonly MethodInfo _toLower;
private readonly MethodInfo _contains;
@@ -5,7 +5,7 @@ using System.Reflection;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
public class StringEndsWithConditionOperator : DisplayConditionOperator
internal class StringEndsWithConditionOperator : DisplayConditionOperator
{
private readonly MethodInfo _toLower;
private readonly MethodInfo _endsWith;
@@ -5,7 +5,7 @@ using System.Reflection;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
public class StringEqualsConditionOperator : DisplayConditionOperator
internal class StringEqualsConditionOperator : DisplayConditionOperator
{
private readonly MethodInfo _toLower;
@@ -5,7 +5,7 @@ using System.Reflection;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
public class StringNotContainsConditionOperator : DisplayConditionOperator
internal class StringNotContainsConditionOperator : DisplayConditionOperator
{
private readonly MethodInfo _toLower;
private readonly MethodInfo _contains;
@@ -5,7 +5,7 @@ using System.Reflection;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
public class StringNotEqualConditionOperator : DisplayConditionOperator
internal class StringNotEqualConditionOperator : DisplayConditionOperator
{
private readonly MethodInfo _toLower;
@@ -4,7 +4,7 @@ using System.Linq.Expressions;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
public class StringNullConditionOperator : DisplayConditionOperator
internal class StringNullConditionOperator : DisplayConditionOperator
{
public StringNullConditionOperator()
{
@@ -5,7 +5,7 @@ using System.Reflection;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
public class StringStartsWithConditionOperator : DisplayConditionOperator
internal class StringStartsWithConditionOperator : DisplayConditionOperator
{
private readonly MethodInfo _toLower;
private readonly MethodInfo _startsWith;