mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 19:36:28 +00:00
Conditions - Big refactor to prepare for conditional databindings
Conditions - Improved list-condition functionality
This commit is contained in:
1 parent
a646ff95ed
commit
8aba0a55ec
55 files changed
+1382
-1608
No files matched your search
@@ -62,7 +62,7 @@ namespace Artemis.Core
|
||||
internal abstract bool EvaluateObject(object target);
|
||||
|
||||
internal abstract void Save();
|
||||
internal abstract DisplayConditionPartEntity GetEntity();
|
||||
internal abstract DataModelConditionPartEntity GetEntity();
|
||||
|
||||
#region IDisposable
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ namespace Artemis.Core
|
||||
public DataModelConditionGroup(DataModelConditionPart parent)
|
||||
{
|
||||
Parent = parent;
|
||||
Entity = new DisplayConditionGroupEntity();
|
||||
Entity = new DataModelConditionGroupEntity();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -28,7 +28,7 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
/// <param name="parent"></param>
|
||||
/// <param name="entity"></param>
|
||||
public DataModelConditionGroup(DataModelConditionPart parent, DisplayConditionGroupEntity entity)
|
||||
public DataModelConditionGroup(DataModelConditionPart parent, DataModelConditionGroupEntity entity)
|
||||
{
|
||||
Parent = parent;
|
||||
Entity = entity;
|
||||
@@ -36,13 +36,13 @@ namespace Artemis.Core
|
||||
|
||||
foreach (var childEntity in Entity.Children)
|
||||
{
|
||||
if (childEntity is DisplayConditionGroupEntity groupEntity)
|
||||
if (childEntity is DataModelConditionGroupEntity groupEntity)
|
||||
AddChild(new DataModelConditionGroup(this, groupEntity));
|
||||
else if (childEntity is DisplayConditionListEntity listEntity)
|
||||
else if (childEntity is DataModelConditionListEntity listEntity)
|
||||
AddChild(new DataModelConditionList(this, listEntity));
|
||||
else if (childEntity is DisplayConditionPredicateEntity predicateEntity)
|
||||
else if (childEntity is DataModelConditionPredicateEntity predicateEntity)
|
||||
AddChild(new DataModelConditionPredicate(this, predicateEntity));
|
||||
else if (childEntity is DisplayConditionListPredicateEntity listPredicateEntity)
|
||||
else if (childEntity is DataModelConditionListPredicateEntity listPredicateEntity)
|
||||
AddChild(new DataModelConditionListPredicate(this, listPredicateEntity));
|
||||
}
|
||||
}
|
||||
@@ -52,13 +52,13 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public BooleanOperator BooleanOperator { get; set; }
|
||||
|
||||
internal DisplayConditionGroupEntity Entity { get; set; }
|
||||
internal DataModelConditionGroupEntity Entity { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Evaluate()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DisplayConditionGroup");
|
||||
throw new ObjectDisposedException("DataModelConditionGroup");
|
||||
|
||||
// Empty groups are always true
|
||||
if (Children.Count == 0)
|
||||
@@ -100,7 +100,7 @@ namespace Artemis.Core
|
||||
internal override bool EvaluateObject(object target)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DisplayConditionGroup");
|
||||
throw new ObjectDisposedException("DataModelConditionGroup");
|
||||
|
||||
// Empty groups are always true
|
||||
if (Children.Count == 0)
|
||||
@@ -129,7 +129,7 @@ namespace Artemis.Core
|
||||
child.Save();
|
||||
}
|
||||
|
||||
internal override DisplayConditionPartEntity GetEntity()
|
||||
internal override DataModelConditionPartEntity GetEntity()
|
||||
{
|
||||
return Entity;
|
||||
}
|
||||
|
||||
@@ -22,12 +22,12 @@ namespace Artemis.Core
|
||||
public DataModelConditionList(DataModelConditionPart parent)
|
||||
{
|
||||
Parent = parent;
|
||||
Entity = new DisplayConditionListEntity();
|
||||
Entity = new DataModelConditionListEntity();
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
internal DataModelConditionList(DataModelConditionPart parent, DisplayConditionListEntity entity)
|
||||
internal DataModelConditionList(DataModelConditionPart parent, DataModelConditionListEntity entity)
|
||||
{
|
||||
Parent = parent;
|
||||
Entity = entity;
|
||||
@@ -36,7 +36,7 @@ namespace Artemis.Core
|
||||
Initialize();
|
||||
}
|
||||
|
||||
internal DisplayConditionListEntity Entity { get; set; }
|
||||
internal DataModelConditionListEntity Entity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list operator
|
||||
@@ -62,7 +62,7 @@ namespace Artemis.Core
|
||||
public override bool Evaluate()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DisplayConditionList");
|
||||
throw new ObjectDisposedException("DataModelConditionList");
|
||||
|
||||
if (CompiledListAccessor == null)
|
||||
return false;
|
||||
@@ -78,7 +78,7 @@ namespace Artemis.Core
|
||||
public void UpdateList(DataModel dataModel, string path)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DisplayConditionList");
|
||||
throw new ObjectDisposedException("DataModelConditionList");
|
||||
|
||||
if (dataModel != null && path == null)
|
||||
throw new ArtemisCoreException("If a data model is provided, a path is also required");
|
||||
@@ -129,7 +129,7 @@ namespace Artemis.Core
|
||||
internal override bool EvaluateObject(object target)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DisplayConditionList");
|
||||
throw new ObjectDisposedException("DataModelConditionList");
|
||||
|
||||
if (!Children.Any())
|
||||
return false;
|
||||
@@ -166,7 +166,7 @@ namespace Artemis.Core
|
||||
child.Save();
|
||||
}
|
||||
|
||||
internal override DisplayConditionPartEntity GetEntity()
|
||||
internal override DataModelConditionPartEntity GetEntity()
|
||||
{
|
||||
return Entity;
|
||||
}
|
||||
@@ -189,7 +189,7 @@ namespace Artemis.Core
|
||||
CreateExpression();
|
||||
|
||||
// There should only be one child and it should be a group
|
||||
if (Entity.Children.SingleOrDefault() is DisplayConditionGroupEntity rootGroup)
|
||||
if (Entity.Children.SingleOrDefault() is DataModelConditionGroupEntity rootGroup)
|
||||
AddChild(new DataModelConditionGroup(this, rootGroup));
|
||||
else
|
||||
{
|
||||
@@ -201,12 +201,14 @@ namespace Artemis.Core
|
||||
private void CreateExpression()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DisplayConditionList");
|
||||
throw new ObjectDisposedException("DataModelConditionList");
|
||||
|
||||
var parameter = Expression.Parameter(typeof(object), "listDataModel");
|
||||
var accessor = ListPropertyPath.Split('.').Aggregate<string, Expression>(
|
||||
Expression.Convert(parameter, ListDataModel.GetType()),
|
||||
(expression, s) => Expression.Convert(Expression.Property(expression, s), typeof(IList)));
|
||||
Expression.Property
|
||||
);
|
||||
accessor = Expression.Convert(accessor, typeof(IList));
|
||||
|
||||
var lambda = Expression.Lambda<Func<object, IList>>(accessor, parameter);
|
||||
CompiledListAccessor = lambda.Compile();
|
||||
|
||||
@@ -18,32 +18,32 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
/// <param name="parent"></param>
|
||||
/// <param name="predicateType"></param>
|
||||
public DataModelConditionListPredicate(DataModelConditionPart parent, ProfileRightSideType predicateType)
|
||||
public DataModelConditionListPredicate(DataModelConditionPart parent, ListRightSideType predicateType)
|
||||
{
|
||||
Parent = parent;
|
||||
PredicateType = predicateType;
|
||||
Entity = new DisplayConditionListPredicateEntity();
|
||||
Entity = new DataModelConditionListPredicateEntity();
|
||||
|
||||
ApplyParentList();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
internal DataModelConditionListPredicate(DataModelConditionPart parent, DisplayConditionListPredicateEntity entity)
|
||||
internal DataModelConditionListPredicate(DataModelConditionPart parent, DataModelConditionListPredicateEntity entity)
|
||||
{
|
||||
Parent = parent;
|
||||
Entity = entity;
|
||||
PredicateType = (ProfileRightSideType) entity.PredicateType;
|
||||
PredicateType = (ListRightSideType) entity.PredicateType;
|
||||
|
||||
ApplyParentList();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
internal DisplayConditionListPredicateEntity Entity { get; set; }
|
||||
internal DataModelConditionListPredicateEntity Entity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the predicate type
|
||||
/// </summary>
|
||||
public ProfileRightSideType PredicateType { get; set; }
|
||||
public ListRightSideType PredicateType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the operator
|
||||
@@ -55,6 +55,11 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public Type ListType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the list contains primitives
|
||||
/// </summary>
|
||||
public bool IsPrimitiveList { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently used instance of the list data model
|
||||
/// </summary>
|
||||
@@ -70,6 +75,12 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public string LeftPropertyPath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the currently used instance of the right side data model
|
||||
/// <para>Note: This is null when using a path inside the list</para>
|
||||
/// </summary>
|
||||
public DataModel RightDataModel { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path of the right property in the <see cref="ListType" />
|
||||
/// </summary>
|
||||
@@ -77,21 +88,28 @@ namespace Artemis.Core
|
||||
|
||||
/// <summary>
|
||||
/// Gets the right static value, only used it <see cref="PredicateType" /> is
|
||||
/// <see cref="ProfileRightSideType.Static" />
|
||||
/// <see cref="ListRightSideType.Static" />
|
||||
/// </summary>
|
||||
public object RightStaticValue { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the compiled function that evaluates this predicate
|
||||
/// Gets the compiled expression that evaluates this predicate
|
||||
/// </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>
|
||||
/// Updates the left side of the predicate
|
||||
/// </summary>
|
||||
/// <param name="path">The path pointing to the left side value inside the list</param>
|
||||
public void UpdateLeftSide(string path)
|
||||
{
|
||||
if (IsPrimitiveList)
|
||||
throw new ArtemisCoreException("Cannot apply a left side to a predicate inside a primitive list");
|
||||
if (!ListContainsInnerPath(path))
|
||||
throw new ArtemisCoreException($"List type {ListType.Name} does not contain path {path}");
|
||||
|
||||
@@ -103,7 +121,8 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the right side of the predicate, makes the predicate dynamic and re-compiles the expression
|
||||
/// Updates the right side of the predicate using a path to a value inside the list item, makes the predicate dynamic
|
||||
/// and re-compiles the expression
|
||||
/// </summary>
|
||||
/// <param name="path">The path pointing to the right side value inside the list</param>
|
||||
public void UpdateRightSideDynamic(string path)
|
||||
@@ -111,7 +130,33 @@ namespace Artemis.Core
|
||||
if (!ListContainsInnerPath(path))
|
||||
throw new ArtemisCoreException($"List type {ListType.Name} does not contain path {path}");
|
||||
|
||||
PredicateType = ProfileRightSideType.Dynamic;
|
||||
PredicateType = ListRightSideType.Dynamic;
|
||||
RightPropertyPath = path;
|
||||
|
||||
CreateExpression();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the right side of the predicate using path to a value in a data model, makes the predicate dynamic and
|
||||
/// re-compiles the expression
|
||||
/// </summary>
|
||||
/// <param name="dataModel"></param>
|
||||
/// <param name="path">The path pointing to the right side value inside the list</param>
|
||||
public void UpdateRightSideDynamic(DataModel dataModel, string path)
|
||||
{
|
||||
if (dataModel != null && path == null)
|
||||
throw new ArtemisCoreException("If a data model is provided, a path is also required");
|
||||
if (dataModel == null && path != null)
|
||||
throw new ArtemisCoreException("If path is provided, a data model is also required");
|
||||
|
||||
if (dataModel != null)
|
||||
{
|
||||
if (!dataModel.ContainsPath(path))
|
||||
throw new ArtemisCoreException($"Data model of type {dataModel.GetType().Name} does not contain a property at path '{path}'");
|
||||
}
|
||||
|
||||
PredicateType = ListRightSideType.DynamicExternal;
|
||||
RightDataModel = dataModel;
|
||||
RightPropertyPath = path;
|
||||
|
||||
CreateExpression();
|
||||
@@ -123,7 +168,7 @@ namespace Artemis.Core
|
||||
/// <param name="staticValue">The right side value to use</param>
|
||||
public void UpdateRightSideStatic(object staticValue)
|
||||
{
|
||||
PredicateType = ProfileRightSideType.Static;
|
||||
PredicateType = ListRightSideType.Static;
|
||||
RightPropertyPath = null;
|
||||
|
||||
SetStaticValue(staticValue);
|
||||
@@ -163,11 +208,36 @@ namespace Artemis.Core
|
||||
return false;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines whether the provided path is contained inside the list
|
||||
/// </summary>
|
||||
/// <param name="path">The path to evaluate</param>
|
||||
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;
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
|
||||
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
|
||||
ConditionOperatorStore.ConditionOperatorAdded -= ConditionOperatorStoreOnConditionOperatorAdded;
|
||||
ConditionOperatorStore.ConditionOperatorRemoved -= ConditionOperatorStoreOnConditionOperatorRemoved;
|
||||
|
||||
@@ -205,9 +275,14 @@ namespace Artemis.Core
|
||||
throw new ArtemisCoreException($"Data model of type {dataModel.GetType().Name} does not contain a list at path '{path}'");
|
||||
|
||||
ListType = listType;
|
||||
IsPrimitiveList = listType.IsPrimitive || listType.IsEnum || listType == typeof(string);
|
||||
}
|
||||
else
|
||||
{
|
||||
ListType = null;
|
||||
IsPrimitiveList = true;
|
||||
}
|
||||
|
||||
|
||||
ListDataModel = dataModel;
|
||||
ListPropertyPath = path;
|
||||
@@ -220,6 +295,7 @@ namespace Artemis.Core
|
||||
CreateExpression();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the condition part on the given target
|
||||
/// </summary>
|
||||
@@ -229,26 +305,14 @@ namespace Artemis.Core
|
||||
/// </param>
|
||||
internal override bool EvaluateObject(object target)
|
||||
{
|
||||
return CompiledListPredicate != null && CompiledListPredicate(target);
|
||||
}
|
||||
if (PredicateType == ListRightSideType.Static && CompiledListPredicate != null)
|
||||
return CompiledListPredicate(target);
|
||||
if (PredicateType == ListRightSideType.Dynamic && CompiledListPredicate != null)
|
||||
return CompiledListPredicate(target);
|
||||
if (PredicateType == ListRightSideType.DynamicExternal && CompiledExternalListPredicate != null)
|
||||
return CompiledExternalListPredicate(target, RightDataModel);
|
||||
|
||||
internal 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;
|
||||
return false;
|
||||
}
|
||||
|
||||
internal Type GetTypeAtInnerPath(string path)
|
||||
@@ -280,6 +344,8 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
Entity.LeftPropertyPath = LeftPropertyPath;
|
||||
|
||||
Entity.RightDataModelGuid = RightDataModel?.PluginInfo?.Guid;
|
||||
Entity.RightPropertyPath = RightPropertyPath;
|
||||
Entity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue);
|
||||
|
||||
@@ -290,13 +356,15 @@ namespace Artemis.Core
|
||||
}
|
||||
}
|
||||
|
||||
internal override DisplayConditionPartEntity GetEntity()
|
||||
internal override DataModelConditionPartEntity GetEntity()
|
||||
{
|
||||
return Entity;
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
DataModelStore.DataModelAdded += DataModelStoreOnDataModelAdded;
|
||||
DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved;
|
||||
ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded;
|
||||
ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved;
|
||||
|
||||
@@ -313,13 +381,20 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
// Right side dynamic
|
||||
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightPropertyPath != null)
|
||||
if (PredicateType == ListRightSideType.Dynamic && Entity.RightPropertyPath != null)
|
||||
{
|
||||
if (ListContainsInnerPath(Entity.RightPropertyPath))
|
||||
UpdateRightSideDynamic(Entity.RightPropertyPath);
|
||||
}
|
||||
// Right side dynamic using an external data model
|
||||
else if (PredicateType == ListRightSideType.Dynamic && Entity.RightDataModelGuid != null && Entity.RightPropertyPath != null)
|
||||
{
|
||||
var dataModel = DataModelStore.Get(Entity.RightDataModelGuid.Value)?.DataModel;
|
||||
if (dataModel != null && dataModel.ContainsPath(Entity.RightPropertyPath))
|
||||
UpdateRightSideDynamic(dataModel, Entity.RightPropertyPath);
|
||||
}
|
||||
// Right side static
|
||||
else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null)
|
||||
else if (PredicateType == ListRightSideType.Static && Entity.RightStaticValue != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
@@ -363,10 +438,12 @@ namespace Artemis.Core
|
||||
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)
|
||||
if (PredicateType == ListRightSideType.Dynamic && Operator.SupportsRightSide)
|
||||
CreateDynamicExpression();
|
||||
|
||||
CreateStaticExpression();
|
||||
else if (PredicateType == ListRightSideType.DynamicExternal && Operator.SupportsRightSide)
|
||||
CreateDynamicExternalExpression();
|
||||
else
|
||||
CreateStaticExpression();
|
||||
}
|
||||
|
||||
private void ValidateOperator()
|
||||
@@ -382,7 +459,7 @@ namespace Artemis.Core
|
||||
private void ValidateRightSide()
|
||||
{
|
||||
var leftSideType = GetTypeAtInnerPath(LeftPropertyPath);
|
||||
if (PredicateType == ProfileRightSideType.Dynamic)
|
||||
if (PredicateType == ListRightSideType.Dynamic)
|
||||
{
|
||||
if (RightPropertyPath == null)
|
||||
return;
|
||||
@@ -391,6 +468,15 @@ namespace Artemis.Core
|
||||
if (!leftSideType.IsCastableFrom(rightSideType))
|
||||
UpdateRightSideDynamic(null);
|
||||
}
|
||||
else if (PredicateType == ListRightSideType.DynamicExternal)
|
||||
{
|
||||
if (RightDataModel == null)
|
||||
return;
|
||||
|
||||
var rightSideType = RightDataModel.GetTypeAtPath(RightPropertyPath);
|
||||
if (!leftSideType.IsCastableFrom(rightSideType))
|
||||
UpdateRightSideDynamic(null, null);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (RightStaticValue != null && leftSideType.IsCastableFrom(RightStaticValue.GetType()))
|
||||
@@ -434,7 +520,7 @@ namespace Artemis.Core
|
||||
var rightSideAccessor = CreateListAccessor(RightPropertyPath, leftSideParameter);
|
||||
|
||||
// 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
|
||||
// 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)
|
||||
rightSideAccessor = Expression.Convert(rightSideAccessor, leftSideAccessor.Type);
|
||||
|
||||
@@ -443,14 +529,34 @@ namespace Artemis.Core
|
||||
CompiledListPredicate = lambda.Compile();
|
||||
}
|
||||
|
||||
private void CreateStaticExpression()
|
||||
private void CreateDynamicExternalExpression()
|
||||
{
|
||||
if (LeftPropertyPath == null || Operator == null)
|
||||
if (LeftPropertyPath == null || RightPropertyPath == null || RightDataModel == null || Operator == null)
|
||||
return;
|
||||
|
||||
// List accessors share the same parameter because a list always contains one item per entry
|
||||
var leftSideParameter = Expression.Parameter(typeof(object), "listItem");
|
||||
var leftSideAccessor = CreateListAccessor(LeftPropertyPath, leftSideParameter);
|
||||
var rightSideAccessor = CreateAccessor(RightDataModel, RightPropertyPath, "right", out var rightSideParameter);
|
||||
|
||||
// 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
|
||||
if (rightSideAccessor.Type != leftSideAccessor.Type)
|
||||
rightSideAccessor = Expression.Convert(rightSideAccessor, leftSideAccessor.Type);
|
||||
|
||||
var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideAccessor);
|
||||
var lambda = Expression.Lambda<Func<object, DataModel, bool>>(conditionExpression, leftSideParameter, rightSideParameter);
|
||||
CompiledExternalListPredicate = lambda.Compile();
|
||||
}
|
||||
|
||||
private void CreateStaticExpression()
|
||||
{
|
||||
if (!IsPrimitiveList && LeftPropertyPath == null || Operator == null)
|
||||
return;
|
||||
|
||||
// List accessors share the same parameter because a list always contains one item per entry
|
||||
var leftSideParameter = Expression.Parameter(typeof(object), "listItem");
|
||||
var leftSideAccessor = IsPrimitiveList ? Expression.Convert(leftSideParameter, ListType) : CreateListAccessor(LeftPropertyPath, 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)
|
||||
@@ -458,7 +564,7 @@ namespace Artemis.Core
|
||||
|
||||
// If the right side value is null, the constant type cannot be inferred and must be provided manually
|
||||
var rightSideConstant = RightStaticValue != null
|
||||
? Expression.Constant(RightStaticValue)
|
||||
? Expression.Constant(Convert.ChangeType(RightStaticValue, leftSideAccessor.Type))
|
||||
: Expression.Constant(null, leftSideAccessor.Type);
|
||||
|
||||
var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideConstant);
|
||||
@@ -474,8 +580,46 @@ namespace Artemis.Core
|
||||
);
|
||||
}
|
||||
|
||||
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
|
||||
|
||||
private void DataModelStoreOnDataModelAdded(object sender, DataModelStoreEvent e)
|
||||
{
|
||||
var dataModel = e.Registration.DataModel;
|
||||
if (dataModel.PluginInfo.Guid == Entity.ListDataModelGuid && dataModel.ContainsPath(Entity.ListPropertyPath))
|
||||
UpdateList(dataModel, Entity.LeftPropertyPath);
|
||||
if (dataModel.PluginInfo.Guid == Entity.RightDataModelGuid && dataModel.ContainsPath(Entity.RightPropertyPath))
|
||||
UpdateRightSideDynamic(dataModel, Entity.RightPropertyPath);
|
||||
}
|
||||
|
||||
private void DataModelStoreOnDataModelRemoved(object sender, DataModelStoreEvent e)
|
||||
{
|
||||
if (ListDataModel == e.Registration.DataModel)
|
||||
{
|
||||
CompiledListPredicate = null;
|
||||
CompiledExternalListPredicate = null;
|
||||
ListDataModel = null;
|
||||
}
|
||||
|
||||
if (RightDataModel == e.Registration.DataModel)
|
||||
{
|
||||
CompiledExternalListPredicate = null;
|
||||
RightDataModel = null;
|
||||
}
|
||||
}
|
||||
|
||||
private void ConditionOperatorStoreOnConditionOperatorAdded(object sender, ConditionOperatorStoreEvent e)
|
||||
{
|
||||
var conditionOperator = e.Registration.ConditionOperator;
|
||||
@@ -493,4 +637,25 @@ namespace Artemis.Core
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// An enum defining the right side type of a profile entity
|
||||
/// </summary>
|
||||
public enum ListRightSideType
|
||||
{
|
||||
/// <summary>
|
||||
/// A static right side value
|
||||
/// </summary>
|
||||
Static,
|
||||
|
||||
/// <summary>
|
||||
/// A dynamic right side value based on a path in the list
|
||||
/// </summary>
|
||||
Dynamic,
|
||||
|
||||
/// <summary>
|
||||
/// A dynamic right side value based on a path in a data model
|
||||
/// </summary>
|
||||
DynamicExternal
|
||||
}
|
||||
}
|
||||
@@ -23,12 +23,12 @@ namespace Artemis.Core
|
||||
{
|
||||
Parent = parent;
|
||||
PredicateType = predicateType;
|
||||
Entity = new DisplayConditionPredicateEntity();
|
||||
Entity = new DataModelConditionPredicateEntity();
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
internal DataModelConditionPredicate(DataModelConditionPart parent, DisplayConditionPredicateEntity entity)
|
||||
internal DataModelConditionPredicate(DataModelConditionPart parent, DataModelConditionPredicateEntity entity)
|
||||
{
|
||||
Parent = parent;
|
||||
Entity = entity;
|
||||
@@ -83,7 +83,7 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public Func<DataModel, bool> CompiledStaticPredicate { get; private set; }
|
||||
|
||||
internal DisplayConditionPredicateEntity Entity { get; set; }
|
||||
internal DataModelConditionPredicateEntity Entity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Updates the left side of the predicate
|
||||
@@ -309,7 +309,7 @@ namespace Artemis.Core
|
||||
}
|
||||
}
|
||||
|
||||
internal override DisplayConditionPartEntity GetEntity()
|
||||
internal override DataModelConditionPartEntity GetEntity()
|
||||
{
|
||||
return Entity;
|
||||
}
|
||||
@@ -369,7 +369,7 @@ namespace Artemis.Core
|
||||
var rightSideAccessor = CreateAccessor(RightDataModel, RightPropertyPath, "right", out var 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
|
||||
// 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)
|
||||
rightSideAccessor = Expression.Convert(rightSideAccessor, leftSideAccessor.Type);
|
||||
|
||||
@@ -391,7 +391,7 @@ namespace Artemis.Core
|
||||
|
||||
// If the right side value is null, the constant type cannot be inferred and must be provided manually
|
||||
var rightSideConstant = RightStaticValue != null
|
||||
? Expression.Constant(RightStaticValue)
|
||||
? Expression.Constant(Convert.ChangeType(RightStaticValue, leftSideAccessor.Type))
|
||||
: Expression.Constant(null, leftSideAccessor.Type);
|
||||
|
||||
var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideConstant);
|
||||
|
||||
@@ -72,6 +72,9 @@ namespace Artemis.Core
|
||||
{
|
||||
_disposed = true;
|
||||
|
||||
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
|
||||
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
|
||||
|
||||
foreach (var dataBindingModifier in Modifiers)
|
||||
dataBindingModifier.Dispose();
|
||||
}
|
||||
@@ -153,8 +156,7 @@ namespace Artemis.Core
|
||||
{
|
||||
return SourceDataModel?.GetTypeAtPath(SourcePropertyPath);
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Updates the source of the data binding and re-compiles the expression
|
||||
/// </summary>
|
||||
|
||||
@@ -49,8 +49,8 @@ namespace Artemis.Core
|
||||
DisplayContinuously = RenderElementEntity.DisplayContinuously;
|
||||
AlwaysFinishTimeline = RenderElementEntity.AlwaysFinishTimeline;
|
||||
|
||||
DataModelConditionGroup = RenderElementEntity.RootDisplayCondition != null
|
||||
? new DataModelConditionGroup(null, RenderElementEntity.RootDisplayCondition)
|
||||
DisplayCondition = RenderElementEntity.DisplayCondition != null
|
||||
? new DataModelConditionGroup(null, RenderElementEntity.DisplayCondition)
|
||||
: new DataModelConditionGroup(null);
|
||||
|
||||
ActivateEffects();
|
||||
@@ -82,8 +82,8 @@ namespace Artemis.Core
|
||||
}
|
||||
|
||||
// Conditions
|
||||
RenderElementEntity.RootDisplayCondition = DataModelConditionGroup?.Entity;
|
||||
DataModelConditionGroup?.Save();
|
||||
RenderElementEntity.DisplayCondition = DisplayCondition?.Entity;
|
||||
DisplayCondition?.Save();
|
||||
}
|
||||
|
||||
#region Properties
|
||||
@@ -392,22 +392,22 @@ namespace Artemis.Core
|
||||
private set => SetAndNotify(ref _displayConditionMet, value);
|
||||
}
|
||||
|
||||
private DataModelConditionGroup _dataModelConditionGroup;
|
||||
private DataModelConditionGroup _displayCondition;
|
||||
private TimeSpan _timelinePosition;
|
||||
private bool _displayConditionMet;
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the root display condition group
|
||||
/// </summary>
|
||||
public DataModelConditionGroup DataModelConditionGroup
|
||||
public DataModelConditionGroup DisplayCondition
|
||||
{
|
||||
get => _dataModelConditionGroup;
|
||||
set => SetAndNotify(ref _dataModelConditionGroup, value);
|
||||
get => _displayCondition;
|
||||
set => SetAndNotify(ref _displayCondition, value);
|
||||
}
|
||||
|
||||
public void UpdateDisplayCondition()
|
||||
{
|
||||
var conditionMet = DataModelConditionGroup == null || DataModelConditionGroup.Evaluate();
|
||||
var conditionMet = DisplayCondition == null || DisplayCondition.Evaluate();
|
||||
if (conditionMet && !DisplayConditionMet)
|
||||
TimelinePosition = TimeSpan.Zero;
|
||||
|
||||
|
||||
Reference in new issue
Block a user