Data model conditions - DRY'd predicate code in preparation for events

This commit is contained in:
SpoinkyNL committed 2020-10-23 20:08:30 +02:00
1 parent a79223fa75
commit 574c4b21ec
21 files changed
+510 -918

No files matched your search

@@ -9,20 +9,19 @@ namespace Artemis.Core
/// A predicate in a data model condition using either two data model values or one data model value and a
/// static value
/// </summary>
public class DataModelConditionPredicate : DataModelConditionPart
public abstract class DataModelConditionPredicate : DataModelConditionPart
{
/// <summary>
/// Creates a new instance of the <see cref="DataModelConditionPredicate" /> class
/// </summary>
/// <param name="parent"></param>
/// <param name="predicateType"></param>
public DataModelConditionPredicate(DataModelConditionPart parent, ProfileRightSideType predicateType)
/// <param name="entity">A new empty entity</param>
protected DataModelConditionPredicate(DataModelConditionPart parent, ProfileRightSideType predicateType, DataModelConditionPredicateEntity entity)
{
Parent = parent;
Entity = entity;
PredicateType = predicateType;
Entity = new DataModelConditionPredicateEntity();
Initialize();
}
internal DataModelConditionPredicate(DataModelConditionPart parent, DataModelConditionPredicateEntity entity)
@@ -30,8 +29,6 @@ namespace Artemis.Core
Parent = parent;
Entity = entity;
PredicateType = (ProfileRightSideType) entity.PredicateType;
Initialize();
}
/// <summary>
@@ -42,31 +39,117 @@ namespace Artemis.Core
/// <summary>
/// Gets the operator
/// </summary>
public BaseConditionOperator? Operator { get; private set; }
public BaseConditionOperator? Operator { get; protected set; }
/// <summary>
/// Gets the path of the left property
/// </summary>
public DataModelPath? LeftPath { get; private set; }
public DataModelPath? LeftPath { get; protected set; }
/// <summary>
/// Gets the path of the right property
/// </summary>
public DataModelPath? RightPath { get; private set; }
public DataModelPath? RightPath { get; protected set; }
/// <summary>
/// Gets the right static value, only used it <see cref="PredicateType" /> is
/// <see cref="ProfileRightSideType.Static" />
/// </summary>
public object? RightStaticValue { get; private set; }
public object? RightStaticValue { get; protected set; }
internal DataModelConditionPredicateEntity Entity { get; set; }
/// <inheritdoc />
public override string ToString()
{
if (PredicateType == ProfileRightSideType.Dynamic)
return $"[Dynamic] {LeftPath} {Operator.Description} {RightPath}";
return $"[Static] {LeftPath} {Operator.Description} {RightStaticValue}";
}
#region IDisposable
/// <inheritdoc />
protected override void Dispose(bool disposing)
{
ConditionOperatorStore.ConditionOperatorAdded -= ConditionOperatorStoreOnConditionOperatorAdded;
ConditionOperatorStore.ConditionOperatorRemoved -= ConditionOperatorStoreOnConditionOperatorRemoved;
LeftPath?.Dispose();
RightPath?.Dispose();
base.Dispose(disposing);
}
#endregion
#region Initialization
internal void Initialize()
{
ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded;
ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved;
InitializeLeftPath();
// Operator
if (Entity.OperatorPluginGuid != null)
{
BaseConditionOperator? conditionOperator = ConditionOperatorStore.Get(Entity.OperatorPluginGuid.Value, Entity.OperatorType)?.ConditionOperator;
if (conditionOperator != null)
UpdateOperator(conditionOperator);
}
// Right side dynamic
if (PredicateType == ProfileRightSideType.Dynamic)
InitializeRightPath();
// Right side static
else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null)
try
{
if (LeftPath != null && LeftPath.IsValid)
{
// Use the left side type so JSON.NET has a better idea what to do
Type leftSideType = LeftPath.GetPropertyType()!;
object? rightSideValue;
try
{
rightSideValue = JsonConvert.DeserializeObject(Entity.RightStaticValue, leftSideType);
}
// If deserialization fails, use the type's default
catch (JsonSerializationException e)
{
DeserializationLogger.LogPredicateDeserializationFailure(this, e);
rightSideValue = Activator.CreateInstance(leftSideType);
}
UpdateRightSideStatic(rightSideValue);
}
else
{
// Hope for the best...
UpdateRightSideStatic(JsonConvert.DeserializeObject(Entity.RightStaticValue));
}
}
catch (JsonReaderException e)
{
DeserializationLogger.LogPredicateDeserializationFailure(this, e);
}
}
protected abstract void InitializeLeftPath();
protected abstract void InitializeRightPath();
#endregion
#region Modification
/// <summary>
/// Updates the left side of the predicate
/// </summary>
/// <param name="path">The path pointing to the left side value inside the data model</param>
public void UpdateLeftSide(DataModelPath? path)
public virtual void UpdateLeftSide(DataModelPath? path)
{
if (path != null && !path.IsValid)
throw new ArtemisCoreException("Cannot update left side of predicate to an invalid path");
@@ -161,153 +244,10 @@ namespace Artemis.Core
ValidateRightSide();
}
/// <inheritdoc />
public override bool Evaluate()
{
if (Operator == null || LeftPath == null || !LeftPath.IsValid)
return false;
// Compare with a static value
if (PredicateType == ProfileRightSideType.Static)
{
object? leftSideValue = LeftPath.GetValue();
if (leftSideValue != null && leftSideValue.GetType().IsValueType && RightStaticValue == null)
return false;
return Operator.InternalEvaluate(leftSideValue, RightStaticValue);
}
if (RightPath == null || !RightPath.IsValid)
return false;
// Compare with dynamic values
return Operator.InternalEvaluate(LeftPath.GetValue(), RightPath.GetValue());
}
/// <summary>
/// Determines the best type to use for the right side op this predicate
/// </summary>
public Type? GetPreferredRightSideType()
{
Type? preferredType = Operator?.RightSideType;
Type? leftSideType = LeftPath?.GetPropertyType();
if (preferredType == null)
return null;
if (leftSideType != null && preferredType.IsAssignableFrom(leftSideType))
preferredType = leftSideType;
return preferredType;
}
/// <inheritdoc />
public override string ToString()
{
if (PredicateType == ProfileRightSideType.Dynamic)
return $"[Dynamic] {LeftPath} {Operator.Description} {RightPath}";
return $"[Static] {LeftPath} {Operator.Description} {RightStaticValue}";
}
/// <inheritdoc />
protected override void Dispose(bool disposing)
{
ConditionOperatorStore.ConditionOperatorAdded -= ConditionOperatorStoreOnConditionOperatorAdded;
ConditionOperatorStore.ConditionOperatorRemoved -= ConditionOperatorStoreOnConditionOperatorRemoved;
LeftPath?.Dispose();
RightPath?.Dispose();
base.Dispose(disposing);
}
/// <inheritdoc />
internal override bool EvaluateObject(object target)
{
return false;
}
internal override void Save()
{
// Don't save an invalid state
if (LeftPath != null && !LeftPath.IsValid || RightPath != null && !RightPath.IsValid)
return;
Entity.PredicateType = (int) PredicateType;
LeftPath?.Save();
Entity.LeftPath = LeftPath?.Entity;
RightPath?.Save();
Entity.RightPath = RightPath?.Entity;
Entity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue);
if (Operator != null)
{
Entity.OperatorPluginGuid = Operator.PluginInfo.Guid;
Entity.OperatorType = Operator.GetType().Name;
}
}
internal void Initialize()
{
ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded;
ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved;
// Left side
if (Entity.LeftPath != null)
LeftPath = new DataModelPath(null, Entity.LeftPath);
// Operator
if (Entity.OperatorPluginGuid != null)
{
BaseConditionOperator? conditionOperator = ConditionOperatorStore.Get(Entity.OperatorPluginGuid.Value, Entity.OperatorType)?.ConditionOperator;
if (conditionOperator != null)
UpdateOperator(conditionOperator);
}
// Right side dynamic
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightPath != null)
RightPath = new DataModelPath(null, Entity.RightPath);
// Right side static
else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null)
try
{
if (LeftPath != null && LeftPath.IsValid)
{
// Use the left side type so JSON.NET has a better idea what to do
Type leftSideType = LeftPath.GetPropertyType()!;
object? rightSideValue;
try
{
rightSideValue = JsonConvert.DeserializeObject(Entity.RightStaticValue, leftSideType);
}
// If deserialization fails, use the type's default
catch (JsonSerializationException e)
{
DeserializationLogger.LogPredicateDeserializationFailure(this, e);
rightSideValue = Activator.CreateInstance(leftSideType);
}
UpdateRightSideStatic(rightSideValue);
}
else
{
// Hope for the best...
UpdateRightSideStatic(JsonConvert.DeserializeObject(Entity.RightStaticValue));
}
}
catch (JsonReaderException)
{
// ignored
// TODO: Some logging would be nice
}
}
internal override DataModelConditionPartEntity GetEntity()
{
return Entity;
}
public abstract Type? GetPreferredRightSideType();
private void ValidateOperator()
{
@@ -343,6 +283,72 @@ namespace Artemis.Core
}
}
#endregion
#region Evaluation
/// <inheritdoc />
public override bool Evaluate()
{
if (Operator == null || LeftPath == null || !LeftPath.IsValid)
return false;
// Compare with a static value
if (PredicateType == ProfileRightSideType.Static)
{
object? leftSideValue = LeftPath.GetValue();
if (leftSideValue != null && leftSideValue.GetType().IsValueType && RightStaticValue == null)
return false;
return Operator.InternalEvaluate(leftSideValue, RightStaticValue);
}
if (RightPath == null || !RightPath.IsValid)
return false;
// Compare with dynamic values
return Operator.InternalEvaluate(LeftPath.GetValue(), RightPath.GetValue());
}
/// <inheritdoc />
internal override bool EvaluateObject(object target)
{
return false;
}
#endregion
#region Storage
internal override DataModelConditionPartEntity GetEntity()
{
return Entity;
}
internal override void Save()
{
// Don't save an invalid state
if (LeftPath != null && !LeftPath.IsValid || RightPath != null && !RightPath.IsValid)
return;
Entity.PredicateType = (int) PredicateType;
LeftPath?.Save();
Entity.LeftPath = LeftPath?.Entity;
RightPath?.Save();
Entity.RightPath = RightPath?.Entity;
Entity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue);
if (Operator != null)
{
Entity.OperatorPluginGuid = Operator.PluginInfo.Guid;
Entity.OperatorType = Operator.GetType().Name;
}
}
#endregion
#region Event handlers
private void ConditionOperatorStoreOnConditionOperatorAdded(object? sender, ConditionOperatorStoreEvent e)
@@ -0,0 +1,63 @@
using System;
using Artemis.Storage.Entities.Profile.Conditions;
namespace Artemis.Core
{
/// <summary>
/// A predicate in a data model condition using either two data model values or one data model value and a
/// static value
/// </summary>
public class DataModelConditionGeneralPredicate : DataModelConditionPredicate
{
/// <summary>
/// Creates a new instance of the <see cref="DataModelConditionGeneralPredicate" /> class
/// </summary>
/// <param name="parent"></param>
/// <param name="predicateType"></param>
public DataModelConditionGeneralPredicate(DataModelConditionPart parent, ProfileRightSideType predicateType)
: base(parent, predicateType, new DataModelConditionGeneralPredicateEntity())
{
Initialize();
}
internal DataModelConditionGeneralPredicate(DataModelConditionPart parent, DataModelConditionGeneralPredicateEntity entity)
: base(parent, entity)
{
Initialize();
}
#region Modification
/// <inheritdoc />
public override Type? GetPreferredRightSideType()
{
Type? preferredType = Operator?.RightSideType;
Type? leftSideType = LeftPath?.GetPropertyType();
if (preferredType == null)
return null;
if (leftSideType != null && preferredType.IsAssignableFrom(leftSideType))
preferredType = leftSideType;
return preferredType;
}
#endregion
#region Initialization
protected override void InitializeLeftPath()
{
if (Entity.LeftPath != null)
LeftPath = new DataModelPath(null, Entity.LeftPath);
}
protected override void InitializeRightPath()
{
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightPath != null)
RightPath = new DataModelPath(null, Entity.RightPath);
}
#endregion
}
}
@@ -42,8 +42,8 @@ namespace Artemis.Core
AddChild(new DataModelConditionList(this, listEntity));
else if (childEntity is DataModelConditionEventEntity eventEntity)
AddChild(new DataModelConditionEvent(this, eventEntity));
else if (childEntity is DataModelConditionPredicateEntity predicateEntity)
AddChild(new DataModelConditionPredicate(this, predicateEntity));
else if (childEntity is DataModelConditionGeneralPredicateEntity predicateEntity)
AddChild(new DataModelConditionGeneralPredicate(this, predicateEntity));
else if (childEntity is DataModelConditionListPredicateEntity listPredicateEntity)
AddChild(new DataModelConditionListPredicate(this, listPredicateEntity));
}
@@ -1,16 +1,12 @@
using System;
using System.Reflection;
using Artemis.Core.DataModelExpansions;
using Artemis.Storage.Entities.Profile.Abstract;
using Artemis.Storage.Entities.Profile.Conditions;
using Newtonsoft.Json;
namespace Artemis.Core
{
/// <summary>
/// A predicate like evaluated inside a <see cref="DataModelConditionList" />
/// </summary>
public class DataModelConditionListPredicate : DataModelConditionPart
public class DataModelConditionListPredicate : DataModelConditionPredicate
{
/// <summary>
/// Creates a new instance of the <see cref="DataModelConditionListPredicate" /> class
@@ -18,173 +14,97 @@ namespace Artemis.Core
/// <param name="parent"></param>
/// <param name="predicateType"></param>
public DataModelConditionListPredicate(DataModelConditionPart parent, ProfileRightSideType predicateType)
: base(parent, predicateType, new DataModelConditionListPredicateEntity())
{
Parent = parent;
PredicateType = predicateType;
Entity = new DataModelConditionListPredicateEntity();
DataModelConditionList = null!;
ApplyParentList();
Initialize();
}
internal DataModelConditionListPredicate(DataModelConditionPart parent, DataModelConditionListPredicateEntity entity)
: base(parent, entity)
{
Parent = parent;
Entity = entity;
PredicateType = (ProfileRightSideType) entity.PredicateType;
DataModelConditionList = null!;
ApplyParentList();
Initialize();
}
/// <summary>
/// Gets or sets the predicate type
/// </summary>
public ProfileRightSideType PredicateType { get; set; }
/// <summary>
/// Gets the operator
/// </summary>
public BaseConditionOperator? Operator { get; private set; }
/// <summary>
/// Gets the data model condition list this predicate belongs to
/// </summary>
public DataModelConditionList DataModelConditionList { get; private set; }
/// <summary>
/// Gets the path of the left property
/// </summary>
public DataModelPath? LeftPath { get; private set; }
private void ApplyParentList()
{
DataModelConditionPart? current = Parent;
while (current != null)
{
if (current is DataModelConditionList parentList)
{
DataModelConditionList = parentList;
return;
}
/// <summary>
/// Gets the path of the right property
/// </summary>
public DataModelPath? RightPath { get; private set; }
current = current.Parent;
}
/// <summary>
/// Gets the right static value, only used it <see cref="PredicateType" /> is
/// <see cref="ProfileRightSideType.Static" />
/// </summary>
public object? RightStaticValue { get; private set; }
if (DataModelConditionList == null)
throw new ArtemisCoreException("This data model condition list predicate does not belong to a data model condition list");
}
internal DataModelConditionListPredicateEntity Entity { get; set; }
private object? GetListPathValue(DataModelPath path, object target)
{
if (!(path.Target is ListPredicateWrapperDataModel wrapper))
throw new ArtemisCoreException("Data model condition list predicate has a path with an invalid target");
wrapper.UntypedValue = target;
return path.GetValue();
}
#region Initialization
protected override void InitializeLeftPath()
{
if (Entity.LeftPath != null)
LeftPath = DataModelConditionList.ListType != null
? new DataModelPath(ListPredicateWrapperDataModel.Create(DataModelConditionList.ListType), Entity.LeftPath)
: null;
}
protected override void InitializeRightPath()
{
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightPath != null)
{
// Right side dynamic inside the list
// TODO: Come up with a general wrapper solution because this will clash with events
if (Entity.RightPath.DataModelGuid == Constants.CorePluginInfo.Guid)
RightPath = DataModelConditionList.ListType != null
? new DataModelPath(ListPredicateWrapperDataModel.Create(DataModelConditionList.ListType), Entity.RightPath)
: null;
// Right side dynamic
else
RightPath = new DataModelPath(null, Entity.RightPath);
}
}
#endregion
#region Modification
/// <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(DataModelPath? path)
public override void UpdateLeftSide(DataModelPath? path)
{
if (DataModelConditionList.IsPrimitiveList)
throw new ArtemisCoreException("Cannot apply a left side to a predicate inside a primitive list");
if (path != null && !path.IsValid)
throw new ArtemisCoreException("Cannot update left side of predicate to an invalid path");
LeftPath?.Dispose();
LeftPath = path != null ? new DataModelPath(path) : null;
ValidateOperator();
ValidateRightSide();
base.UpdateLeftSide(path);
}
/// <summary>
/// Updates the right side of the predicate using a path and makes the predicate dynamic
/// </summary>
/// <param name="path">The path pointing to the right side value</param>
public void UpdateRightSideDynamic(DataModelPath? path)
{
if (path != null && !path.IsValid)
throw new ArtemisCoreException("Cannot update right side of predicate to an invalid path");
if (Operator != null && path != null && !Operator.SupportsType(path.GetPropertyType()!, ConditionParameterSide.Right))
throw new ArtemisCoreException($"Selected operator does not support right side of type {path.GetPropertyType()!.Name}");
RightPath?.Dispose();
RightPath = path != null ? new DataModelPath(path) : null;
PredicateType = ProfileRightSideType.Dynamic;
}
/// <summary>
/// Updates the right side of the predicate, makes the predicate static and re-compiles the expression
/// </summary>
/// <param name="staticValue">The right side value to use</param>
public void UpdateRightSideStatic(object? staticValue)
{
PredicateType = ProfileRightSideType.Static;
RightPath?.Dispose();
RightPath = null;
SetStaticValue(staticValue);
}
/// <summary>
/// Updates the operator of the predicate and re-compiles the expression
/// </summary>
/// <param name="conditionOperator"></param>
public void UpdateOperator(BaseConditionOperator? conditionOperator)
{
if (conditionOperator == null)
{
Operator = null;
return;
}
// No need to clear compiled expressions, without a left data model they are already null
if (LeftPath == null || !LeftPath.IsValid)
{
Operator = conditionOperator;
return;
}
Type leftType = LeftPath.GetPropertyType()!;
// Left side can't go empty so enforce a match
if (!conditionOperator.SupportsType(leftType, ConditionParameterSide.Left))
throw new ArtemisCoreException($"Cannot apply operator {conditionOperator.GetType().Name} to this predicate because " +
$"it does not support left side type {leftType.Name}");
Operator = conditionOperator;
ValidateRightSide();
}
/// <summary>
/// Not supported for list predicates, always returns <c>false</c>
/// </summary>
public override bool Evaluate()
{
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 (DataModelConditionList.ListType == null)
return false;
string[] parts = path.Split('.');
Type current = DataModelConditionList.ListType;
foreach (string part in parts)
{
PropertyInfo? property = current.GetProperty(part);
current = property?.PropertyType;
if (property == null)
return false;
}
return true;
}
/// <summary>
/// Determines the best type to use for the right side op this predicate
/// </summary>
public Type? GetPreferredRightSideType()
/// <inheritdoc />
public override Type? GetPreferredRightSideType()
{
Type? preferredType = Operator?.RightSideType;
Type? leftSideType = DataModelConditionList.IsPrimitiveList
@@ -199,22 +119,18 @@ namespace Artemis.Core
return preferredType;
}
#region IDisposable
/// <inheritdoc />
protected override void Dispose(bool disposing)
{
ConditionOperatorStore.ConditionOperatorAdded -= ConditionOperatorStoreOnConditionOperatorAdded;
ConditionOperatorStore.ConditionOperatorRemoved -= ConditionOperatorStoreOnConditionOperatorRemoved;
LeftPath?.Dispose();
RightPath?.Dispose();
base.Dispose(disposing);
}
#endregion
#region Evaluation
/// <summary>
/// Not supported for list predicates, always returns <c>false</c>
/// </summary>
public override bool Evaluate()
{
return false;
}
internal override bool EvaluateObject(object target)
{
if (Operator == null || LeftPath == null || !LeftPath.IsValid)
@@ -245,227 +161,6 @@ namespace Artemis.Core
return false;
}
internal Type GetTypeAtInnerPath(string path)
{
if (!ListContainsInnerPath(path))
return null;
string[] parts = path.Split('.');
Type current = DataModelConditionList.ListType;
Type result = null;
foreach (string part in parts)
{
PropertyInfo? property = current.GetProperty(part);
current = property.PropertyType;
result = property.PropertyType;
}
return result;
}
internal override void Save()
{
Entity.PredicateType = (int) PredicateType;
LeftPath?.Save();
Entity.LeftPath = LeftPath?.Entity;
RightPath?.Save();
Entity.RightPath = RightPath?.Entity;
Entity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue);
if (Operator != null)
{
Entity.OperatorPluginGuid = Operator.PluginInfo.Guid;
Entity.OperatorType = Operator.GetType().Name;
}
}
internal override DataModelConditionPartEntity GetEntity()
{
return Entity;
}
private void ApplyParentList()
{
DataModelConditionPart? current = Parent;
while (current != null)
{
if (current is DataModelConditionList parentList)
{
DataModelConditionList = parentList;
return;
}
current = current.Parent;
}
if (DataModelConditionList == null)
throw new ArtemisCoreException("This data model condition list predicate does not belong to a data model condition list");
}
private void Initialize()
{
ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded;
ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved;
// Left side
if (Entity.LeftPath != null)
{
LeftPath = DataModelConditionList.ListType != null
? new DataModelPath(ListPredicateWrapperDataModel.Create(DataModelConditionList.ListType), Entity.LeftPath)
: null;
}
// Operator
if (Entity.OperatorPluginGuid != null)
{
BaseConditionOperator? conditionOperator = ConditionOperatorStore.Get(Entity.OperatorPluginGuid.Value, Entity.OperatorType)?.ConditionOperator;
if (conditionOperator != null)
UpdateOperator(conditionOperator);
}
// Right side dynamic
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightPath != null)
{
// Right side dynamic inside the list
// TODO: Come up with a general wrapper solution because this will clash with events
if (Entity.RightPath.DataModelGuid == Constants.CorePluginInfo.Guid)
{
RightPath = DataModelConditionList.ListType != null
? new DataModelPath(ListPredicateWrapperDataModel.Create(DataModelConditionList.ListType), Entity.RightPath)
: null;
}
// Right side dynamic
else
RightPath = new DataModelPath(null, Entity.RightPath);
}
// Right side static
else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null)
try
{
if (LeftPath != null && LeftPath.IsValid)
{
// Use the left side type so JSON.NET has a better idea what to do
Type leftSideType = LeftPath.GetPropertyType()!;
object? rightSideValue;
try
{
rightSideValue = JsonConvert.DeserializeObject(Entity.RightStaticValue, leftSideType);
}
// If deserialization fails, use the type's default
catch (JsonSerializationException e)
{
DeserializationLogger.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)
{
DeserializationLogger.LogListPredicateDeserializationFailure(this, e);
}
}
private void ValidateOperator()
{
if (LeftPath == null || !LeftPath.IsValid || Operator == null)
return;
Type leftType = LeftPath.GetPropertyType()!;
if (!Operator.SupportsType(leftType, ConditionParameterSide.Left))
Operator = null;
}
private void ValidateRightSide()
{
if (Operator == null)
return;
if (PredicateType == ProfileRightSideType.Dynamic)
{
if (RightPath == null || !RightPath.IsValid)
return;
Type rightSideType = RightPath.GetPropertyType()!;
if (!Operator.SupportsType(rightSideType, ConditionParameterSide.Right))
UpdateRightSideDynamic(null);
}
else
{
if (RightStaticValue == null)
return;
if (!Operator.SupportsType(RightStaticValue.GetType(), ConditionParameterSide.Right))
UpdateRightSideStatic(null);
}
}
private void SetStaticValue(object? staticValue)
{
RightPath?.Dispose();
RightPath = null;
// If the operator is null simply apply the value, any validation will wait
if (Operator == null)
{
RightStaticValue = staticValue;
return;
}
// If the operator does not support a right side, always set it to null
if (Operator.RightSideType == null)
{
RightStaticValue = null;
return;
}
// If not null ensure the types match and if not, convert it
Type? preferredType = GetPreferredRightSideType();
if (staticValue != null && staticValue.GetType() == preferredType || preferredType == null)
RightStaticValue = staticValue;
else if (staticValue != null)
RightStaticValue = Convert.ChangeType(staticValue, preferredType);
// If null create a default instance for value types or simply make it null for reference types
else if (preferredType.IsValueType)
RightStaticValue = Activator.CreateInstance(preferredType);
else
RightStaticValue = null;
}
private object? GetListPathValue(DataModelPath path, object target)
{
if (!(path.Target is ListPredicateWrapperDataModel wrapper))
throw new ArtemisCoreException("Data model condition list predicate has a path with an invalid target");
wrapper.UntypedValue = target;
return path.GetValue();
}
#region Event handlers
private void ConditionOperatorStoreOnConditionOperatorAdded(object sender, ConditionOperatorStoreEvent e)
{
BaseConditionOperator conditionOperator = e.Registration.ConditionOperator;
if (Entity.OperatorPluginGuid == conditionOperator.PluginInfo.Guid && Entity.OperatorType == conditionOperator.GetType().Name)
UpdateOperator(conditionOperator);
}
private void ConditionOperatorStoreOnConditionOperatorRemoved(object sender, ConditionOperatorStoreEvent e)
{
if (e.Registration.ConditionOperator != Operator)
return;
Operator = null;
}
#endregion
}
}
@@ -24,17 +24,6 @@ namespace Artemis.Core
);
}
public static void LogListPredicateDeserializationFailure(DataModelConditionListPredicate dataModelConditionPredicate, JsonException exception)
{
_logger.Warning(
exception,
"Failed to deserialize display condition list predicate {list} => {left} {operator} {right}",
dataModelConditionPredicate.Entity.LeftPath?.Path,
dataModelConditionPredicate.Entity.OperatorType,
dataModelConditionPredicate.Entity.RightPath?.Path
);
}
public static void LogModifierDeserializationFailure(string modifierName, JsonSerializationException exception)
{
_logger.Warning(exception, "Failed to deserialize static parameter for modifier {modifierName}", modifierName);