Display conditions - Seperate list conditions into their own type

This commit is contained in:
Robert committed 2020-08-13 21:00:29 +02:00
1 parent f2f77da953
commit f359256ede
22 files changed
+692 -218

No files matched your search

@@ -12,19 +12,21 @@ namespace Artemis.Core.Models.Profile.Conditions
public DisplayConditionGroup(DisplayConditionPart parent)
{
Parent = parent;
DisplayConditionGroupEntity = new DisplayConditionGroupEntity();
Entity = new DisplayConditionGroupEntity();
}
public DisplayConditionGroup(DisplayConditionPart parent, DisplayConditionGroupEntity entity)
{
Parent = parent;
DisplayConditionGroupEntity = entity;
BooleanOperator = (BooleanOperator) DisplayConditionGroupEntity.BooleanOperator;
Entity = entity;
BooleanOperator = (BooleanOperator) Entity.BooleanOperator;
foreach (var childEntity in DisplayConditionGroupEntity.Children)
foreach (var childEntity in Entity.Children)
{
if (childEntity is DisplayConditionGroupEntity groupEntity)
AddChild(new DisplayConditionGroup(this, groupEntity));
else if (childEntity is DisplayConditionListEntity listEntity)
AddChild(new DisplayConditionList(this, listEntity));
else if (childEntity is DisplayConditionPredicateEntity predicateEntity)
AddChild(new DisplayConditionPredicate(this, predicateEntity));
else if (childEntity is DisplayConditionListPredicateEntity listPredicateEntity)
@@ -33,7 +35,7 @@ namespace Artemis.Core.Models.Profile.Conditions
}
public BooleanOperator BooleanOperator { get; set; }
public DisplayConditionGroupEntity DisplayConditionGroupEntity { get; set; }
public DisplayConditionGroupEntity Entity { get; set; }
public override bool Evaluate()
{
@@ -80,10 +82,10 @@ namespace Artemis.Core.Models.Profile.Conditions
internal override void ApplyToEntity()
{
DisplayConditionGroupEntity.BooleanOperator = (int) BooleanOperator;
Entity.BooleanOperator = (int) BooleanOperator;
DisplayConditionGroupEntity.Children.Clear();
DisplayConditionGroupEntity.Children.AddRange(Children.Select(c => c.GetEntity()));
Entity.Children.Clear();
Entity.Children.AddRange(Children.Select(c => c.GetEntity()));
foreach (var child in Children)
child.ApplyToEntity();
}
@@ -96,7 +98,7 @@ namespace Artemis.Core.Models.Profile.Conditions
internal override DisplayConditionPartEntity GetEntity()
{
return DisplayConditionGroupEntity;
return Entity;
}
}
@@ -0,0 +1,148 @@
using System;
using System.Collections;
using System.Linq;
using System.Linq.Expressions;
using Artemis.Core.Exceptions;
using Artemis.Core.Models.Profile.Conditions.Abstract;
using Artemis.Core.Plugins.Abstract.DataModels;
using Artemis.Core.Services.Interfaces;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract;
namespace Artemis.Core.Models.Profile.Conditions
{
public class DisplayConditionList : DisplayConditionPart
{
public DisplayConditionList(DisplayConditionPart parent)
{
Parent = parent;
Entity = new DisplayConditionListEntity();
// There is always a child root group, add it
AddChild(new DisplayConditionGroup(this));
}
public DisplayConditionList(DisplayConditionPart parent, DisplayConditionListEntity entity)
{
Parent = parent;
Entity = entity;
ListOperator = (ListOperator) entity.ListOperator;
// There should only be one child and it should be a group
var rootGroup = Entity.Children.SingleOrDefault() as DisplayConditionGroupEntity;
if (rootGroup == null)
{
Entity.Children.Clear();
AddChild(new DisplayConditionGroup(this));
}
else
AddChild(new DisplayConditionGroup(this, rootGroup));
}
public DisplayConditionListEntity Entity { get; set; }
public ListOperator ListOperator { get; set; }
public DataModel ListDataModel { get; private set; }
public string ListPropertyPath { get; private set; }
public override bool Evaluate()
{
if (CompiledListAccessor == null)
return false;
return EvaluateObject(CompiledListAccessor(ListDataModel));
}
public override bool EvaluateObject(object target)
{
if (!(target is IList list))
return false;
var objectList = list.Cast<object>();
return ListOperator switch
{
ListOperator.Any => objectList.Any(o => Children[0].EvaluateObject(o)),
ListOperator.All => objectList.All(o => Children[0].EvaluateObject(o)),
ListOperator.None => objectList.Any(o => !Children[0].EvaluateObject(o)),
ListOperator.Count => false,
_ => throw new ArgumentOutOfRangeException()
};
}
internal override void ApplyToEntity()
{
// Target list
Entity.ListDataModelGuid = ListDataModel?.PluginInfo?.Guid;
Entity.ListPropertyPath = ListPropertyPath;
// Operator
Entity.ListOperator = (int) ListOperator;
// Children
Entity.Children.Clear();
Entity.Children.AddRange(Children.Select(c => c.GetEntity()));
foreach (var child in Children)
child.ApplyToEntity();
}
internal override DisplayConditionPartEntity GetEntity()
{
return Entity;
}
internal override void Initialize(IDataModelService dataModelService)
{
// Target list
if (Entity.ListDataModelGuid != null)
{
var dataModel = dataModelService.GetPluginDataModelByGuid(Entity.ListDataModelGuid.Value);
if (dataModel != null && dataModel.ContainsPath(Entity.ListPropertyPath))
UpdateList(dataModel, Entity.ListPropertyPath);
}
// Children
var rootGroup = (DisplayConditionGroup) Children.Single();
rootGroup.Initialize(dataModelService);
}
public void UpdateList(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}'");
if (dataModel.GetListTypeAtPath(path) == null)
throw new ArtemisCoreException($"The path '{path}' does not contain a list");
}
ListDataModel = dataModel;
ListPropertyPath = path;
if (dataModel != null)
{
var parameter = Expression.Parameter(typeof(object), "listDataModel");
var accessor = path.Split('.').Aggregate<string, Expression>(
Expression.Convert(parameter, dataModel.GetType()),
(expression, s) => Expression.Convert(Expression.Property(expression, s), typeof(IList)));
var lambda = Expression.Lambda<Func<object, IList>>(accessor, parameter);
CompiledListAccessor = lambda.Compile();
}
}
public Func<object, IList> CompiledListAccessor { get; set; }
}
public enum ListOperator
{
Any,
All,
None,
Count
}
}
@@ -1,106 +1,47 @@
using System;
using System.Collections;
using System.Linq;
using System.Linq.Expressions;
using Artemis.Core.Exceptions;
using Artemis.Core.Extensions;
using Artemis.Core.Models.Profile.Conditions.Abstract;
using Artemis.Core.Plugins.Abstract.DataModels;
using Artemis.Core.Services.Interfaces;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract;
using Newtonsoft.Json;
namespace Artemis.Core.Models.Profile.Conditions
{
public class DisplayConditionListPredicate : DisplayConditionPart
{
public DisplayConditionListPredicate(DisplayConditionPart parent)
public DisplayConditionListPredicate(DisplayConditionPart parent, PredicateType predicateType)
{
Parent = parent;
DisplayConditionListPredicateEntity = new DisplayConditionListPredicateEntity();
// There is always a child root group, add it
AddChild(new DisplayConditionGroup(this));
PredicateType = predicateType;
Entity = new DisplayConditionListPredicateEntity();
}
public DisplayConditionListPredicate(DisplayConditionPart parent, DisplayConditionListPredicateEntity entity)
{
Parent = parent;
DisplayConditionListPredicateEntity = entity;
ListOperator = (ListOperator) entity.ListOperator;
// There should only be one child and it should be a group
var rootGroup = DisplayConditionListPredicateEntity.Children.SingleOrDefault() as DisplayConditionGroupEntity;
if (rootGroup == null)
{
DisplayConditionListPredicateEntity.Children.Clear();
AddChild(new DisplayConditionGroup(this));
}
else
AddChild(new DisplayConditionGroup(this, rootGroup));
Entity = entity;
PredicateType = (PredicateType) entity.PredicateType;
}
public DisplayConditionListPredicateEntity DisplayConditionListPredicateEntity { get; set; }
public DisplayConditionListPredicateEntity Entity { get; set; }
public ListOperator ListOperator { get; set; }
public PredicateType PredicateType { get; set; }
public DisplayConditionOperator Operator { get; private set; }
public Type ListType { get; private set; }
public DataModel ListDataModel { get; private set; }
public string ListPropertyPath { get; private set; }
public override bool Evaluate()
{
return EvaluateObject(CompiledListAccessor(ListDataModel));
}
public string LeftPropertyPath { get; private set; }
public string RightPropertyPath { get; private set; }
public object RightStaticValue { get; private set; }
public override bool EvaluateObject(object target)
{
if (!(target is IList list))
return false;
var objectList = list.Cast<object>();
return ListOperator switch
{
ListOperator.Any => objectList.Any(o => Children[0].EvaluateObject(o)),
ListOperator.All => objectList.All(o => Children[0].EvaluateObject(o)),
ListOperator.None => objectList.Any(o => !Children[0].EvaluateObject(o)),
ListOperator.Count => false,
_ => throw new ArgumentOutOfRangeException()
};
}
internal override void ApplyToEntity()
{
// Target list
DisplayConditionListPredicateEntity.ListDataModelGuid = ListDataModel?.PluginInfo?.Guid;
DisplayConditionListPredicateEntity.ListPropertyPath = ListPropertyPath;
// Operator
DisplayConditionListPredicateEntity.ListOperator = (int) ListOperator;
// Children
DisplayConditionListPredicateEntity.Children.Clear();
DisplayConditionListPredicateEntity.Children.AddRange(Children.Select(c => c.GetEntity()));
foreach (var child in Children)
child.ApplyToEntity();
}
internal override DisplayConditionPartEntity GetEntity()
{
return DisplayConditionListPredicateEntity;
}
internal override void Initialize(IDataModelService dataModelService)
{
// Target list
if (DisplayConditionListPredicateEntity.ListDataModelGuid != null)
{
var dataModel = dataModelService.GetPluginDataModelByGuid(DisplayConditionListPredicateEntity.ListDataModelGuid.Value);
if (dataModel != null && dataModel.ContainsPath(DisplayConditionListPredicateEntity.ListPropertyPath))
UpdateList(dataModel, DisplayConditionListPredicateEntity.ListPropertyPath);
}
// Children
var rootGroup = (DisplayConditionGroup) Children.Single();
rootGroup.Initialize(dataModelService);
}
public Func<object, bool> CompiledListPredicate { get; private set; }
public void UpdateList(DataModel dataModel, string path)
{
@@ -111,35 +52,322 @@ namespace Artemis.Core.Models.Profile.Conditions
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}'");
if (dataModel.GetListTypeAtPath(path) == null)
throw new ArtemisCoreException($"The path '{path}' does not contain a list");
var listType = dataModel.GetListTypeAtPath(path);
if (listType == null)
throw new ArtemisCoreException($"Data model of type {dataModel.GetType().Name} does not contain a list at path '{path}'");
ListType = listType;
}
else
{
ListType = null;
}
ListDataModel = dataModel;
ListPropertyPath = path;
if (dataModel != null)
{
var parameter = Expression.Parameter(typeof(object), "listDataModel");
var accessor = path.Split('.').Aggregate<string, Expression>(
Expression.Convert(parameter, dataModel.GetType()),
(expression, s) => Expression.Convert(Expression.Property(expression, s), typeof(IList)));
if (!ListContainsInnerPath(LeftPropertyPath))
LeftPropertyPath = null;
if (!ListContainsInnerPath(RightPropertyPath))
RightPropertyPath = null;
var lambda = Expression.Lambda<Func<object, IList>>(accessor, parameter);
CompiledListAccessor = lambda.Compile();
CreateExpression();
}
public void UpdateLeftSide(string path)
{
if (!ListContainsInnerPath(path))
throw new ArtemisCoreException($"List type {ListType.Name} does not contain path {path}");
LeftPropertyPath = path;
ValidateOperator();
ValidateRightSide();
CreateExpression();
}
public void UpdateRightSideDynamic(string path)
{
if (!ListContainsInnerPath(path))
throw new ArtemisCoreException($"List type {ListType.Name} does not contain path {path}");
PredicateType = PredicateType.Dynamic;
RightPropertyPath = path;
CreateExpression();
}
public void UpdateRightSideStatic(object staticValue)
{
PredicateType = PredicateType.Static;
RightPropertyPath = null;
SetStaticValue(staticValue);
CreateExpression();
}
public void UpdateOperator(DisplayConditionOperator displayConditionOperator)
{
if (displayConditionOperator == null)
{
Operator = null;
return;
}
if (LeftPropertyPath == null)
{
Operator = displayConditionOperator;
return;
}
var leftType = GetTypeAtInnerPath(LeftPropertyPath);
if (displayConditionOperator.SupportsType(leftType))
Operator = displayConditionOperator;
CreateExpression();
}
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 == PredicateType.Dynamic && Operator.SupportsRightSide)
CreateDynamicExpression();
CreateStaticExpression();
}
internal override void ApplyToEntity()
{
Entity.PredicateType = (int) PredicateType;
Entity.ListDataModelGuid = ListDataModel?.PluginInfo?.Guid;
Entity.ListPropertyPath = ListPropertyPath;
Entity.LeftPropertyPath = LeftPropertyPath;
Entity.RightPropertyPath = RightPropertyPath;
Entity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue);
Entity.OperatorPluginGuid = Operator?.PluginInfo?.Guid;
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
if (Entity.LeftPropertyPath != null && ListContainsInnerPath(Entity.LeftPropertyPath))
UpdateLeftSide(Entity.LeftPropertyPath);
// Operator
if (Entity.OperatorPluginGuid != null)
{
var conditionOperator = dataModelService.GetConditionOperator(Entity.OperatorPluginGuid.Value, Entity.OperatorType);
if (conditionOperator != null)
UpdateOperator(conditionOperator);
}
// Right side dynamic
if (PredicateType == PredicateType.Dynamic && Entity.RightPropertyPath != null)
{
if (ListContainsInnerPath(Entity.RightPropertyPath))
UpdateLeftSide(Entity.LeftPropertyPath);
}
// Right side static
else if (PredicateType == PredicateType.Static && Entity.RightStaticValue != null)
{
try
{
if (LeftPropertyPath != null)
{
// Use the left side type so JSON.NET has a better idea what to do
var leftSideType = GetTypeAtInnerPath(LeftPropertyPath);
object rightSideValue;
try
{
rightSideValue = JsonConvert.DeserializeObject(Entity.RightStaticValue, leftSideType);
}
// If deserialization fails, use the type's default
catch (JsonSerializationException e)
{
dataModelService.LogListPredicateDeserializationFailure(this, e);
rightSideValue = Activator.CreateInstance(leftSideType);
}
UpdateRightSideStatic(rightSideValue);
}
else
{
// Hope for the best... we must infer the type from JSON now
UpdateRightSideStatic(JsonConvert.DeserializeObject(Entity.RightStaticValue));
}
}
catch (JsonException e)
{
dataModelService.LogListPredicateDeserializationFailure(this, e);
}
}
}
public Func<object, IList> CompiledListAccessor { get; set; }
}
internal override DisplayConditionPartEntity GetEntity()
{
return Entity;
}
public enum ListOperator
{
Any,
All,
None,
Count
private void ValidateOperator()
{
if (LeftPropertyPath == null || Operator == null)
return;
var leftSideType = GetTypeAtInnerPath(LeftPropertyPath);
if (!Operator.SupportsType(leftSideType))
Operator = null;
}
private void ValidateRightSide()
{
var leftSideType = GetTypeAtInnerPath(LeftPropertyPath);
if (PredicateType == PredicateType.Dynamic)
{
if (RightPropertyPath == null)
return;
var rightSideType = GetTypeAtInnerPath(RightPropertyPath);
if (!leftSideType.IsCastableFrom(rightSideType))
UpdateRightSideDynamic(null);
}
else
{
if (RightStaticValue != null && leftSideType.IsCastableFrom(RightStaticValue.GetType()))
UpdateRightSideStatic(RightStaticValue);
else
UpdateRightSideStatic(null);
}
}
private void SetStaticValue(object staticValue)
{
// If the left side is empty simply apply the value, any validation will wait
if (LeftPropertyPath == null)
{
RightStaticValue = staticValue;
return;
}
var leftSideType = GetTypeAtInnerPath(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 (LeftPropertyPath == null || RightPropertyPath == 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 = 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
if (rightSideAccessor.Type != leftSideAccessor.Type)
rightSideAccessor = Expression.Convert(rightSideAccessor, leftSideAccessor.Type);
var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideAccessor);
var lambda = Expression.Lambda<Func<object, bool>>(conditionExpression, leftSideParameter);
CompiledListPredicate = lambda.Compile();
}
private void CreateStaticExpression()
{
if (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 = 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)
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(RightStaticValue)
: 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)
{
return path.Split('.').Aggregate<string, Expression>(
Expression.Convert(listParameter, ListType), // Cast to the appropriate type
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;
}
}
}
@@ -18,17 +18,17 @@ namespace Artemis.Core.Models.Profile.Conditions
{
Parent = parent;
PredicateType = predicateType;
DisplayConditionPredicateEntity = new DisplayConditionPredicateEntity();
Entity = new DisplayConditionPredicateEntity();
}
public DisplayConditionPredicate(DisplayConditionPart parent, DisplayConditionPredicateEntity entity)
{
Parent = parent;
DisplayConditionPredicateEntity = entity;
Entity = entity;
PredicateType = (PredicateType) entity.PredicateType;
}
public DisplayConditionPredicateEntity DisplayConditionPredicateEntity { get; set; }
public DisplayConditionPredicateEntity Entity { get; set; }
public PredicateType PredicateType { get; set; }
public DisplayConditionOperator Operator { get; private set; }
@@ -38,6 +38,8 @@ namespace Artemis.Core.Models.Profile.Conditions
public DataModel RightDataModel { get; private set; }
public string RightPropertyPath { get; private set; }
public object RightStaticValue { get; private set; }
public DataModel ListDataModel { get; private set; }
public string ListPropertyPath { get; private set; }
public Func<DataModel, DataModel, bool> CompiledDynamicPredicate { get; private set; }
public Func<DataModel, bool> CompiledStaticPredicate { get; private set; }
@@ -135,16 +137,16 @@ namespace Artemis.Core.Models.Profile.Conditions
internal override void ApplyToEntity()
{
DisplayConditionPredicateEntity.PredicateType = (int) PredicateType;
DisplayConditionPredicateEntity.LeftDataModelGuid = LeftDataModel?.PluginInfo?.Guid;
DisplayConditionPredicateEntity.LeftPropertyPath = LeftPropertyPath;
Entity.PredicateType = (int) PredicateType;
Entity.LeftDataModelGuid = LeftDataModel?.PluginInfo?.Guid;
Entity.LeftPropertyPath = LeftPropertyPath;
DisplayConditionPredicateEntity.RightDataModelGuid = RightDataModel?.PluginInfo?.Guid;
DisplayConditionPredicateEntity.RightPropertyPath = RightPropertyPath;
DisplayConditionPredicateEntity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue);
Entity.RightDataModelGuid = RightDataModel?.PluginInfo?.Guid;
Entity.RightPropertyPath = RightPropertyPath;
Entity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue);
DisplayConditionPredicateEntity.OperatorPluginGuid = Operator?.PluginInfo?.Guid;
DisplayConditionPredicateEntity.OperatorType = Operator?.GetType().Name;
Entity.OperatorPluginGuid = Operator?.PluginInfo?.Guid;
Entity.OperatorType = Operator?.GetType().Name;
}
public override bool Evaluate()
@@ -168,30 +170,30 @@ namespace Artemis.Core.Models.Profile.Conditions
internal override void Initialize(IDataModelService dataModelService)
{
// Left side
if (DisplayConditionPredicateEntity.LeftDataModelGuid != null)
if (Entity.LeftDataModelGuid != null)
{
var dataModel = dataModelService.GetPluginDataModelByGuid(DisplayConditionPredicateEntity.LeftDataModelGuid.Value);
if (dataModel != null && dataModel.ContainsPath(DisplayConditionPredicateEntity.LeftPropertyPath))
UpdateLeftSide(dataModel, DisplayConditionPredicateEntity.LeftPropertyPath);
var dataModel = dataModelService.GetPluginDataModelByGuid(Entity.LeftDataModelGuid.Value);
if (dataModel != null && dataModel.ContainsPath(Entity.LeftPropertyPath))
UpdateLeftSide(dataModel, Entity.LeftPropertyPath);
}
// Operator
if (DisplayConditionPredicateEntity.OperatorPluginGuid != null)
if (Entity.OperatorPluginGuid != null)
{
var conditionOperator = dataModelService.GetConditionOperator(DisplayConditionPredicateEntity.OperatorPluginGuid.Value, DisplayConditionPredicateEntity.OperatorType);
var conditionOperator = dataModelService.GetConditionOperator(Entity.OperatorPluginGuid.Value, Entity.OperatorType);
if (conditionOperator != null)
UpdateOperator(conditionOperator);
}
// Right side dynamic
if (PredicateType == PredicateType.Dynamic && DisplayConditionPredicateEntity.RightDataModelGuid != null)
if (PredicateType == PredicateType.Dynamic && Entity.RightDataModelGuid != null)
{
var dataModel = dataModelService.GetPluginDataModelByGuid(DisplayConditionPredicateEntity.RightDataModelGuid.Value);
if (dataModel != null && dataModel.ContainsPath(DisplayConditionPredicateEntity.RightPropertyPath))
UpdateRightSide(dataModel, DisplayConditionPredicateEntity.RightPropertyPath);
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 && DisplayConditionPredicateEntity.RightStaticValue != null)
else if (PredicateType == PredicateType.Static && Entity.RightStaticValue != null)
{
try
{
@@ -203,12 +205,12 @@ namespace Artemis.Core.Models.Profile.Conditions
try
{
rightSideValue = JsonConvert.DeserializeObject(DisplayConditionPredicateEntity.RightStaticValue, leftSideType);
rightSideValue = JsonConvert.DeserializeObject(Entity.RightStaticValue, leftSideType);
}
// If deserialization fails, use the type's default
catch (JsonSerializationException e)
{
dataModelService.LogDeserializationFailure(this, e);
dataModelService.LogPredicateDeserializationFailure(this, e);
rightSideValue = Activator.CreateInstance(leftSideType);
}
@@ -217,7 +219,7 @@ namespace Artemis.Core.Models.Profile.Conditions
else
{
// Hope for the best...
UpdateRightSide(JsonConvert.DeserializeObject(DisplayConditionPredicateEntity.RightStaticValue));
UpdateRightSide(JsonConvert.DeserializeObject(Entity.RightStaticValue));
}
}
catch (JsonReaderException)
@@ -230,7 +232,7 @@ namespace Artemis.Core.Models.Profile.Conditions
internal override DisplayConditionPartEntity GetEntity()
{
return DisplayConditionPredicateEntity;
return Entity;
}
private void ValidateOperator()
@@ -292,7 +294,7 @@ namespace Artemis.Core.Models.Profile.Conditions
if (LeftDataModel == null || RightDataModel == null || Operator == null)
return;
var isListExpression = LeftDataModel.GetListTypeAtPath(LeftPropertyPath) != null;
var isListExpression = LeftDataModel.GetListTypeInPath(LeftPropertyPath) != null;
Expression leftSideAccessor;
Expression rightSideAccessor;
@@ -335,7 +337,7 @@ namespace Artemis.Core.Models.Profile.Conditions
if (LeftDataModel == null || Operator == null)
return;
var isListExpression = LeftDataModel.GetListTypeAtPath(LeftPropertyPath) != null;
var isListExpression = LeftDataModel.GetListTypeInPath(LeftPropertyPath) != null;
Expression leftSideAccessor;
ParameterExpression leftSideParameter;
@@ -373,7 +375,7 @@ namespace Artemis.Core.Models.Profile.Conditions
private Expression CreateAccessor(DataModel dataModel, string path, string parameterName, out ParameterExpression parameter)
{
var listType = dataModel.GetListTypeAtPath(path);
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");
@@ -386,7 +388,7 @@ namespace Artemis.Core.Models.Profile.Conditions
private Expression CreateListAccessor(DataModel dataModel, string path, ParameterExpression listParameter)
{
var listType = dataModel.GetListTypeAtPath(path);
var listType = dataModel.GetListTypeInPath(path);
if (listType == null)
throw new ArtemisCoreException($"Cannot create a list accessor at path {path} because the path does not contain a list");
+1 -1
View File
@@ -299,7 +299,7 @@ namespace Artemis.Core.Models.Profile
ApplyRenderElementToEntity();
// Conditions
RenderElementEntity.RootDisplayCondition = DisplayConditionGroup?.DisplayConditionGroupEntity;
RenderElementEntity.RootDisplayCondition = DisplayConditionGroup?.Entity;
DisplayConditionGroup?.ApplyToEntity();
}
+1 -1
View File
@@ -206,7 +206,7 @@ namespace Artemis.Core.Models.Profile
}
// Conditions
RenderElementEntity.RootDisplayCondition = DisplayConditionGroup?.DisplayConditionGroupEntity;
RenderElementEntity.RootDisplayCondition = DisplayConditionGroup?.Entity;
DisplayConditionGroup?.ApplyToEntity();
}
@@ -31,14 +31,8 @@ namespace Artemis.Core.Plugins.Abstract.DataModels
var current = GetType();
foreach (var part in parts)
{
var property = current.GetProperty(part);
// For lists, look into the list type instead of the list itself
if (property != null && typeof(IList).IsAssignableFrom(property.PropertyType))
current = property.PropertyType.GetGenericArguments()[0];
else
current = property?.PropertyType;
var property = current?.GetProperty(part);
current = property?.PropertyType;
if (property == null)
return false;
}
@@ -58,20 +52,14 @@ namespace Artemis.Core.Plugins.Abstract.DataModels
foreach (var part in parts)
{
var property = current.GetProperty(part);
// For lists, look into the list type instead of the list itself
if (typeof(IList).IsAssignableFrom(property.PropertyType))
current = property.PropertyType.GetGenericArguments()[0];
else
current = property.PropertyType;
current = property.PropertyType;
result = property.PropertyType;
}
return result;
}
public Type GetListTypeAtPath(string path)
public Type GetListTypeInPath(string path)
{
if (!ContainsPath(path))
return null;
@@ -79,8 +67,13 @@ namespace Artemis.Core.Plugins.Abstract.DataModels
var parts = path.Split('.');
var current = GetType();
var index = 0;
foreach (var part in parts)
{
// Only return a type if the path CONTAINS a list, not if it points TO a list
if (index == parts.Length - 1)
return null;
var property = current.GetProperty(part);
// For lists, look into the list type instead of the list itself
@@ -88,14 +81,24 @@ namespace Artemis.Core.Plugins.Abstract.DataModels
return property.PropertyType.GetGenericArguments()[0];
current = property.PropertyType;
index++;
}
return null;
}
public Type GetListTypeAtPath(string path)
{
if (!ContainsPath(path))
return null;
var child = GetTypeAtPath(path);
return child.GenericTypeArguments.Length > 0 ? child.GenericTypeArguments[0] : null;
}
public string GetListInnerPath(string path)
{
if (GetListTypeAtPath(path) == null)
if (GetListTypeInPath(path) == null)
throw new ArtemisCoreException($"Cannot determine inner list path at {path} because it does not contain a list");
var parts = path.Split('.');
+20 -2
View File
@@ -178,9 +178,27 @@ namespace Artemis.Core.Services
return RegisteredConditionOperators.FirstOrDefault(o => o.PluginInfo.Guid == operatorPluginGuid && o.GetType().Name == operatorType);
}
public void LogDeserializationFailure(DisplayConditionPredicate displayConditionPredicate, JsonSerializationException exception)
public void LogPredicateDeserializationFailure(DisplayConditionPredicate displayConditionPredicate, JsonException exception)
{
_logger.Warning(exception, "Failed to deserialize display condition predicate {predicate}", displayConditionPredicate);
_logger.Warning(
exception,
"Failed to deserialize display condition predicate {left} {operator} {right}",
displayConditionPredicate.Entity.LeftPropertyPath,
displayConditionPredicate.Entity.OperatorType,
displayConditionPredicate.Entity.RightPropertyPath
);
}
public void LogListPredicateDeserializationFailure(DisplayConditionListPredicate displayConditionPredicate, JsonException exception)
{
_logger.Warning(
exception,
"Failed to deserialize display condition list predicate {list} => {left} {operator} {right}",
displayConditionPredicate.Entity.ListPropertyPath,
displayConditionPredicate.Entity.LeftPropertyPath,
displayConditionPredicate.Entity.OperatorType,
displayConditionPredicate.Entity.RightPropertyPath
);
}
private void RegisterBuiltInConditionOperators()
@@ -60,6 +60,7 @@ namespace Artemis.Core.Services.Interfaces
List<DisplayConditionOperator> GetCompatibleConditionOperators(Type type);
DisplayConditionOperator GetConditionOperator(Guid operatorPluginGuid, string operatorType);
void LogDeserializationFailure(DisplayConditionPredicate displayConditionPredicate, JsonSerializationException exception);
void LogPredicateDeserializationFailure(DisplayConditionPredicate displayConditionPredicate, JsonException exception);
void LogListPredicateDeserializationFailure(DisplayConditionListPredicate displayConditionListPredicate, JsonException exception);
}
}
@@ -154,8 +154,15 @@ namespace Artemis.Core.Services
? new DisplayConditionGroup(null, renderElement.RenderElementEntity.RootDisplayCondition)
: new DisplayConditionGroup(null);
displayCondition.Initialize(_dataModelService);
renderElement.DisplayConditionGroup = displayCondition;
try
{
displayCondition.Initialize(_dataModelService);
renderElement.DisplayConditionGroup = displayCondition;
}
catch (Exception e)
{
_logger.Warning(e, $"Failed to init display conditions for {renderElement}");
}
}
}
}