mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-01-01 10:13:30 +00:00
Conditions - Added null-checks to accessors
Conditions - Simplified operators, removing unnecessary expression trees
This commit is contained in:
parent
ad3581a93a
commit
33373bda57
@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq.Expressions;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
namespace Artemis.Core.DefaultTypes
|
||||||
{
|
{
|
||||||
@ -11,9 +10,9 @@ namespace Artemis.Core.DefaultTypes
|
|||||||
public override string Description => "Equals";
|
public override string Description => "Equals";
|
||||||
public override string Icon => "Equal";
|
public override string Icon => "Equal";
|
||||||
|
|
||||||
public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
|
public override bool Evaluate(object a, object b)
|
||||||
{
|
{
|
||||||
return Expression.Equal(leftSide, rightSide);
|
return Equals(a, b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq.Expressions;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
namespace Artemis.Core.DefaultTypes
|
||||||
{
|
{
|
||||||
@ -11,9 +10,9 @@ namespace Artemis.Core.DefaultTypes
|
|||||||
public override string Description => "Is greater than";
|
public override string Description => "Is greater than";
|
||||||
public override string Icon => "GreaterThan";
|
public override string Icon => "GreaterThan";
|
||||||
|
|
||||||
public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
|
public override bool Evaluate(object a, object b)
|
||||||
{
|
{
|
||||||
return Expression.GreaterThan(leftSide, rightSide);
|
return Convert.ToSingle(a) > Convert.ToSingle(b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq.Expressions;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
namespace Artemis.Core.DefaultTypes
|
||||||
{
|
{
|
||||||
@ -11,9 +10,9 @@ namespace Artemis.Core.DefaultTypes
|
|||||||
public override string Description => "Is greater than or equal to";
|
public override string Description => "Is greater than or equal to";
|
||||||
public override string Icon => "GreaterThanOrEqual";
|
public override string Icon => "GreaterThanOrEqual";
|
||||||
|
|
||||||
public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
|
public override bool Evaluate(object a, object b)
|
||||||
{
|
{
|
||||||
return Expression.GreaterThanOrEqual(leftSide, rightSide);
|
return Convert.ToSingle(a) >= Convert.ToSingle(b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq.Expressions;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
namespace Artemis.Core.DefaultTypes
|
||||||
{
|
{
|
||||||
@ -11,9 +10,9 @@ namespace Artemis.Core.DefaultTypes
|
|||||||
public override string Description => "Is less than";
|
public override string Description => "Is less than";
|
||||||
public override string Icon => "LessThan";
|
public override string Icon => "LessThan";
|
||||||
|
|
||||||
public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
|
public override bool Evaluate(object a, object b)
|
||||||
{
|
{
|
||||||
return Expression.LessThan(leftSide, rightSide);
|
return Convert.ToSingle(a) < Convert.ToSingle(b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq.Expressions;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
namespace Artemis.Core.DefaultTypes
|
||||||
{
|
{
|
||||||
@ -11,9 +10,9 @@ namespace Artemis.Core.DefaultTypes
|
|||||||
public override string Description => "Is less than or equal to";
|
public override string Description => "Is less than or equal to";
|
||||||
public override string Icon => "LessThanOrEqual";
|
public override string Icon => "LessThanOrEqual";
|
||||||
|
|
||||||
public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
|
public override bool Evaluate(object a, object b)
|
||||||
{
|
{
|
||||||
return Expression.LessThanOrEqual(leftSide, rightSide);
|
return Convert.ToSingle(a) <= Convert.ToSingle(b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq.Expressions;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
namespace Artemis.Core.DefaultTypes
|
||||||
{
|
{
|
||||||
@ -11,9 +10,9 @@ namespace Artemis.Core.DefaultTypes
|
|||||||
public override string Description => "Does not equal";
|
public override string Description => "Does not equal";
|
||||||
public override string Icon => "NotEqualVariant";
|
public override string Icon => "NotEqualVariant";
|
||||||
|
|
||||||
public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
|
public override bool Evaluate(object a, object b)
|
||||||
{
|
{
|
||||||
return Expression.NotEqual(leftSide, rightSide);
|
return !Equals(a, b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Artemis.Core.DefaultTypes
|
||||||
|
{
|
||||||
|
internal class NotNullConditionOperator : ConditionOperator
|
||||||
|
{
|
||||||
|
public NotNullConditionOperator()
|
||||||
|
{
|
||||||
|
SupportsRightSide = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(object)};
|
||||||
|
|
||||||
|
public override string Description => "Is not null";
|
||||||
|
public override string Icon => "CheckboxMarkedCircleOutline";
|
||||||
|
|
||||||
|
public override bool Evaluate(object a, object b)
|
||||||
|
{
|
||||||
|
return a != null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,23 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Artemis.Core.DefaultTypes
|
||||||
|
{
|
||||||
|
internal class NullConditionOperator : ConditionOperator
|
||||||
|
{
|
||||||
|
public NullConditionOperator()
|
||||||
|
{
|
||||||
|
SupportsRightSide = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(object)};
|
||||||
|
|
||||||
|
public override string Description => "Is null";
|
||||||
|
public override string Icon => "Null";
|
||||||
|
|
||||||
|
public override bool Evaluate(object a, object b)
|
||||||
|
{
|
||||||
|
return a == null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,33 +1,21 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
namespace Artemis.Core.DefaultTypes
|
||||||
{
|
{
|
||||||
internal class StringContainsConditionOperator : ConditionOperator
|
internal class StringContainsConditionOperator : ConditionOperator
|
||||||
{
|
{
|
||||||
private readonly MethodInfo _contains;
|
|
||||||
private readonly MethodInfo _toLower;
|
|
||||||
|
|
||||||
public StringContainsConditionOperator()
|
|
||||||
{
|
|
||||||
_toLower = typeof(string).GetMethod("ToLower", new Type[] { });
|
|
||||||
_contains = typeof(string).GetMethod("Contains", new[] {typeof(string)});
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(string)};
|
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(string)};
|
||||||
|
|
||||||
public override string Description => "Contains";
|
public override string Description => "Contains";
|
||||||
public override string Icon => "Contain";
|
public override string Icon => "Contain";
|
||||||
|
|
||||||
public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
|
public override bool Evaluate(object a, object b)
|
||||||
{
|
{
|
||||||
var contains = Expression.Equal(
|
var aString = (string) a;
|
||||||
Expression.Call(Expression.Call(leftSide, _toLower), _contains, Expression.Call(rightSide, _toLower)),
|
var bString = (string) b;
|
||||||
Expression.Constant(true)
|
|
||||||
);
|
return bString != null && aString != null && aString.Contains(bString, StringComparison.InvariantCultureIgnoreCase);
|
||||||
return AddNullChecks(leftSide, rightSide, contains);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,33 +1,21 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
namespace Artemis.Core.DefaultTypes
|
||||||
{
|
{
|
||||||
internal class StringEndsWithConditionOperator : ConditionOperator
|
internal class StringEndsWithConditionOperator : ConditionOperator
|
||||||
{
|
{
|
||||||
private readonly MethodInfo _endsWith;
|
|
||||||
private readonly MethodInfo _toLower;
|
|
||||||
|
|
||||||
public StringEndsWithConditionOperator()
|
|
||||||
{
|
|
||||||
_toLower = typeof(string).GetMethod("ToLower", new Type[] { });
|
|
||||||
_endsWith = typeof(string).GetMethod("EndsWith", new[] {typeof(string)});
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(string)};
|
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(string)};
|
||||||
|
|
||||||
public override string Description => "Ends with";
|
public override string Description => "Ends with";
|
||||||
public override string Icon => "ContainEnd";
|
public override string Icon => "ContainEnd";
|
||||||
|
|
||||||
public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
|
public override bool Evaluate(object a, object b)
|
||||||
{
|
{
|
||||||
var endsWith = Expression.Equal(
|
var aString = (string) a;
|
||||||
Expression.Call(Expression.Call(leftSide, _toLower), _endsWith, Expression.Call(rightSide, _toLower)),
|
var bString = (string) b;
|
||||||
Expression.Constant(true)
|
|
||||||
);
|
return bString != null && aString != null && aString.EndsWith(bString, StringComparison.InvariantCultureIgnoreCase);
|
||||||
return AddNullChecks(leftSide, rightSide, endsWith);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,27 +1,21 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
namespace Artemis.Core.DefaultTypes
|
||||||
{
|
{
|
||||||
internal class StringEqualsConditionOperator : ConditionOperator
|
internal class StringEqualsConditionOperator : ConditionOperator
|
||||||
{
|
{
|
||||||
private readonly MethodInfo _toLower;
|
|
||||||
|
|
||||||
public StringEqualsConditionOperator()
|
|
||||||
{
|
|
||||||
_toLower = typeof(string).GetMethod("ToLower", new Type[] { });
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(string)};
|
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(string)};
|
||||||
|
|
||||||
public override string Description => "Equals";
|
public override string Description => "Equals";
|
||||||
public override string Icon => "Equal";
|
public override string Icon => "Equal";
|
||||||
|
|
||||||
public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
|
public override bool Evaluate(object a, object b)
|
||||||
{
|
{
|
||||||
return Expression.Equal(Expression.Call(leftSide, _toLower), Expression.Call(rightSide, _toLower));
|
var aString = (string) a;
|
||||||
|
var bString = (string) b;
|
||||||
|
|
||||||
|
return string.Equals(aString, bString, StringComparison.InvariantCultureIgnoreCase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,33 +1,21 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
namespace Artemis.Core.DefaultTypes
|
||||||
{
|
{
|
||||||
internal class StringNotContainsConditionOperator : ConditionOperator
|
internal class StringNotContainsConditionOperator : ConditionOperator
|
||||||
{
|
{
|
||||||
private readonly MethodInfo _contains;
|
|
||||||
private readonly MethodInfo _toLower;
|
|
||||||
|
|
||||||
public StringNotContainsConditionOperator()
|
|
||||||
{
|
|
||||||
_toLower = typeof(string).GetMethod("ToLower", new Type[] { });
|
|
||||||
_contains = typeof(string).GetMethod("Contains", new[] {typeof(string)});
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(string)};
|
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(string)};
|
||||||
|
|
||||||
public override string Description => "Does not contain";
|
public override string Description => "Does not contain";
|
||||||
public override string Icon => "FormatStrikethrough";
|
public override string Icon => "FormatStrikethrough";
|
||||||
|
|
||||||
public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
|
public override bool Evaluate(object a, object b)
|
||||||
{
|
{
|
||||||
var notContains = Expression.Equal(
|
var aString = (string) a;
|
||||||
Expression.Call(Expression.Call(leftSide, _toLower), _contains, Expression.Call(rightSide, _toLower)),
|
var bString = (string) b;
|
||||||
Expression.Constant(false)
|
|
||||||
);
|
return bString != null && aString != null && !aString.Contains(bString, StringComparison.InvariantCultureIgnoreCase);
|
||||||
return AddNullChecks(leftSide, rightSide, notContains);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,27 +1,21 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
namespace Artemis.Core.DefaultTypes
|
||||||
{
|
{
|
||||||
internal class StringNotEqualConditionOperator : ConditionOperator
|
internal class StringNotEqualConditionOperator : ConditionOperator
|
||||||
{
|
{
|
||||||
private readonly MethodInfo _toLower;
|
|
||||||
|
|
||||||
public StringNotEqualConditionOperator()
|
|
||||||
{
|
|
||||||
_toLower = typeof(string).GetMethod("ToLower", new Type[] { });
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(string)};
|
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(string)};
|
||||||
|
|
||||||
public override string Description => "Does not equal";
|
public override string Description => "Does not equal";
|
||||||
public override string Icon => "NotEqualVariant";
|
public override string Icon => "NotEqualVariant";
|
||||||
|
|
||||||
public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
|
public override bool Evaluate(object a, object b)
|
||||||
{
|
{
|
||||||
return Expression.NotEqual(Expression.Call(leftSide, _toLower), Expression.Call(rightSide, _toLower));
|
var aString = (string) a;
|
||||||
|
var bString = (string) b;
|
||||||
|
|
||||||
|
return !string.Equals(aString, bString, StringComparison.InvariantCultureIgnoreCase);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,24 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
|
||||||
{
|
|
||||||
internal class StringNullConditionOperator : ConditionOperator
|
|
||||||
{
|
|
||||||
public StringNullConditionOperator()
|
|
||||||
{
|
|
||||||
SupportsRightSide = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(string)};
|
|
||||||
|
|
||||||
public override string Description => "Is null";
|
|
||||||
public override string Icon => "Null";
|
|
||||||
|
|
||||||
public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
|
|
||||||
{
|
|
||||||
return Expression.Equal(leftSide, Expression.Constant(null, leftSide.Type));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -1,33 +1,21 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
namespace Artemis.Core.DefaultTypes
|
||||||
{
|
{
|
||||||
internal class StringStartsWithConditionOperator : ConditionOperator
|
internal class StringStartsWithConditionOperator : ConditionOperator
|
||||||
{
|
{
|
||||||
private readonly MethodInfo _startsWith;
|
|
||||||
private readonly MethodInfo _toLower;
|
|
||||||
|
|
||||||
public StringStartsWithConditionOperator()
|
|
||||||
{
|
|
||||||
_toLower = typeof(string).GetMethod("ToLower", new Type[] { });
|
|
||||||
_startsWith = typeof(string).GetMethod("StartsWith", new[] {typeof(string)});
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(string)};
|
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(string)};
|
||||||
|
|
||||||
public override string Description => "Starts with";
|
public override string Description => "Starts with";
|
||||||
public override string Icon => "ContainStart";
|
public override string Icon => "ContainStart";
|
||||||
|
|
||||||
public override BinaryExpression CreateExpression(Expression leftSide, Expression rightSide)
|
public override bool Evaluate(object a, object b)
|
||||||
{
|
{
|
||||||
var startsWith = Expression.Equal(
|
var aString = (string) a;
|
||||||
Expression.Call(Expression.Call(leftSide, _toLower), _startsWith, Expression.Call(rightSide, _toLower)),
|
var bString = (string) b;
|
||||||
Expression.Constant(true)
|
|
||||||
);
|
return bString != null && aString != null && aString.StartsWith(bString, StringComparison.InvariantCultureIgnoreCase);
|
||||||
return AddNullChecks(leftSide, rightSide, startsWith);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,7 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
|
||||||
|
|
||||||
namespace Artemis.Core
|
namespace Artemis.Core
|
||||||
{
|
{
|
||||||
@ -47,40 +46,10 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Creates a binary expression comparing two types
|
/// Evaluates the operator on a and b
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="leftSide">The parameter on the left side of the expression</param>
|
/// <param name="a">The parameter on the left side of the expression</param>
|
||||||
/// <param name="rightSide">The parameter on the right side of the expression</param>
|
/// <param name="b">The parameter on the right side of the expression</param>
|
||||||
/// <returns></returns>
|
public abstract bool Evaluate(object a, object b);
|
||||||
public abstract BinaryExpression CreateExpression(Expression leftSide, Expression rightSide);
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Wraps the provided expression in null-checks for the left and right side
|
|
||||||
/// <para>
|
|
||||||
/// The resulting expression body looks like:
|
|
||||||
/// <code>(a == null && b == null) || ((a != null && b != null) && <expression>)</code>
|
|
||||||
/// </para>
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="leftSide">The left side to check for nulls</param>
|
|
||||||
/// <param name="rightSide">The right side to check for nulls</param>
|
|
||||||
/// <param name="expression">The expression to wrap</param>
|
|
||||||
/// <returns>The wrapped expression</returns>
|
|
||||||
protected BinaryExpression AddNullChecks(Expression leftSide, Expression rightSide, BinaryExpression expression)
|
|
||||||
{
|
|
||||||
var nullConst = Expression.Constant(null);
|
|
||||||
return Expression.OrElse(
|
|
||||||
Expression.AndAlso(
|
|
||||||
Expression.Equal(leftSide, nullConst),
|
|
||||||
Expression.Equal(rightSide, nullConst)
|
|
||||||
),
|
|
||||||
Expression.AndAlso(
|
|
||||||
Expression.AndAlso(
|
|
||||||
Expression.NotEqual(leftSide, nullConst),
|
|
||||||
Expression.NotEqual(rightSide, nullConst)
|
|
||||||
),
|
|
||||||
expression
|
|
||||||
)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,15 +77,8 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public object RightStaticValue { get; private set; }
|
public object RightStaticValue { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
public Func<object, object> LeftSideAccessor { get; set; }
|
||||||
/// Gets the compiled expression that evaluates this predicate
|
public Func<object, object> RightSideAccessor { get; set; }
|
||||||
/// </summary>
|
|
||||||
public Func<object, bool> CompiledListPredicate { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the compiled expression that evaluates this predicate on an external right-side data model
|
|
||||||
/// </summary>
|
|
||||||
public Func<object, DataModel, bool> CompiledExternalListPredicate { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the left side of the predicate
|
/// Updates the left side of the predicate
|
||||||
@ -215,28 +208,26 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region IDisposable
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
ConditionOperatorStore.ConditionOperatorAdded -= ConditionOperatorStoreOnConditionOperatorAdded;
|
|
||||||
ConditionOperatorStore.ConditionOperatorRemoved -= ConditionOperatorStoreOnConditionOperatorRemoved;
|
|
||||||
|
|
||||||
base.Dispose(disposing);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
internal override bool EvaluateObject(object target)
|
internal override bool EvaluateObject(object target)
|
||||||
{
|
{
|
||||||
if (PredicateType == ListRightSideType.Static && CompiledListPredicate != null)
|
if (Operator == null || LeftSideAccessor == null || PredicateType != ListRightSideType.Static && RightSideAccessor == null)
|
||||||
return CompiledListPredicate(target);
|
return false;
|
||||||
if (PredicateType == ListRightSideType.DynamicList && CompiledListPredicate != null)
|
|
||||||
return CompiledListPredicate(target);
|
// Compare with a static value
|
||||||
if (PredicateType == ListRightSideType.Dynamic && CompiledExternalListPredicate != null)
|
if (PredicateType == ListRightSideType.Static)
|
||||||
return CompiledExternalListPredicate(target, RightDataModel);
|
{
|
||||||
|
if (!DataModelConditionList.ListType.IsValueType && RightStaticValue == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return Operator.Evaluate(LeftSideAccessor(target), RightStaticValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare with dynamic values
|
||||||
|
if (PredicateType == ListRightSideType.Dynamic)
|
||||||
|
return Operator.Evaluate(LeftSideAccessor(target), RightSideAccessor(RightDataModel));
|
||||||
|
if (PredicateType == ListRightSideType.DynamicList)
|
||||||
|
return Operator.Evaluate(LeftSideAccessor(target), RightSideAccessor(target));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -308,6 +299,8 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
private void Initialize()
|
private void Initialize()
|
||||||
{
|
{
|
||||||
|
DataModelStore.DataModelAdded += DataModelStoreOnDataModelAdded;
|
||||||
|
DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved;
|
||||||
ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded;
|
ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded;
|
||||||
ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved;
|
ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved;
|
||||||
|
|
||||||
@ -375,18 +368,16 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
private void CreateExpression()
|
private void CreateExpression()
|
||||||
{
|
{
|
||||||
CompiledListPredicate = null;
|
|
||||||
|
|
||||||
if (Operator == null)
|
if (Operator == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If the operator does not support a right side, create a static expression because the right side will simply be null
|
// If the operator does not support a right side, create a static expression because the right side will simply be null
|
||||||
if (PredicateType == ListRightSideType.DynamicList && Operator.SupportsRightSide)
|
if (PredicateType == ListRightSideType.DynamicList && Operator.SupportsRightSide)
|
||||||
CreateDynamicListExpression();
|
CreateDynamicListAccessors();
|
||||||
else if (PredicateType == ListRightSideType.Dynamic && Operator.SupportsRightSide)
|
else if (PredicateType == ListRightSideType.Dynamic && Operator.SupportsRightSide)
|
||||||
CreateDynamicExpression();
|
CreateDynamicAccessors();
|
||||||
else
|
else
|
||||||
CreateStaticExpression();
|
CreateStaticAccessors();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ValidateOperator()
|
private void ValidateOperator()
|
||||||
@ -452,7 +443,7 @@ namespace Artemis.Core
|
|||||||
RightStaticValue = null;
|
RightStaticValue = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateDynamicListExpression()
|
private void CreateDynamicListAccessors()
|
||||||
{
|
{
|
||||||
if (LeftPropertyPath == null || RightPropertyPath == null || Operator == null)
|
if (LeftPropertyPath == null || RightPropertyPath == null || Operator == null)
|
||||||
return;
|
return;
|
||||||
@ -467,12 +458,11 @@ namespace Artemis.Core
|
|||||||
if (rightSideAccessor.Type != leftSideAccessor.Type)
|
if (rightSideAccessor.Type != leftSideAccessor.Type)
|
||||||
rightSideAccessor = Expression.Convert(rightSideAccessor, leftSideAccessor.Type);
|
rightSideAccessor = Expression.Convert(rightSideAccessor, leftSideAccessor.Type);
|
||||||
|
|
||||||
var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideAccessor);
|
LeftSideAccessor = Expression.Lambda<Func<object, object>>(leftSideAccessor, leftSideParameter).Compile();
|
||||||
var lambda = Expression.Lambda<Func<object, bool>>(conditionExpression, leftSideParameter);
|
RightSideAccessor = Expression.Lambda<Func<object, object>>(rightSideAccessor, leftSideParameter).Compile();
|
||||||
CompiledListPredicate = lambda.Compile();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateDynamicExpression()
|
private void CreateDynamicAccessors()
|
||||||
{
|
{
|
||||||
if (LeftPropertyPath == null || RightPropertyPath == null || RightDataModel == null || Operator == null)
|
if (LeftPropertyPath == null || RightPropertyPath == null || RightDataModel == null || Operator == null)
|
||||||
return;
|
return;
|
||||||
@ -480,19 +470,18 @@ namespace Artemis.Core
|
|||||||
// List accessors share the same parameter because a list always contains one item per entry
|
// List accessors share the same parameter because a list always contains one item per entry
|
||||||
var leftSideParameter = Expression.Parameter(typeof(object), "listItem");
|
var leftSideParameter = Expression.Parameter(typeof(object), "listItem");
|
||||||
var leftSideAccessor = CreateListAccessor(LeftPropertyPath, leftSideParameter);
|
var leftSideAccessor = CreateListAccessor(LeftPropertyPath, leftSideParameter);
|
||||||
var rightSideAccessor = CreateAccessor(RightDataModel, RightPropertyPath, "right", out var rightSideParameter);
|
var rightSideAccessor = ExpressionUtilities.CreateDataModelAccessor(RightDataModel, RightPropertyPath, "right", out var rightSideParameter);
|
||||||
|
|
||||||
// A conversion may be required if the types differ
|
// A conversion may be required if the types differ
|
||||||
// This can cause issues if the DataModelConditionOperator wasn't accurate in it's supported types but that is not a concern here
|
// This can cause issues if the DataModelConditionOperator wasn't accurate in it's supported types but that is not a concern here
|
||||||
if (rightSideAccessor.Type != leftSideAccessor.Type)
|
if (rightSideAccessor.Type != leftSideAccessor.Type)
|
||||||
rightSideAccessor = Expression.Convert(rightSideAccessor, leftSideAccessor.Type);
|
rightSideAccessor = Expression.Convert(rightSideAccessor, leftSideAccessor.Type);
|
||||||
|
|
||||||
var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideAccessor);
|
LeftSideAccessor = Expression.Lambda<Func<object, object>>(leftSideAccessor, leftSideParameter).Compile();
|
||||||
var lambda = Expression.Lambda<Func<object, DataModel, bool>>(conditionExpression, leftSideParameter, rightSideParameter);
|
RightSideAccessor = Expression.Lambda<Func<object, object>>(rightSideAccessor, rightSideParameter).Compile();
|
||||||
CompiledExternalListPredicate = lambda.Compile();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateStaticExpression()
|
private void CreateStaticAccessors()
|
||||||
{
|
{
|
||||||
if (!DataModelConditionList.IsPrimitiveList && LeftPropertyPath == null || Operator == null)
|
if (!DataModelConditionList.IsPrimitiveList && LeftPropertyPath == null || Operator == null)
|
||||||
return;
|
return;
|
||||||
@ -503,43 +492,51 @@ namespace Artemis.Core
|
|||||||
? Expression.Convert(leftSideParameter, DataModelConditionList.ListType)
|
? Expression.Convert(leftSideParameter, DataModelConditionList.ListType)
|
||||||
: CreateListAccessor(LeftPropertyPath, leftSideParameter);
|
: CreateListAccessor(LeftPropertyPath, leftSideParameter);
|
||||||
|
|
||||||
// If the left side is a value type but the input is empty, this isn't a valid expression
|
LeftSideAccessor = Expression.Lambda<Func<object, object>>(leftSideAccessor, leftSideParameter).Compile();
|
||||||
if (leftSideAccessor.Type.IsValueType && RightStaticValue == null)
|
RightSideAccessor = null;
|
||||||
return;
|
|
||||||
|
|
||||||
// If the right side value is null, the constant type cannot be inferred and must be provided manually
|
|
||||||
var rightSideConstant = RightStaticValue != null
|
|
||||||
? Expression.Constant(Convert.ChangeType(RightStaticValue, leftSideAccessor.Type))
|
|
||||||
: Expression.Constant(null, leftSideAccessor.Type);
|
|
||||||
|
|
||||||
var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideConstant);
|
|
||||||
var lambda = Expression.Lambda<Func<object, bool>>(conditionExpression, leftSideParameter);
|
|
||||||
CompiledListPredicate = lambda.Compile();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expression CreateListAccessor(string path, ParameterExpression listParameter)
|
private Expression CreateListAccessor(string path, ParameterExpression listParameter)
|
||||||
{
|
{
|
||||||
return path.Split('.').Aggregate<string, Expression>(
|
// Create an expression that checks every part of the path for null
|
||||||
Expression.Convert(listParameter, DataModelConditionList.ListType), // Cast to the appropriate type
|
// In the same iteration, create the accessor
|
||||||
Expression.Property
|
Expression source = Expression.Convert(listParameter, DataModelConditionList.ListType);
|
||||||
);
|
return ExpressionUtilities.CreateNullCheckedAccessor(source, path);
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expression CreateAccessor(DataModel dataModel, string path, string parameterName, out ParameterExpression parameter)
|
#region IDisposable
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
var listType = dataModel.GetListTypeInPath(path);
|
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
|
||||||
if (listType != null)
|
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
|
||||||
throw new ArtemisCoreException($"Cannot create a regular accessor at path {path} because the path contains a list");
|
ConditionOperatorStore.ConditionOperatorAdded -= ConditionOperatorStoreOnConditionOperatorAdded;
|
||||||
|
ConditionOperatorStore.ConditionOperatorRemoved -= ConditionOperatorStoreOnConditionOperatorRemoved;
|
||||||
|
|
||||||
parameter = Expression.Parameter(typeof(object), parameterName + "DataModel");
|
base.Dispose(disposing);
|
||||||
return path.Split('.').Aggregate<string, Expression>(
|
|
||||||
Expression.Convert(parameter, dataModel.GetType()), // Cast to the appropriate type
|
|
||||||
Expression.Property
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Event handlers
|
#region Event handlers
|
||||||
|
|
||||||
|
private void DataModelStoreOnDataModelAdded(object sender, DataModelStoreEvent e)
|
||||||
|
{
|
||||||
|
var dataModel = e.Registration.DataModel;
|
||||||
|
if (dataModel.PluginInfo.Guid == Entity.RightDataModelGuid && dataModel.ContainsPath(Entity.RightPropertyPath))
|
||||||
|
UpdateRightSideDynamic(dataModel, Entity.RightPropertyPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DataModelStoreOnDataModelRemoved(object sender, DataModelStoreEvent e)
|
||||||
|
{
|
||||||
|
if (RightDataModel == e.Registration.DataModel)
|
||||||
|
{
|
||||||
|
RightSideAccessor = null;
|
||||||
|
RightDataModel = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private void ConditionOperatorStoreOnConditionOperatorAdded(object sender, ConditionOperatorStoreEvent e)
|
private void ConditionOperatorStoreOnConditionOperatorAdded(object sender, ConditionOperatorStoreEvent e)
|
||||||
{
|
{
|
||||||
var conditionOperator = e.Registration.ConditionOperator;
|
var conditionOperator = e.Registration.ConditionOperator;
|
||||||
@ -552,7 +549,6 @@ namespace Artemis.Core
|
|||||||
if (e.Registration.ConditionOperator != Operator)
|
if (e.Registration.ConditionOperator != Operator)
|
||||||
return;
|
return;
|
||||||
Operator = null;
|
Operator = null;
|
||||||
CompiledListPredicate = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -73,15 +73,8 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public object RightStaticValue { get; private set; }
|
public object RightStaticValue { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
public Func<object, object> LeftSideAccessor { get; set; }
|
||||||
/// Gets the compiled function that evaluates this predicate if it of a dynamic <see cref="PredicateType" />
|
public Func<object, object> RightSideAccessor { get; set; }
|
||||||
/// </summary>
|
|
||||||
public Func<DataModel, DataModel, bool> CompiledDynamicPredicate { 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 DataModelConditionPredicateEntity Entity { get; set; }
|
internal DataModelConditionPredicateEntity Entity { get; set; }
|
||||||
|
|
||||||
@ -109,7 +102,7 @@ namespace Artemis.Core
|
|||||||
ValidateOperator();
|
ValidateOperator();
|
||||||
ValidateRightSide();
|
ValidateRightSide();
|
||||||
|
|
||||||
CreateExpression();
|
CreateAccessors();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -134,7 +127,7 @@ namespace Artemis.Core
|
|||||||
RightDataModel = dataModel;
|
RightDataModel = dataModel;
|
||||||
RightPropertyPath = path;
|
RightPropertyPath = path;
|
||||||
|
|
||||||
CreateExpression();
|
CreateAccessors();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -167,7 +160,7 @@ namespace Artemis.Core
|
|||||||
else
|
else
|
||||||
RightStaticValue = null;
|
RightStaticValue = null;
|
||||||
|
|
||||||
CreateExpression();
|
CreateAccessors();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -180,7 +173,7 @@ namespace Artemis.Core
|
|||||||
if (conditionOperator == null)
|
if (conditionOperator == null)
|
||||||
{
|
{
|
||||||
Operator = null;
|
Operator = null;
|
||||||
CreateExpression();
|
CreateAccessors();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,16 +192,28 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
Operator = conditionOperator;
|
Operator = conditionOperator;
|
||||||
CreateExpression();
|
CreateAccessors();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool Evaluate()
|
public override bool Evaluate()
|
||||||
{
|
{
|
||||||
if (CompiledDynamicPredicate != null)
|
if (Operator == null || LeftSideAccessor == null || PredicateType != ProfileRightSideType.Static && RightSideAccessor == null)
|
||||||
return CompiledDynamicPredicate(LeftDataModel, RightDataModel);
|
return false;
|
||||||
if (CompiledStaticPredicate != null)
|
|
||||||
return CompiledStaticPredicate(LeftDataModel);
|
// Compare with a static value
|
||||||
|
if (PredicateType == ProfileRightSideType.Static)
|
||||||
|
{
|
||||||
|
var leftSideValue = LeftSideAccessor(LeftDataModel);
|
||||||
|
if (leftSideValue.GetType().IsValueType && RightStaticValue == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return Operator.Evaluate(leftSideValue, RightStaticValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Compare with dynamic values
|
||||||
|
if (PredicateType == ProfileRightSideType.Dynamic)
|
||||||
|
return Operator.Evaluate(LeftSideAccessor(LeftDataModel), RightSideAccessor(RightDataModel));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -314,17 +319,14 @@ namespace Artemis.Core
|
|||||||
return Entity;
|
return Entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateExpression()
|
private void CreateAccessors()
|
||||||
{
|
{
|
||||||
CompiledDynamicPredicate = null;
|
|
||||||
CompiledStaticPredicate = null;
|
|
||||||
|
|
||||||
if (Operator == null)
|
if (Operator == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If the operator does not support a right side, create a static expression because the right side will simply be null
|
// 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)
|
if (PredicateType == ProfileRightSideType.Dynamic && Operator.SupportsRightSide)
|
||||||
CreateDynamicExpression();
|
CreateDynamicAccessors();
|
||||||
else
|
else
|
||||||
CreateStaticExpression();
|
CreateStaticExpression();
|
||||||
}
|
}
|
||||||
@ -360,22 +362,21 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateDynamicExpression()
|
private void CreateDynamicAccessors()
|
||||||
{
|
{
|
||||||
if (LeftDataModel == null || RightDataModel == null || Operator == null)
|
if (LeftDataModel == null || RightDataModel == null || Operator == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var leftSideAccessor = CreateAccessor(LeftDataModel, LeftPropertyPath, "left", out var leftSideParameter);
|
var leftSideAccessor = ExpressionUtilities.CreateDataModelAccessor(LeftDataModel, LeftPropertyPath, "left", out var leftSideParameter);
|
||||||
var rightSideAccessor = CreateAccessor(RightDataModel, RightPropertyPath, "right", out var rightSideParameter);
|
var rightSideAccessor = ExpressionUtilities.CreateDataModelAccessor(RightDataModel, RightPropertyPath, "right", out var rightSideParameter);
|
||||||
|
|
||||||
// A conversion may be required if the types differ
|
// A conversion may be required if the types differ
|
||||||
// This can cause issues if the DataModelConditionOperator wasn't accurate in it's supported types but that is not a concern here
|
// This can cause issues if the DataModelConditionOperator wasn't accurate in it's supported types but that is not a concern here
|
||||||
if (rightSideAccessor.Type != leftSideAccessor.Type)
|
if (rightSideAccessor.Type != leftSideAccessor.Type)
|
||||||
rightSideAccessor = Expression.Convert(rightSideAccessor, leftSideAccessor.Type);
|
rightSideAccessor = Expression.Convert(rightSideAccessor, leftSideAccessor.Type);
|
||||||
|
|
||||||
var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideAccessor);
|
LeftSideAccessor = Expression.Lambda<Func<object, object>>(leftSideAccessor, leftSideParameter).Compile();
|
||||||
var lambda = Expression.Lambda<Func<DataModel, DataModel, bool>>(conditionExpression, leftSideParameter, rightSideParameter);
|
RightSideAccessor = Expression.Lambda<Func<object, object>>(rightSideAccessor, rightSideParameter).Compile();
|
||||||
CompiledDynamicPredicate = lambda.Compile();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateStaticExpression()
|
private void CreateStaticExpression()
|
||||||
@ -383,34 +384,19 @@ namespace Artemis.Core
|
|||||||
if (LeftDataModel == null || Operator == null)
|
if (LeftDataModel == null || Operator == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var leftSideAccessor = CreateAccessor(LeftDataModel, LeftPropertyPath, "left", out var leftSideParameter);
|
var leftSideAccessor = Expression.Convert(
|
||||||
|
ExpressionUtilities.CreateDataModelAccessor(LeftDataModel, LeftPropertyPath, "left", out var leftSideParameter),
|
||||||
|
typeof(object)
|
||||||
|
);
|
||||||
|
|
||||||
// If the left side is a value type but the input is empty, this isn't a valid expression
|
// 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)
|
if (leftSideAccessor.Type.IsValueType && RightStaticValue == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If the right side value is null, the constant type cannot be inferred and must be provided manually
|
LeftSideAccessor = Expression.Lambda<Func<object, object>>(leftSideAccessor, leftSideParameter).Compile();
|
||||||
var rightSideConstant = RightStaticValue != null
|
RightSideAccessor = null;
|
||||||
? Expression.Constant(Convert.ChangeType(RightStaticValue, leftSideAccessor.Type))
|
|
||||||
: Expression.Constant(null, leftSideAccessor.Type);
|
|
||||||
|
|
||||||
var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideConstant);
|
|
||||||
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);
|
|
||||||
if (listType != null)
|
|
||||||
throw new ArtemisCoreException($"Cannot create a regular accessor at path {path} because the path contains a list");
|
|
||||||
|
|
||||||
parameter = Expression.Parameter(typeof(object), parameterName + "DataModel");
|
|
||||||
return path.Split('.').Aggregate<string, Expression>(
|
|
||||||
Expression.Convert(parameter, dataModel.GetType()), // Cast to the appropriate type
|
|
||||||
Expression.Property
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Event handlers
|
#region Event handlers
|
||||||
|
|
||||||
@ -427,13 +413,13 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
if (LeftDataModel == e.Registration.DataModel)
|
if (LeftDataModel == e.Registration.DataModel)
|
||||||
{
|
{
|
||||||
CompiledDynamicPredicate = null;
|
LeftSideAccessor = null;
|
||||||
LeftDataModel = null;
|
LeftDataModel = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RightDataModel == e.Registration.DataModel)
|
if (RightDataModel == e.Registration.DataModel)
|
||||||
{
|
{
|
||||||
CompiledDynamicPredicate = null;
|
RightSideAccessor = null;
|
||||||
RightDataModel = null;
|
RightDataModel = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -451,8 +437,6 @@ namespace Artemis.Core
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Operator = null;
|
Operator = null;
|
||||||
CompiledStaticPredicate = null;
|
|
||||||
CompiledDynamicPredicate = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|||||||
@ -37,6 +37,8 @@ namespace Artemis.Core
|
|||||||
throw new ObjectDisposedException("ConditionalDataBinding");
|
throw new ObjectDisposedException("ConditionalDataBinding");
|
||||||
|
|
||||||
var condition = Conditions.FirstOrDefault(c => c.Evaluate());
|
var condition = Conditions.FirstOrDefault(c => c.Evaluate());
|
||||||
|
if (condition != null)
|
||||||
|
Console.WriteLine();
|
||||||
return condition == null ? baseValue : condition.Value;
|
return condition == null ? baseValue : condition.Value;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -108,8 +110,7 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
|
||||||
#region Storage
|
#region Storage
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
|
|||||||
@ -300,25 +300,14 @@ namespace Artemis.Core
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// If the right side value is null, the constant type cannot be inferred and must be provided based on the data binding target
|
// If the right side value is null, the constant type cannot be inferred and must be provided based on the data binding target
|
||||||
var parameterAccessor = CreateAccessor(ParameterDataModel, ParameterPropertyPath, "parameter", out var rightSideParameter);
|
var parameterAccessor = ExpressionUtilities.CreateDataModelAccessor(
|
||||||
|
ParameterDataModel, ParameterPropertyPath, "parameter", out var rightSideParameter
|
||||||
|
);
|
||||||
var lambda = Expression.Lambda<Func<DataModel, object>>(Expression.Convert(parameterAccessor, typeof(object)), rightSideParameter);
|
var lambda = Expression.Lambda<Func<DataModel, object>>(Expression.Convert(parameterAccessor, typeof(object)), rightSideParameter);
|
||||||
CompiledParameterAccessor = lambda.Compile();
|
CompiledParameterAccessor = lambda.Compile();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expression CreateAccessor(DataModel dataModel, string path, string parameterName, out ParameterExpression parameter)
|
|
||||||
{
|
|
||||||
var listType = dataModel.GetListTypeInPath(path);
|
|
||||||
if (listType != null)
|
|
||||||
throw new ArtemisCoreException($"Cannot create a regular accessor at path {path} because the path contains a list");
|
|
||||||
|
|
||||||
parameter = Expression.Parameter(typeof(object), parameterName + "DataModel");
|
|
||||||
return path.Split('.').Aggregate<string, Expression>(
|
|
||||||
Expression.Convert(parameter, dataModel.GetType()), // Cast to the appropriate type
|
|
||||||
Expression.Property
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Event handlers
|
#region Event handlers
|
||||||
|
|
||||||
private void DataBindingModifierTypeStoreOnDataBindingModifierAdded(object sender, DataBindingModifierTypeStoreEvent e)
|
private void DataBindingModifierTypeStoreOnDataBindingModifierAdded(object sender, DataBindingModifierTypeStoreEvent e)
|
||||||
|
|||||||
@ -59,7 +59,11 @@ namespace Artemis.Core.Services
|
|||||||
RegisterConditionOperator(Constants.CorePluginInfo, new StringNotContainsConditionOperator());
|
RegisterConditionOperator(Constants.CorePluginInfo, new StringNotContainsConditionOperator());
|
||||||
RegisterConditionOperator(Constants.CorePluginInfo, new StringStartsWithConditionOperator());
|
RegisterConditionOperator(Constants.CorePluginInfo, new StringStartsWithConditionOperator());
|
||||||
RegisterConditionOperator(Constants.CorePluginInfo, new StringEndsWithConditionOperator());
|
RegisterConditionOperator(Constants.CorePluginInfo, new StringEndsWithConditionOperator());
|
||||||
RegisterConditionOperator(Constants.CorePluginInfo, new StringNullConditionOperator());
|
|
||||||
|
// Null checks, at the bottom
|
||||||
|
// TODO: Implement a priority mechanism
|
||||||
|
RegisterConditionOperator(Constants.CorePluginInfo, new NullConditionOperator());
|
||||||
|
RegisterConditionOperator(Constants.CorePluginInfo, new NotNullConditionOperator());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
37
src/Artemis.Core/Utilities/ExpressionUtilities.cs
Normal file
37
src/Artemis.Core/Utilities/ExpressionUtilities.cs
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
using System.Linq.Expressions;
|
||||||
|
using Artemis.Core.DataModelExpansions;
|
||||||
|
|
||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
internal static class ExpressionUtilities
|
||||||
|
{
|
||||||
|
internal static Expression CreateDataModelAccessor(DataModel dataModel, string path, string parameterName, out ParameterExpression parameter)
|
||||||
|
{
|
||||||
|
parameter = Expression.Parameter(typeof(object), parameterName + "DataModel");
|
||||||
|
|
||||||
|
// Create an expression that checks every part of the path for null
|
||||||
|
// In the same iteration, create the accessor
|
||||||
|
Expression source = Expression.Convert(parameter, dataModel.GetType());
|
||||||
|
return CreateNullCheckedAccessor(source, path);
|
||||||
|
}
|
||||||
|
|
||||||
|
internal static Expression CreateNullCheckedAccessor(Expression source, string path)
|
||||||
|
{
|
||||||
|
// Create an expression that checks every part of the path for null
|
||||||
|
// In the same iteration, create the accessor
|
||||||
|
Expression condition = null;
|
||||||
|
foreach (var memberName in path.Split('.'))
|
||||||
|
{
|
||||||
|
var notNull = Expression.NotEqual(source, Expression.Constant(null));
|
||||||
|
condition = condition != null ? Expression.AndAlso(condition, notNull) : notNull;
|
||||||
|
source = Expression.PropertyOrField(source, memberName);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (condition == null)
|
||||||
|
throw new ArtemisCoreException($"Failed to create a null-check for path {path}");
|
||||||
|
|
||||||
|
// Combine the null check and the accessor in a conditional statement that returns the default for the type if null
|
||||||
|
return Expression.Condition(condition, source, Expression.Default(source.Type));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using Artemis.Core.DataModelExpansions;
|
using Artemis.Core.DataModelExpansions;
|
||||||
using Artemis.Plugins.DataModelExpansions.TestData.DataModels;
|
using Artemis.Plugins.DataModelExpansions.TestData.DataModels;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
|
|||||||
@ -128,10 +128,15 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
if (DataModelConditionPredicate.Operator == null)
|
if (DataModelConditionPredicate.Operator == null)
|
||||||
DataModelConditionPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType)));
|
DataModelConditionPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType)));
|
||||||
SelectedOperator = DataModelConditionPredicate.Operator;
|
SelectedOperator = DataModelConditionPredicate.Operator;
|
||||||
|
if (!SelectedOperator.SupportsRightSide)
|
||||||
|
{
|
||||||
|
DisposeRightSideStatic();
|
||||||
|
DisposeRightSideDynamic();
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure the right side has the proper VM
|
// Ensure the right side has the proper VM
|
||||||
var targetType = LeftSideSelectionViewModel?.SelectedPropertyViewModel?.PropertyInfo?.PropertyType;
|
var targetType = LeftSideSelectionViewModel?.SelectedPropertyViewModel?.PropertyInfo?.PropertyType;
|
||||||
if (DataModelConditionPredicate.PredicateType == ProfileRightSideType.Dynamic)
|
if (DataModelConditionPredicate.PredicateType == ProfileRightSideType.Dynamic && SelectedOperator.SupportsRightSide)
|
||||||
{
|
{
|
||||||
DisposeRightSideStatic();
|
DisposeRightSideStatic();
|
||||||
if (RightSideSelectionViewModel == null)
|
if (RightSideSelectionViewModel == null)
|
||||||
@ -147,7 +152,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
);
|
);
|
||||||
RightSideSelectionViewModel.FilterTypes = new[] {targetType};
|
RightSideSelectionViewModel.FilterTypes = new[] {targetType};
|
||||||
}
|
}
|
||||||
else
|
else if (SelectedOperator.SupportsRightSide)
|
||||||
{
|
{
|
||||||
DisposeRightSideDynamic();
|
DisposeRightSideDynamic();
|
||||||
if (RightSideInputViewModel == null)
|
if (RightSideInputViewModel == null)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user