mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-11-11 19:36:28 +00:00
Conditions - Removed old systems
Data bindings - Removed old systems Data bindings - Implemented node system
This commit is contained in:
1 parent
32d4ec2812
commit
5675d1895b
157 files changed
+784
-8802
No files matched your search
@@ -1,82 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a condition operator that performs a boolean operation
|
||||
/// <para>
|
||||
/// To implement your own condition operator, inherit <see cref="ConditionOperator{TLeftSide, TRightSide}" /> or
|
||||
/// <see cref="ConditionOperator{TLeftSide}" />
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public abstract class BaseConditionOperator
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets or sets the description of this logical operator
|
||||
/// </summary>
|
||||
public abstract string Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon of this logical operator
|
||||
/// </summary>
|
||||
public abstract string Icon { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the plugin this condition operator belongs to
|
||||
/// <para>Note: Not set until after registering</para>
|
||||
/// </summary>
|
||||
public Plugin? Plugin { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the left side type of this condition operator
|
||||
/// </summary>
|
||||
public abstract Type LeftSideType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the right side type of this condition operator. May be null if the operator does not support a right side
|
||||
/// </summary>
|
||||
public abstract Type? RightSideType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the given type is supported by the operator
|
||||
/// </summary>
|
||||
/// <param name="type">The type to check for, must be either the same or be castable to the target type</param>
|
||||
/// <param name="side">Which side of the operator to check, left or right</param>
|
||||
public bool SupportsType(Type type, ConditionParameterSide side)
|
||||
{
|
||||
if (type == null)
|
||||
return true;
|
||||
if (side == ConditionParameterSide.Left)
|
||||
return LeftSideType.IsCastableFrom(type);
|
||||
return RightSideType != null && RightSideType.IsCastableFrom(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the condition with the input types being provided as objects
|
||||
/// <para>
|
||||
/// This leaves the caller responsible for the types matching <see cref="LeftSideType" /> and
|
||||
/// <see cref="RightSideType" />
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="leftSideValue">The left side value, type should match <see cref="LeftSideType" /></param>
|
||||
/// <param name="rightSideValue">The right side value, type should match <see cref="RightSideType" /></param>
|
||||
/// <returns>The result of the boolean condition's evaluation</returns>
|
||||
internal abstract bool InternalEvaluate(object? leftSideValue, object? rightSideValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a side of a condition parameter
|
||||
/// </summary>
|
||||
public enum ConditionParameterSide
|
||||
{
|
||||
/// <summary>
|
||||
/// The left side of a condition parameter
|
||||
/// </summary>
|
||||
Left,
|
||||
|
||||
/// <summary>
|
||||
/// The right side of a condition parameter
|
||||
/// </summary>
|
||||
Right
|
||||
}
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a condition operator that performs a boolean operation using a left- and right-side
|
||||
/// </summary>
|
||||
public abstract class ConditionOperator<TLeftSide, TRightSide> : BaseConditionOperator
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override Type LeftSideType => typeof(TLeftSide);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type RightSideType => typeof(TRightSide);
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the operator on a and b
|
||||
/// </summary>
|
||||
/// <param name="a">The parameter on the left side of the expression</param>
|
||||
/// <param name="b">The parameter on the right side of the expression</param>
|
||||
public abstract bool Evaluate(TLeftSide a, TRightSide b);
|
||||
|
||||
/// <inheritdoc />
|
||||
internal override bool InternalEvaluate(object? leftSideValue, object? rightSideValue)
|
||||
{
|
||||
// TODO: Can we avoid boxing/unboxing?
|
||||
TLeftSide leftSide;
|
||||
if (leftSideValue != null)
|
||||
{
|
||||
if (leftSideValue.GetType() != typeof(TLeftSide) && leftSideValue is IConvertible)
|
||||
leftSide = (TLeftSide) Convert.ChangeType(leftSideValue, typeof(TLeftSide));
|
||||
else
|
||||
leftSide = (TLeftSide) leftSideValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
leftSide = default;
|
||||
}
|
||||
|
||||
TRightSide rightSide;
|
||||
if (rightSideValue != null)
|
||||
{
|
||||
if (rightSideValue.GetType() != typeof(TRightSide) && leftSideValue is IConvertible)
|
||||
rightSide = (TRightSide) Convert.ChangeType(rightSideValue, typeof(TRightSide));
|
||||
else
|
||||
rightSide = (TRightSide) rightSideValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
rightSide = default;
|
||||
}
|
||||
|
||||
return Evaluate(leftSide!, rightSide!);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a condition operator that performs a boolean operation using only a left side
|
||||
/// </summary>
|
||||
public abstract class ConditionOperator<TLeftSide> : BaseConditionOperator
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override Type LeftSideType => typeof(TLeftSide);
|
||||
|
||||
/// <summary>
|
||||
/// Always <c>null</c>, not applicable to this type of condition operator
|
||||
/// </summary>
|
||||
public override Type? RightSideType => null;
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the operator on a and b
|
||||
/// </summary>
|
||||
/// <param name="a">The parameter on the left side of the expression</param>
|
||||
public abstract bool Evaluate(TLeftSide a);
|
||||
|
||||
/// <inheritdoc />
|
||||
internal override bool InternalEvaluate(object? leftSideValue, object? rightSideValue)
|
||||
{
|
||||
// TODO: Can we avoid boxing/unboxing?
|
||||
TLeftSide leftSide;
|
||||
if (leftSideValue != null)
|
||||
{
|
||||
if (leftSideValue.GetType() != typeof(TLeftSide) && leftSideValue is IConvertible)
|
||||
leftSide = (TLeftSide) Convert.ChangeType(leftSideValue, typeof(TLeftSide));
|
||||
else
|
||||
leftSide = (TLeftSide) leftSideValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
leftSide = default;
|
||||
}
|
||||
|
||||
return Evaluate(leftSide!);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,135 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Storage.Entities.Profile.Abstract;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// An abstract class for display condition parts
|
||||
/// </summary>
|
||||
public abstract class DataModelConditionPart : IDisposable
|
||||
{
|
||||
private readonly List<DataModelConditionPart> _children = new();
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parent of this part
|
||||
/// </summary>
|
||||
public DataModelConditionPart? Parent { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the children of this part
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<DataModelConditionPart> Children => _children.AsReadOnly();
|
||||
|
||||
/// <summary>
|
||||
/// Adds a child to the display condition part's <see cref="Children" /> collection
|
||||
/// </summary>
|
||||
/// <param name="dataModelConditionPart"></param>
|
||||
/// <param name="index">An optional index at which to insert the condition</param>
|
||||
public void AddChild(DataModelConditionPart dataModelConditionPart, int? index = null)
|
||||
{
|
||||
if (!_children.Contains(dataModelConditionPart))
|
||||
{
|
||||
dataModelConditionPart.Parent = this;
|
||||
if (index != null)
|
||||
_children.Insert(index.Value, dataModelConditionPart);
|
||||
else
|
||||
_children.Add(dataModelConditionPart);
|
||||
|
||||
OnChildAdded();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a child from the display condition part's <see cref="Children" /> collection
|
||||
/// </summary>
|
||||
/// <param name="dataModelConditionPart">The child to remove</param>
|
||||
public void RemoveChild(DataModelConditionPart dataModelConditionPart)
|
||||
{
|
||||
if (_children.Contains(dataModelConditionPart))
|
||||
{
|
||||
dataModelConditionPart.Parent = null;
|
||||
_children.Remove(dataModelConditionPart);
|
||||
OnChildRemoved();
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes all children. You monster.
|
||||
/// </summary>
|
||||
public void ClearChildren()
|
||||
{
|
||||
while (Children.Any())
|
||||
RemoveChild(Children[0]);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the condition part on the data model
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public abstract bool Evaluate();
|
||||
|
||||
/// <summary>
|
||||
/// Evaluates the condition part on the given target (currently only for lists)
|
||||
/// </summary>
|
||||
/// <param name="target"></param>
|
||||
/// <returns></returns>
|
||||
internal abstract bool EvaluateObject(object? target);
|
||||
|
||||
internal abstract void Save();
|
||||
internal abstract DataModelConditionPartEntity GetEntity();
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <summary>
|
||||
/// Disposed the condition part
|
||||
/// </summary>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a child-condition was added
|
||||
/// </summary>
|
||||
public event EventHandler? ChildAdded;
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a child-condition was removed
|
||||
/// </summary>
|
||||
public event EventHandler? ChildRemoved;
|
||||
|
||||
/// <summary>
|
||||
/// Invokers the <see cref="ChildAdded" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnChildAdded()
|
||||
{
|
||||
ChildAdded?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Invokers the <see cref="ChildRemoved" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnChildRemoved()
|
||||
{
|
||||
ChildRemoved?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,411 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Storage.Entities.Profile.Abstract;
|
||||
using Artemis.Storage.Entities.Profile.Conditions;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
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 abstract class DataModelConditionPredicate : DataModelConditionPart
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DataModelConditionPredicate" /> class
|
||||
/// </summary>
|
||||
/// <param name="parent"></param>
|
||||
/// <param name="predicateType"></param>
|
||||
/// <param name="entity">A new empty entity</param>
|
||||
protected DataModelConditionPredicate(DataModelConditionPart parent, ProfileRightSideType predicateType, DataModelConditionPredicateEntity entity)
|
||||
{
|
||||
Parent = parent;
|
||||
Entity = entity;
|
||||
PredicateType = predicateType;
|
||||
}
|
||||
|
||||
internal DataModelConditionPredicate(DataModelConditionPart parent, DataModelConditionPredicateEntity entity)
|
||||
{
|
||||
Parent = parent;
|
||||
Entity = entity;
|
||||
PredicateType = (ProfileRightSideType) entity.PredicateType;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the predicate type
|
||||
/// </summary>
|
||||
public ProfileRightSideType PredicateType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the operator
|
||||
/// </summary>
|
||||
public BaseConditionOperator? Operator { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path of the left property
|
||||
/// </summary>
|
||||
public DataModelPath? LeftPath { get; protected set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path of the right property
|
||||
/// </summary>
|
||||
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; 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 the left path is not valid we cannot reliably set up the right side because the type is unknown
|
||||
// Because of that wait for it to validate first
|
||||
if (LeftPath != null && !LeftPath.IsValid)
|
||||
{
|
||||
LeftPath.PathValidated += InitializeRightSideStatic;
|
||||
return;
|
||||
}
|
||||
if (LeftPath == null)
|
||||
return;
|
||||
|
||||
// Use the left side type so JSON.NET has a better idea what to do
|
||||
Type leftSideType = LeftPath.GetPropertyType()!;
|
||||
object? rightSideValue;
|
||||
|
||||
try
|
||||
{
|
||||
rightSideValue = CoreJson.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);
|
||||
}
|
||||
catch (JsonReaderException e)
|
||||
{
|
||||
DeserializationLogger.LogPredicateDeserializationFailure(this, e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void InitializeRightSideStatic(object? sender, EventArgs args)
|
||||
{
|
||||
if (LeftPath == null)
|
||||
return;
|
||||
|
||||
LeftPath.PathValidated -= InitializeRightSideStatic;
|
||||
|
||||
// Use the left side type so JSON.NET has a better idea what to do
|
||||
Type leftSideType = LeftPath.GetPropertyType()!;
|
||||
object? rightSideValue;
|
||||
|
||||
try
|
||||
{
|
||||
rightSideValue = CoreJson.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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the left path of this condition predicate
|
||||
/// </summary>
|
||||
protected abstract void InitializeLeftPath();
|
||||
|
||||
/// <summary>
|
||||
/// Initializes the right path of this condition predicate
|
||||
/// </summary>
|
||||
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 virtual void UpdateLeftSide(DataModelPath? path)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the right side of the predicate, makes the predicate dynamic and re-compiles the expression
|
||||
/// </summary>
|
||||
/// <param name="path">The path pointing to the right side value inside the data model</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;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
/// <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 check for compatibility without a left side, when left site does get set it will be checked again
|
||||
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>
|
||||
/// Determines the best type to use for the right side op this predicate
|
||||
/// </summary>
|
||||
public abstract Type? GetPreferredRightSideType();
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Evaluation
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Evaluate()
|
||||
{
|
||||
if (Operator == null || LeftPath == null || !LeftPath.IsValid)
|
||||
return false;
|
||||
|
||||
// If the operator does not support a right side, immediately evaluate with null
|
||||
if (Operator.RightSideType == null)
|
||||
return Operator.InternalEvaluate(LeftPath.GetValue(), null);
|
||||
|
||||
// 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 = CoreJson.SerializeObject(RightStaticValue);
|
||||
|
||||
if (Operator?.Plugin != null)
|
||||
{
|
||||
Entity.OperatorPluginGuid = Operator.Plugin.Guid;
|
||||
Entity.OperatorType = Operator.GetType().Name;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void ConditionOperatorStoreOnConditionOperatorAdded(object? sender, ConditionOperatorStoreEvent e)
|
||||
{
|
||||
BaseConditionOperator conditionOperator = e.Registration.ConditionOperator;
|
||||
if (Entity.OperatorPluginGuid == conditionOperator.Plugin!.Guid && Entity.OperatorType == conditionOperator.GetType().Name)
|
||||
UpdateOperator(conditionOperator);
|
||||
}
|
||||
|
||||
private void ConditionOperatorStoreOnConditionOperatorRemoved(object? sender, ConditionOperatorStoreEvent e)
|
||||
{
|
||||
if (e.Registration.ConditionOperator != Operator)
|
||||
return;
|
||||
|
||||
Operator = null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,257 +0,0 @@
|
||||
using System;
|
||||
using System.Linq;
|
||||
using Artemis.Storage.Entities.Profile.Abstract;
|
||||
using Artemis.Storage.Entities.Profile.Conditions;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A condition that evaluates to true when an event is triggered
|
||||
/// </summary>
|
||||
public class DataModelConditionEvent : DataModelConditionPart
|
||||
{
|
||||
private bool _disposed;
|
||||
private bool _reinitializing;
|
||||
private IDataModelEvent? _valueChangedEvent;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DataModelConditionEvent" /> class
|
||||
/// </summary>
|
||||
/// <param name="parent"></param>
|
||||
public DataModelConditionEvent(DataModelConditionPart parent)
|
||||
{
|
||||
Parent = parent;
|
||||
Entity = new DataModelConditionEventEntity();
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
internal DataModelConditionEvent(DataModelConditionPart parent, DataModelConditionEventEntity entity)
|
||||
{
|
||||
Parent = parent;
|
||||
Entity = entity;
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path of the event property
|
||||
/// </summary>
|
||||
public DataModelPath? EventPath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last time the event this condition is applied to was triggered
|
||||
/// </summary>
|
||||
public DateTime LastTrigger { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the type of argument the event provides
|
||||
/// </summary>
|
||||
public Type? EventArgumentType { get; private set; }
|
||||
|
||||
internal DataModelConditionEventEntity Entity { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Evaluate()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataModelConditionEvent");
|
||||
|
||||
IDataModelEvent? dataModelEvent = GetDataModelEvent();
|
||||
if (dataModelEvent == null)
|
||||
return false;
|
||||
dataModelEvent.Update();
|
||||
|
||||
// Only evaluate to true once every time the event has been triggered since the last evaluation
|
||||
if (dataModelEvent.LastTrigger <= LastTrigger)
|
||||
return false;
|
||||
|
||||
LastTrigger = DateTime.Now;
|
||||
|
||||
// If there is a child (root group), it must evaluate to true whenever the event triggered
|
||||
if (Children.Any())
|
||||
return Children[0].EvaluateObject(dataModelEvent.LastEventArgumentsUntyped);
|
||||
|
||||
// If there are no children, we always evaluate to true whenever the event triggered
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the event the condition is triggered by
|
||||
/// </summary>
|
||||
public void UpdateEvent(DataModelPath? path)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataModelConditionEvent");
|
||||
|
||||
if (path != null && !path.IsValid)
|
||||
throw new ArtemisCoreException("Cannot update event to an invalid path");
|
||||
|
||||
EventPath?.Dispose();
|
||||
EventPath = path != null ? new DataModelPath(path) : null;
|
||||
SubscribeToEventPath();
|
||||
CreateValueChangedEventIfNeeded();
|
||||
|
||||
// Remove the old root group that was tied to the old data model
|
||||
ClearChildren();
|
||||
|
||||
if (EventPath != null)
|
||||
{
|
||||
EventArgumentType = GetEventArgumentType();
|
||||
// Create a new root group
|
||||
AddChild(new DataModelConditionGroup(this));
|
||||
}
|
||||
else
|
||||
{
|
||||
EventArgumentType = null;
|
||||
}
|
||||
|
||||
LastTrigger = GetDataModelEvent()?.LastTrigger ?? DateTime.Now;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the <see cref="IDataModelEvent" /> this <see cref="DataModelConditionEvent" /> is triggered by
|
||||
/// </summary>
|
||||
/// <returns>The <see cref="IDataModelEvent" /> this <see cref="DataModelConditionEvent" /> is triggered by</returns>
|
||||
public IDataModelEvent? GetDataModelEvent()
|
||||
{
|
||||
if (_valueChangedEvent != null)
|
||||
return _valueChangedEvent;
|
||||
return EventPath?.GetValue() as IDataModelEvent;
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
_disposed = true;
|
||||
|
||||
EventPath?.Dispose();
|
||||
|
||||
foreach (DataModelConditionPart child in Children)
|
||||
child.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
internal override bool EvaluateObject(object? target)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
internal override void Save()
|
||||
{
|
||||
// Don't save an invalid state
|
||||
if (EventPath != null && !EventPath.IsValid)
|
||||
return;
|
||||
|
||||
// Target list
|
||||
EventPath?.Save();
|
||||
Entity.EventPath = EventPath?.Entity;
|
||||
|
||||
// Children
|
||||
Entity.Children.Clear();
|
||||
Entity.Children.AddRange(Children.Select(c => c.GetEntity()));
|
||||
foreach (DataModelConditionPart child in Children)
|
||||
child.Save();
|
||||
}
|
||||
|
||||
internal override DataModelConditionPartEntity GetEntity()
|
||||
{
|
||||
return Entity;
|
||||
}
|
||||
|
||||
internal void Initialize()
|
||||
{
|
||||
ClearChildren();
|
||||
|
||||
if (Entity.EventPath == null)
|
||||
return;
|
||||
|
||||
DataModelPath eventPath = new(null, Entity.EventPath);
|
||||
EventPath = eventPath;
|
||||
SubscribeToEventPath();
|
||||
CreateValueChangedEventIfNeeded();
|
||||
|
||||
EventArgumentType = GetEventArgumentType();
|
||||
// There should only be one child and it should be a group
|
||||
if (Entity.Children.FirstOrDefault() is DataModelConditionGroupEntity rootGroup)
|
||||
{
|
||||
AddChild(new DataModelConditionGroup(this, rootGroup));
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity.Children.Clear();
|
||||
AddChild(new DataModelConditionGroup(this));
|
||||
}
|
||||
|
||||
LastTrigger = GetDataModelEvent()?.LastTrigger ?? DateTime.Now;
|
||||
}
|
||||
|
||||
private Type? GetEventArgumentType()
|
||||
{
|
||||
if (EventPath == null || !EventPath.IsValid)
|
||||
return null;
|
||||
|
||||
if (_valueChangedEvent != null)
|
||||
return _valueChangedEvent.ArgumentsType;
|
||||
|
||||
// Cannot rely on EventPath.GetValue() because part of the path might be null
|
||||
Type eventType = EventPath.GetPropertyType()!;
|
||||
return eventType.IsGenericType ? eventType.GetGenericArguments()[0] : typeof(DataModelEventArgs);
|
||||
}
|
||||
|
||||
private void SubscribeToEventPath()
|
||||
{
|
||||
if (EventPath == null) return;
|
||||
EventPath.PathValidated += EventPathOnPathValidated;
|
||||
EventPath.PathInvalidated += EventPathOnPathInvalidated;
|
||||
}
|
||||
|
||||
private void CreateValueChangedEventIfNeeded()
|
||||
{
|
||||
Type? propertyType = EventPath?.GetPropertyType();
|
||||
if (propertyType == null)
|
||||
return;
|
||||
|
||||
if (!typeof(IDataModelEvent).IsAssignableFrom(propertyType))
|
||||
{
|
||||
IDataModelEvent? instance = (IDataModelEvent?) Activator.CreateInstance(typeof(DataModelValueChangedEvent<>).MakeGenericType(propertyType), EventPath);
|
||||
_valueChangedEvent = instance ?? throw new ArtemisCoreException("Failed to create a DataModelValueChangedEvent for a property changed data model event");
|
||||
}
|
||||
else
|
||||
{
|
||||
_valueChangedEvent = null;
|
||||
}
|
||||
}
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void EventPathOnPathValidated(object? sender, EventArgs e)
|
||||
{
|
||||
if (_reinitializing)
|
||||
return;
|
||||
|
||||
_reinitializing = true;
|
||||
EventPath?.Dispose();
|
||||
Initialize();
|
||||
_reinitializing = false;
|
||||
}
|
||||
|
||||
private void EventPathOnPathInvalidated(object? sender, EventArgs e)
|
||||
{
|
||||
if (_reinitializing)
|
||||
return;
|
||||
|
||||
_reinitializing = true;
|
||||
EventPath?.Dispose();
|
||||
Initialize();
|
||||
_reinitializing = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,163 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using Artemis.Storage.Entities.Profile.Conditions;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A predicate like evaluated inside a <see cref="DataModelConditionEvent" />
|
||||
/// </summary>
|
||||
public class DataModelConditionEventPredicate : DataModelConditionPredicate
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DataModelConditionEventPredicate" /> class
|
||||
/// </summary>
|
||||
/// <param name="parent"></param>
|
||||
/// <param name="predicateType"></param>
|
||||
public DataModelConditionEventPredicate(DataModelConditionPart parent, ProfileRightSideType predicateType)
|
||||
: base(parent, predicateType, new DataModelConditionEventPredicateEntity())
|
||||
{
|
||||
DataModelConditionEvent = null!;
|
||||
ApplyParentEvent();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
internal DataModelConditionEventPredicate(DataModelConditionPart parent, DataModelConditionEventPredicateEntity entity)
|
||||
: base(parent, entity)
|
||||
{
|
||||
DataModelConditionEvent = null!;
|
||||
ApplyParentEvent();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data model condition event this predicate belongs to
|
||||
/// </summary>
|
||||
public DataModelConditionEvent DataModelConditionEvent { get; private set; }
|
||||
|
||||
private void ApplyParentEvent()
|
||||
{
|
||||
DataModelConditionPart? current = Parent;
|
||||
while (current != null)
|
||||
{
|
||||
if (current is DataModelConditionEvent parentEvent)
|
||||
{
|
||||
DataModelConditionEvent = parentEvent;
|
||||
return;
|
||||
}
|
||||
|
||||
current = current.Parent;
|
||||
}
|
||||
|
||||
if (DataModelConditionEvent == null)
|
||||
throw new ArtemisCoreException("This data model condition event predicate does not belong to a data model condition event");
|
||||
}
|
||||
|
||||
private object? GetEventPathValue(DataModelPath path, object? target)
|
||||
{
|
||||
lock (path)
|
||||
{
|
||||
if (!(path.Target is EventPredicateWrapperDataModel wrapper))
|
||||
throw new ArtemisCoreException("Data model condition event predicate has a path with an invalid target");
|
||||
|
||||
wrapper.UntypedArguments = target;
|
||||
return path.GetValue();
|
||||
}
|
||||
}
|
||||
|
||||
#region Initialization
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void InitializeLeftPath()
|
||||
{
|
||||
if (Entity.LeftPath != null)
|
||||
LeftPath = DataModelConditionEvent.EventArgumentType != null
|
||||
? new DataModelPath(EventPredicateWrapperDataModel.Create(DataModelConditionEvent.EventArgumentType), Entity.LeftPath)
|
||||
: null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void InitializeRightPath()
|
||||
{
|
||||
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightPath != null)
|
||||
{
|
||||
// Right side dynamic using event arguments
|
||||
if (Entity.RightPath.WrapperType == PathWrapperType.Event)
|
||||
{
|
||||
RightPath = DataModelConditionEvent.EventArgumentType != null
|
||||
? new DataModelPath(EventPredicateWrapperDataModel.Create(DataModelConditionEvent.EventArgumentType), Entity.RightPath)
|
||||
: null;
|
||||
}
|
||||
// Right side dynamic
|
||||
else
|
||||
RightPath = new DataModelPath(null, Entity.RightPath);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#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 Evaluation
|
||||
|
||||
/// <summary>
|
||||
/// Not supported for event 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)
|
||||
return false;
|
||||
|
||||
// If the operator does not support a right side, immediately evaluate with null
|
||||
if (Operator.RightSideType == null)
|
||||
return Operator.InternalEvaluate(GetEventPathValue(LeftPath, target), null);
|
||||
|
||||
// Compare with a static value
|
||||
if (PredicateType == ProfileRightSideType.Static)
|
||||
{
|
||||
object? leftSideValue = GetEventPathValue(LeftPath, target);
|
||||
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
|
||||
if (PredicateType == ProfileRightSideType.Dynamic)
|
||||
{
|
||||
// If the path targets a property inside the event, evaluate on the event path value instead of the right path value
|
||||
if (RightPath.Target is EventPredicateWrapperDataModel)
|
||||
return Operator.InternalEvaluate(GetEventPathValue(LeftPath, target), GetEventPathValue(RightPath, target));
|
||||
return Operator.InternalEvaluate(GetEventPathValue(LeftPath, target), RightPath.GetValue());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,65 +0,0 @@
|
||||
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
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void InitializeLeftPath()
|
||||
{
|
||||
if (Entity.LeftPath != null)
|
||||
LeftPath = new DataModelPath(null, Entity.LeftPath);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void InitializeRightPath()
|
||||
{
|
||||
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightPath != null)
|
||||
RightPath = new DataModelPath(null, Entity.RightPath);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,190 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Storage.Entities.Profile.Abstract;
|
||||
using Artemis.Storage.Entities.Profile.Conditions;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A group containing zero to many <see cref="DataModelConditionPart" />s which it evaluates using a boolean specific
|
||||
/// operator
|
||||
/// </summary>
|
||||
public class DataModelConditionGroup : DataModelConditionPart
|
||||
{
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DataModelConditionGroup" /> class
|
||||
/// </summary>
|
||||
/// <param name="parent"></param>
|
||||
public DataModelConditionGroup(DataModelConditionPart? parent)
|
||||
{
|
||||
Parent = parent;
|
||||
Entity = new DataModelConditionGroupEntity();
|
||||
ChildAdded += OnChildrenChanged;
|
||||
ChildRemoved += OnChildrenChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DataModelConditionGroup" /> class
|
||||
/// </summary>
|
||||
/// <param name="parent"></param>
|
||||
/// <param name="entity"></param>
|
||||
public DataModelConditionGroup(DataModelConditionPart? parent, DataModelConditionGroupEntity entity)
|
||||
{
|
||||
Parent = parent;
|
||||
Entity = entity;
|
||||
BooleanOperator = (BooleanOperator) Entity.BooleanOperator;
|
||||
|
||||
foreach (DataModelConditionPartEntity childEntity in Entity.Children)
|
||||
{
|
||||
if (childEntity is DataModelConditionGroupEntity groupEntity)
|
||||
AddChild(new DataModelConditionGroup(this, groupEntity));
|
||||
else if (childEntity is DataModelConditionListEntity listEntity)
|
||||
AddChild(new DataModelConditionList(this, listEntity));
|
||||
else if (childEntity is DataModelConditionEventEntity eventEntity)
|
||||
AddChild(new DataModelConditionEvent(this, eventEntity));
|
||||
else if (childEntity is DataModelConditionGeneralPredicateEntity predicateEntity)
|
||||
AddChild(new DataModelConditionGeneralPredicate(this, predicateEntity));
|
||||
else if (childEntity is DataModelConditionListPredicateEntity listPredicateEntity)
|
||||
AddChild(new DataModelConditionListPredicate(this, listPredicateEntity));
|
||||
else if (childEntity is DataModelConditionEventPredicateEntity eventPredicateEntity)
|
||||
AddChild(new DataModelConditionEventPredicate(this, eventPredicateEntity));
|
||||
}
|
||||
|
||||
ContainsEvents = Children.Any(c => c is DataModelConditionEvent);
|
||||
ChildAdded += OnChildrenChanged;
|
||||
ChildRemoved += OnChildrenChanged;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the boolean operator of this group
|
||||
/// </summary>
|
||||
public BooleanOperator BooleanOperator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether this group contains any events
|
||||
/// </summary>
|
||||
public bool ContainsEvents { get; private set; }
|
||||
|
||||
internal DataModelConditionGroupEntity Entity { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Evaluate()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataModelConditionGroup");
|
||||
|
||||
// Empty groups are always true
|
||||
if (Children.Count == 0)
|
||||
return true;
|
||||
// Groups with only one child ignore the boolean operator
|
||||
if (Children.Count == 1)
|
||||
return Children[0].Evaluate();
|
||||
|
||||
if (ContainsEvents)
|
||||
{
|
||||
bool eventTriggered = Children.Where(c => c is DataModelConditionEvent).Any(c => c.Evaluate());
|
||||
return eventTriggered && EvaluateWithOperator(Children.Where(c => !(c is DataModelConditionEvent)));
|
||||
}
|
||||
return EvaluateWithOperator(Children);
|
||||
}
|
||||
|
||||
private bool EvaluateWithOperator(IEnumerable<DataModelConditionPart> targets)
|
||||
{
|
||||
return BooleanOperator switch
|
||||
{
|
||||
BooleanOperator.And => targets.All(c => c.Evaluate()),
|
||||
BooleanOperator.Or => targets.Any(c => c.Evaluate()),
|
||||
BooleanOperator.AndNot => targets.All(c => !c.Evaluate()),
|
||||
BooleanOperator.OrNot => targets.Any(c => !c.Evaluate()),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
_disposed = true;
|
||||
foreach (DataModelConditionPart child in Children)
|
||||
child.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <inheritdoc />
|
||||
internal override bool EvaluateObject(object? target)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataModelConditionGroup");
|
||||
|
||||
// Empty groups are always true
|
||||
if (Children.Count == 0)
|
||||
return true;
|
||||
// Groups with only one child ignore the boolean operator
|
||||
if (Children.Count == 1)
|
||||
return Children[0].EvaluateObject(target);
|
||||
|
||||
return BooleanOperator switch
|
||||
{
|
||||
BooleanOperator.And => Children.All(c => c.EvaluateObject(target)),
|
||||
BooleanOperator.Or => Children.Any(c => c.EvaluateObject(target)),
|
||||
BooleanOperator.AndNot => Children.All(c => !c.EvaluateObject(target)),
|
||||
BooleanOperator.OrNot => Children.Any(c => !c.EvaluateObject(target)),
|
||||
_ => throw new ArgumentOutOfRangeException()
|
||||
};
|
||||
}
|
||||
|
||||
internal override void Save()
|
||||
{
|
||||
Entity.BooleanOperator = (int) BooleanOperator;
|
||||
|
||||
Entity.Children.Clear();
|
||||
Entity.Children.AddRange(Children.Select(c => c.GetEntity()));
|
||||
foreach (DataModelConditionPart child in Children)
|
||||
child.Save();
|
||||
}
|
||||
|
||||
internal override DataModelConditionPartEntity GetEntity()
|
||||
{
|
||||
return Entity;
|
||||
}
|
||||
|
||||
private void OnChildrenChanged(object? sender, EventArgs e)
|
||||
{
|
||||
ContainsEvents = Children.Any(c => c is DataModelConditionEvent);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a boolean operator
|
||||
/// </summary>
|
||||
public enum BooleanOperator
|
||||
{
|
||||
/// <summary>
|
||||
/// All the conditions in the group should evaluate to true
|
||||
/// </summary>
|
||||
And,
|
||||
|
||||
/// <summary>
|
||||
/// Any of the conditions in the group should evaluate to true
|
||||
/// </summary>
|
||||
Or,
|
||||
|
||||
/// <summary>
|
||||
/// All the conditions in the group should evaluate to false
|
||||
/// </summary>
|
||||
AndNot,
|
||||
|
||||
/// <summary>
|
||||
/// Any of the conditions in the group should evaluate to false
|
||||
/// </summary>
|
||||
OrNot
|
||||
}
|
||||
}
|
||||
@@ -1,276 +0,0 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Artemis.Storage.Entities.Profile.Abstract;
|
||||
using Artemis.Storage.Entities.Profile.Conditions;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A condition that evaluates one or more predicates inside a list
|
||||
/// </summary>
|
||||
public class DataModelConditionList : DataModelConditionPart
|
||||
{
|
||||
private bool _disposed;
|
||||
private bool _reinitializing;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DataModelConditionList" /> class
|
||||
/// </summary>
|
||||
/// <param name="parent"></param>
|
||||
public DataModelConditionList(DataModelConditionPart parent)
|
||||
{
|
||||
Parent = parent;
|
||||
Entity = new DataModelConditionListEntity();
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
internal DataModelConditionList(DataModelConditionPart parent, DataModelConditionListEntity entity)
|
||||
{
|
||||
Parent = parent;
|
||||
Entity = entity;
|
||||
ListOperator = (ListOperator) entity.ListOperator;
|
||||
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the list operator
|
||||
/// </summary>
|
||||
public ListOperator ListOperator { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path of the list property
|
||||
/// </summary>
|
||||
public DataModelPath? ListPath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the content of the list this predicate is evaluated on
|
||||
/// </summary>
|
||||
public Type? ListType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets whether the list contains primitives
|
||||
/// </summary>
|
||||
public bool IsPrimitiveList { get; set; }
|
||||
|
||||
internal DataModelConditionListEntity Entity { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public override bool Evaluate()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataModelConditionList");
|
||||
|
||||
if (ListPath == null || !ListPath.IsValid)
|
||||
return false;
|
||||
|
||||
return EvaluateObject(ListPath.GetValue());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the list the predicate is evaluated on
|
||||
/// </summary>
|
||||
/// <param name="path">The path pointing to the list inside the list</param>
|
||||
public void UpdateList(DataModelPath? path)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataModelConditionList");
|
||||
|
||||
if (path != null && !path.IsValid)
|
||||
throw new ArtemisCoreException("Cannot update list to an invalid path");
|
||||
|
||||
ListPath?.Dispose();
|
||||
ListPath = path != null ? new DataModelPath(path) : null;
|
||||
SubscribeToListPath();
|
||||
|
||||
// Remove the old root group that was tied to the old data model
|
||||
while (Children.Any())
|
||||
RemoveChild(Children[0]);
|
||||
|
||||
if (ListPath != null)
|
||||
{
|
||||
Type listType = ListPath.GetPropertyType()!;
|
||||
ListType = listType.GetGenericEnumerableType();
|
||||
IsPrimitiveList = ListType == null || ListType.IsPrimitive || ListType.IsEnum || ListType == typeof(string);
|
||||
|
||||
// Create a new root group
|
||||
AddChild(new DataModelConditionGroup(this));
|
||||
}
|
||||
else
|
||||
{
|
||||
ListType = null;
|
||||
}
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
_disposed = true;
|
||||
|
||||
ListPath?.Dispose();
|
||||
|
||||
foreach (DataModelConditionPart child in Children)
|
||||
child.Dispose();
|
||||
|
||||
base.Dispose(disposing);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
internal override bool EvaluateObject(object? target)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataModelConditionList");
|
||||
|
||||
if (!Children.Any())
|
||||
return false;
|
||||
if (!(target is IEnumerable enumerable))
|
||||
return false;
|
||||
|
||||
IEnumerable<object> objectList = enumerable.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 Save()
|
||||
{
|
||||
// Don't save an invalid state
|
||||
if (ListPath != null && !ListPath.IsValid)
|
||||
return;
|
||||
|
||||
// Target list
|
||||
ListPath?.Save();
|
||||
Entity.ListPath = ListPath?.Entity;
|
||||
|
||||
// Operator
|
||||
Entity.ListOperator = (int) ListOperator;
|
||||
|
||||
// Children
|
||||
Entity.Children.Clear();
|
||||
Entity.Children.AddRange(Children.Select(c => c.GetEntity()));
|
||||
foreach (DataModelConditionPart child in Children)
|
||||
child.Save();
|
||||
}
|
||||
|
||||
internal override DataModelConditionPartEntity GetEntity()
|
||||
{
|
||||
return Entity;
|
||||
}
|
||||
|
||||
internal void Initialize()
|
||||
{
|
||||
while (Children.Any())
|
||||
RemoveChild(Children[0]);
|
||||
|
||||
if (Entity.ListPath == null)
|
||||
return;
|
||||
|
||||
// Ensure the list path is valid and points to a list
|
||||
DataModelPath listPath = new(null, Entity.ListPath);
|
||||
Type listType = listPath.GetPropertyType()!;
|
||||
// Can't check this on an invalid list, if it becomes valid later lets hope for the best
|
||||
if (listPath.IsValid && !PointsToList(listPath))
|
||||
return;
|
||||
|
||||
ListPath = listPath;
|
||||
SubscribeToListPath();
|
||||
if (ListPath.IsValid)
|
||||
{
|
||||
ListType = listType.GetGenericEnumerableType();
|
||||
IsPrimitiveList = ListType == null || ListType.IsPrimitive || ListType.IsEnum || ListType == typeof(string);
|
||||
}
|
||||
else
|
||||
{
|
||||
ListType = null;
|
||||
IsPrimitiveList = false;
|
||||
}
|
||||
|
||||
// There should only be one child and it should be a group
|
||||
if (Entity.Children.FirstOrDefault() is DataModelConditionGroupEntity rootGroup)
|
||||
{
|
||||
AddChild(new DataModelConditionGroup(this, rootGroup));
|
||||
}
|
||||
else
|
||||
{
|
||||
Entity.Children.Clear();
|
||||
AddChild(new DataModelConditionGroup(this));
|
||||
}
|
||||
}
|
||||
|
||||
private bool PointsToList(DataModelPath dataModelPath)
|
||||
{
|
||||
Type? type = dataModelPath.GetPropertyType();
|
||||
return type?.IsGenericEnumerable() ?? false;
|
||||
}
|
||||
|
||||
private void SubscribeToListPath()
|
||||
{
|
||||
if (ListPath == null) return;
|
||||
ListPath.PathValidated += ListPathOnPathValidated;
|
||||
ListPath.PathInvalidated += ListPathOnPathInvalidated;
|
||||
}
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void ListPathOnPathValidated(object? sender, EventArgs e)
|
||||
{
|
||||
if (_reinitializing)
|
||||
return;
|
||||
|
||||
_reinitializing = true;
|
||||
ListPath?.Dispose();
|
||||
Initialize();
|
||||
_reinitializing = false;
|
||||
}
|
||||
|
||||
private void ListPathOnPathInvalidated(object? sender, EventArgs e)
|
||||
{
|
||||
if (_reinitializing)
|
||||
return;
|
||||
|
||||
_reinitializing = true;
|
||||
ListPath?.Dispose();
|
||||
Initialize();
|
||||
_reinitializing = false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a list operator
|
||||
/// </summary>
|
||||
public enum ListOperator
|
||||
{
|
||||
/// <summary>
|
||||
/// Any of the list items should evaluate to true
|
||||
/// </summary>
|
||||
Any,
|
||||
|
||||
/// <summary>
|
||||
/// All of the list items should evaluate to true
|
||||
/// </summary>
|
||||
All,
|
||||
|
||||
/// <summary>
|
||||
/// None of the list items should evaluate to true
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// A specific amount of the list items should evaluate to true
|
||||
/// </summary>
|
||||
Count
|
||||
}
|
||||
}
|
||||
@@ -1,165 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Core.Modules;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using Artemis.Storage.Entities.Profile.Conditions;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A predicate like evaluated inside a <see cref="DataModelConditionList" />
|
||||
/// </summary>
|
||||
public class DataModelConditionListPredicate : DataModelConditionPredicate
|
||||
{
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DataModelConditionListPredicate" /> class
|
||||
/// </summary>
|
||||
/// <param name="parent"></param>
|
||||
/// <param name="predicateType"></param>
|
||||
public DataModelConditionListPredicate(DataModelConditionPart parent, ProfileRightSideType predicateType)
|
||||
: base(parent, predicateType, new DataModelConditionListPredicateEntity())
|
||||
{
|
||||
DataModelConditionList = null!;
|
||||
ApplyParentList();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
internal DataModelConditionListPredicate(DataModelConditionPart parent, DataModelConditionListPredicateEntity entity)
|
||||
: base(parent, entity)
|
||||
{
|
||||
DataModelConditionList = null!;
|
||||
ApplyParentList();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data model condition list this predicate belongs to
|
||||
/// </summary>
|
||||
public DataModelConditionList DataModelConditionList { get; private set; }
|
||||
|
||||
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 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
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void InitializeLeftPath()
|
||||
{
|
||||
if (Entity.LeftPath != null)
|
||||
LeftPath = DataModelConditionList.ListType != null
|
||||
? new DataModelPath(ListPredicateWrapperDataModel.Create(
|
||||
DataModelConditionList.ListType,
|
||||
DataModelConditionList.ListPath?.GetPropertyDescription()?.ListItemName
|
||||
), Entity.LeftPath)
|
||||
: null;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void InitializeRightPath()
|
||||
{
|
||||
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightPath != null)
|
||||
{
|
||||
// Right side dynamic inside the list
|
||||
if (Entity.RightPath.WrapperType == PathWrapperType.List)
|
||||
{
|
||||
RightPath = DataModelConditionList.ListType != null
|
||||
? new DataModelPath(ListPredicateWrapperDataModel.Create(
|
||||
DataModelConditionList.ListType,
|
||||
DataModelConditionList.ListPath?.GetPropertyDescription()?.ListItemName
|
||||
), Entity.RightPath)
|
||||
: null;
|
||||
}
|
||||
// Right side dynamic
|
||||
else
|
||||
RightPath = new DataModelPath(null, Entity.RightPath);
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Modification
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type? GetPreferredRightSideType()
|
||||
{
|
||||
Type? preferredType = Operator?.RightSideType;
|
||||
Type? leftSideType = DataModelConditionList.IsPrimitiveList
|
||||
? DataModelConditionList.ListType
|
||||
: LeftPath?.GetPropertyType();
|
||||
if (preferredType == null)
|
||||
return null;
|
||||
|
||||
if (leftSideType != null && preferredType.IsAssignableFrom(leftSideType))
|
||||
preferredType = leftSideType;
|
||||
|
||||
return preferredType;
|
||||
}
|
||||
|
||||
#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)
|
||||
return false;
|
||||
|
||||
// Compare with a static value
|
||||
if (PredicateType == ProfileRightSideType.Static)
|
||||
{
|
||||
object? leftSideValue = GetListPathValue(LeftPath, target);
|
||||
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
|
||||
if (PredicateType == ProfileRightSideType.Dynamic)
|
||||
{
|
||||
// If the path targets a property inside the list, evaluate on the list path value instead of the right path value
|
||||
if (RightPath.Target is ListPredicateWrapperDataModel)
|
||||
return Operator.InternalEvaluate(GetListPathValue(LeftPath, target), GetListPathValue(RightPath, target));
|
||||
return Operator.InternalEvaluate(GetListPathValue(LeftPath, target), RightPath.GetValue());
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Core.Modules;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
internal class EventPredicateWrapperDataModel<T> : EventPredicateWrapperDataModel
|
||||
{
|
||||
[DataModelProperty(Name = "Event arguments", Description = "The arguments provided when the event triggers")]
|
||||
public T Arguments => (UntypedArguments is T typedArguments ? typedArguments : default)!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a datamodel that wraps the event arguments of an event
|
||||
/// </summary>
|
||||
public abstract class EventPredicateWrapperDataModel : DataModel
|
||||
{
|
||||
internal EventPredicateWrapperDataModel()
|
||||
{
|
||||
Module = Constants.CorePluginFeature;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the last arguments of this event as an object
|
||||
/// </summary>
|
||||
[DataModelIgnore]
|
||||
public object? UntypedArguments { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="EventPredicateWrapperDataModel" /> class
|
||||
/// </summary>
|
||||
public static EventPredicateWrapperDataModel Create(Type type)
|
||||
{
|
||||
object? instance = Activator.CreateInstance(typeof(EventPredicateWrapperDataModel<>).MakeGenericType(type));
|
||||
if (instance == null)
|
||||
throw new ArtemisCoreException($"Failed to create an instance of EventPredicateWrapperDataModel<T> for type {type.Name}");
|
||||
|
||||
return (EventPredicateWrapperDataModel) instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
using System;
|
||||
using System.Reflection;
|
||||
using Artemis.Core.Modules;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
internal class ListPredicateWrapperDataModel<T> : ListPredicateWrapperDataModel
|
||||
{
|
||||
public T Value => (UntypedValue is T typedValue ? typedValue : default)!;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a datamodel that wraps a value in a list
|
||||
/// </summary>
|
||||
public abstract class ListPredicateWrapperDataModel : DataModel
|
||||
{
|
||||
internal ListPredicateWrapperDataModel()
|
||||
{
|
||||
Module = Constants.CorePluginFeature;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value of this list as an object
|
||||
/// </summary>
|
||||
[DataModelIgnore]
|
||||
public object? UntypedValue { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the name of the list item
|
||||
/// </summary>
|
||||
[DataModelIgnore]
|
||||
public string? ItemName { get; set; }
|
||||
|
||||
#region Overrides of DataModel
|
||||
|
||||
/// <inheritdoc />
|
||||
public override DataModelPropertyAttribute? GetPropertyDescription(PropertyInfo propertyInfo)
|
||||
{
|
||||
if (!string.IsNullOrWhiteSpace(ItemName))
|
||||
return new DataModelPropertyAttribute {Name = ItemName};
|
||||
return base.GetPropertyDescription(propertyInfo);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="ListPredicateWrapperDataModel" /> class
|
||||
/// </summary>
|
||||
public static ListPredicateWrapperDataModel Create(Type type, string? name = null)
|
||||
{
|
||||
ListPredicateWrapperDataModel? instance = Activator.CreateInstance(typeof(ListPredicateWrapperDataModel<>).MakeGenericType(type)) as ListPredicateWrapperDataModel;
|
||||
if (instance == null)
|
||||
throw new ArtemisCoreException($"Failed to create an instance of ListPredicateWrapperDataModel<T> for type {type.Name}");
|
||||
|
||||
instance.ItemName = name;
|
||||
return instance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -17,20 +17,20 @@ namespace Artemis.Core
|
||||
{
|
||||
LayerProperty = dataBindingRegistration.LayerProperty;
|
||||
Entity = new DataBindingEntity();
|
||||
Script = new NodeScript<TProperty>(LayerProperty.PropertyDescription.Name ?? LayerProperty.Path, "");
|
||||
|
||||
ApplyRegistration(dataBindingRegistration);
|
||||
Save();
|
||||
ApplyDataBindingMode();
|
||||
}
|
||||
|
||||
internal DataBinding(LayerProperty<TLayerProperty> layerProperty, DataBindingEntity entity)
|
||||
{
|
||||
LayerProperty = layerProperty;
|
||||
Entity = entity;
|
||||
Script = new NodeScript<TProperty>(LayerProperty.PropertyDescription.Name ?? LayerProperty.Path, "");
|
||||
|
||||
// Load will add children so be initialized before that
|
||||
Load();
|
||||
ApplyDataBindingMode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -48,10 +48,7 @@ namespace Artemis.Core
|
||||
/// </summary>
|
||||
public DataBindingConverter<TLayerProperty, TProperty>? Converter { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the data binding mode
|
||||
/// </summary>
|
||||
public IDataBindingMode<TLayerProperty, TProperty>? DataBindingMode { get; private set; }
|
||||
public NodeScript<TProperty> Script { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the easing time of the data binding
|
||||
@@ -78,18 +75,18 @@ namespace Artemis.Core
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataBinding");
|
||||
|
||||
if (Converter == null || DataBindingMode == null)
|
||||
if (Converter == null)
|
||||
return baseValue;
|
||||
|
||||
TProperty value = DataBindingMode.GetValue(baseValue);
|
||||
Script.Run();
|
||||
|
||||
// If no easing is to be applied simple return whatever the current value is
|
||||
if (EasingTime == TimeSpan.Zero || !Converter.SupportsInterpolate)
|
||||
return value;
|
||||
return Script.Result;
|
||||
|
||||
// If the value changed, update the current and previous values used for easing
|
||||
if (!Equals(value, _currentValue))
|
||||
ResetEasing(value);
|
||||
if (!Equals(Script.Result, _currentValue))
|
||||
ResetEasing(Script.Result);
|
||||
|
||||
// Apply interpolation between the previous and current value
|
||||
return GetInterpolatedValue();
|
||||
@@ -118,7 +115,9 @@ namespace Artemis.Core
|
||||
|
||||
if (Registration != null)
|
||||
Registration.DataBinding = null;
|
||||
DataBindingMode?.Dispose();
|
||||
|
||||
Script?.Dispose();
|
||||
Script = null;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -221,57 +220,6 @@ namespace Artemis.Core
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#region Mode management
|
||||
|
||||
/// <summary>
|
||||
/// Changes the data binding mode of the data binding to the specified <paramref name="dataBindingMode" />
|
||||
/// </summary>
|
||||
public void ChangeDataBindingMode(DataBindingModeType dataBindingMode)
|
||||
{
|
||||
switch (dataBindingMode)
|
||||
{
|
||||
case DataBindingModeType.Direct:
|
||||
Entity.DataBindingMode = new DirectDataBindingEntity();
|
||||
break;
|
||||
case DataBindingModeType.Conditional:
|
||||
Entity.DataBindingMode = new ConditionalDataBindingEntity();
|
||||
break;
|
||||
default:
|
||||
Entity.DataBindingMode = null;
|
||||
break;
|
||||
}
|
||||
|
||||
ApplyDataBindingMode();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the current data binding mode with one based on the provided data binding mode entity
|
||||
/// </summary>
|
||||
/// <param name="dataBindingModeEntity">The data binding mode entity to base the new data binding mode upon</param>
|
||||
public void ApplyDataBindingEntity(IDataBindingModeEntity dataBindingModeEntity)
|
||||
{
|
||||
Entity.DataBindingMode = dataBindingModeEntity;
|
||||
ApplyDataBindingMode();
|
||||
}
|
||||
|
||||
private void ApplyDataBindingMode()
|
||||
{
|
||||
DataBindingMode?.Dispose();
|
||||
DataBindingMode = null;
|
||||
|
||||
switch (Entity.DataBindingMode)
|
||||
{
|
||||
case DirectDataBindingEntity directDataBindingEntity:
|
||||
DataBindingMode = new DirectDataBinding<TLayerProperty, TProperty>(this, directDataBindingEntity);
|
||||
break;
|
||||
case ConditionalDataBindingEntity conditionalDataBindingEntity:
|
||||
DataBindingMode = new ConditionalDataBinding<TLayerProperty, TProperty>(this, conditionalDataBindingEntity);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Storage
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -288,7 +236,10 @@ namespace Artemis.Core
|
||||
EasingTime = Entity.EasingTime;
|
||||
EasingFunction = (Easings.Functions) Entity.EasingFunction;
|
||||
|
||||
DataBindingMode?.Load();
|
||||
Script.Dispose();
|
||||
Script = Entity.NodeScript != null
|
||||
? new NodeScript<TProperty>(Entity.NodeScript)
|
||||
: new NodeScript<TProperty>(LayerProperty.PropertyDescription.Name ?? LayerProperty.Path, "");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@@ -307,30 +258,10 @@ namespace Artemis.Core
|
||||
Entity.EasingTime = EasingTime;
|
||||
Entity.EasingFunction = (int) EasingFunction;
|
||||
|
||||
DataBindingMode?.Save();
|
||||
Script?.Save();
|
||||
Entity.NodeScript = Script?.Entity;
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A mode that determines how the data binding is applied to the layer property
|
||||
/// </summary>
|
||||
public enum DataBindingModeType
|
||||
{
|
||||
/// <summary>
|
||||
/// Disables the data binding
|
||||
/// </summary>
|
||||
None,
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the layer property value with the data binding value
|
||||
/// </summary>
|
||||
Direct,
|
||||
|
||||
/// <summary>
|
||||
/// Replaces the layer property value with the data binding value
|
||||
/// </summary>
|
||||
Conditional
|
||||
}
|
||||
}
|
||||
-167
@@ -1,167 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Linq;
|
||||
using Artemis.Storage.Entities.Profile.DataBindings;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a data binding mode that applies a value depending on conditions
|
||||
/// </summary>
|
||||
public class ConditionalDataBinding<TLayerProperty, TProperty> : IDataBindingMode<TLayerProperty, TProperty>
|
||||
{
|
||||
private readonly List<DataBindingCondition<TLayerProperty, TProperty>> _conditions = new();
|
||||
private bool _disposed;
|
||||
|
||||
internal ConditionalDataBinding(DataBinding<TLayerProperty, TProperty> dataBinding, ConditionalDataBindingEntity entity)
|
||||
{
|
||||
DataBinding = dataBinding;
|
||||
Entity = entity;
|
||||
Load();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of conditions applied to this data binding
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<DataBindingCondition<TLayerProperty, TProperty>> Conditions => _conditions.AsReadOnly();
|
||||
|
||||
internal ConditionalDataBindingEntity Entity { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public DataBinding<TLayerProperty, TProperty> DataBinding { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public TProperty GetValue(TProperty baseValue)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("ConditionalDataBinding");
|
||||
|
||||
DataBindingCondition<TLayerProperty, TProperty>? condition = Conditions.FirstOrDefault(c => c.Evaluate());
|
||||
return condition == null ? baseValue : condition.Value;
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">
|
||||
/// <see langword="true" /> to release both managed and unmanaged resources;
|
||||
/// <see langword="false" /> to release only unmanaged resources.
|
||||
/// </param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_disposed = true;
|
||||
|
||||
foreach (DataBindingCondition<TLayerProperty, TProperty> dataBindingCondition in Conditions)
|
||||
dataBindingCondition.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Values
|
||||
|
||||
/// <summary>
|
||||
/// Adds a condition to the conditional data binding's <see cref="Conditions" /> collection
|
||||
/// </summary>
|
||||
/// <returns>The newly created <see cref="DataBindingCondition{TLayerProperty,TProperty}" /></returns>
|
||||
public DataBindingCondition<TLayerProperty, TProperty> AddCondition()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("ConditionalDataBinding");
|
||||
|
||||
DataBindingCondition<TLayerProperty, TProperty> condition = new(this);
|
||||
_conditions.Add(condition);
|
||||
|
||||
ApplyOrder();
|
||||
OnConditionsUpdated();
|
||||
|
||||
return condition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a condition from the conditional data binding's <see cref="Conditions" /> collection and disposes it
|
||||
/// </summary>
|
||||
/// <param name="condition"></param>
|
||||
public void RemoveCondition(DataBindingCondition<TLayerProperty, TProperty> condition)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("ConditionalDataBinding");
|
||||
if (!_conditions.Contains(condition))
|
||||
return;
|
||||
|
||||
_conditions.Remove(condition);
|
||||
condition.Dispose();
|
||||
|
||||
ApplyOrder();
|
||||
OnConditionsUpdated();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the current order of conditions to the <see cref="Conditions" /> collection
|
||||
/// </summary>
|
||||
public void ApplyOrder()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("ConditionalDataBinding");
|
||||
|
||||
_conditions.Sort((a, b) => a.Order.CompareTo(b.Order));
|
||||
for (int index = 0; index < _conditions.Count; index++)
|
||||
{
|
||||
DataBindingCondition<TLayerProperty, TProperty> condition = _conditions[index];
|
||||
condition.Order = index + 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Storage
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Load()
|
||||
{
|
||||
foreach (DataBindingConditionEntity dataBindingConditionEntity in Entity.Values)
|
||||
_conditions.Add(new DataBindingCondition<TLayerProperty, TProperty>(this, dataBindingConditionEntity));
|
||||
|
||||
ApplyOrder();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save()
|
||||
{
|
||||
Entity.Values.Clear();
|
||||
foreach (DataBindingCondition<TLayerProperty, TProperty> dataBindingCondition in Conditions)
|
||||
dataBindingCondition.Save();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a condition is added or removed
|
||||
/// </summary>
|
||||
public event EventHandler? ConditionsUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="ConditionsUpdated" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnConditionsUpdated()
|
||||
{
|
||||
ConditionsUpdated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
-121
@@ -1,121 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Storage.Entities.Profile.DataBindings;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class DataBindingCondition<TLayerProperty, TProperty> : IDataBindingCondition
|
||||
{
|
||||
private bool _disposed;
|
||||
|
||||
/// <summary>
|
||||
/// Creates a new instance of the <see cref="DataBindingCondition{TLayerProperty,TProperty}" /> class
|
||||
/// </summary>
|
||||
/// <param name="conditionalDataBinding">The conditional data binding this condition is applied too</param>
|
||||
internal DataBindingCondition(ConditionalDataBinding<TLayerProperty, TProperty> conditionalDataBinding)
|
||||
{
|
||||
ConditionalDataBinding = conditionalDataBinding ?? throw new ArgumentNullException(nameof(conditionalDataBinding));
|
||||
Order = conditionalDataBinding.Conditions.Count + 1;
|
||||
Condition = new DataModelConditionGroup(null);
|
||||
Value = default!;
|
||||
|
||||
Entity = new DataBindingConditionEntity();
|
||||
Save();
|
||||
}
|
||||
|
||||
internal DataBindingCondition(ConditionalDataBinding<TLayerProperty, TProperty> conditionalDataBinding, DataBindingConditionEntity entity)
|
||||
{
|
||||
ConditionalDataBinding = conditionalDataBinding ?? throw new ArgumentNullException(nameof(conditionalDataBinding));
|
||||
Entity = entity;
|
||||
Condition = null!;
|
||||
Value = default!;
|
||||
|
||||
Load();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the conditional data binding this condition is applied to
|
||||
/// </summary>
|
||||
public ConditionalDataBinding<TLayerProperty, TProperty> ConditionalDataBinding { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position at which the modifier appears on the data binding
|
||||
/// </summary>
|
||||
public int Order { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the value to be applied when the condition is met
|
||||
/// </summary>
|
||||
public TProperty Value { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the root group of the condition that must be met
|
||||
/// </summary>
|
||||
public DataModelConditionGroup Condition { get; private set; }
|
||||
|
||||
internal DataBindingConditionEntity Entity { get; set; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public bool Evaluate()
|
||||
{
|
||||
return Condition.Evaluate();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataBindingCondition");
|
||||
|
||||
if (!ConditionalDataBinding.Entity.Values.Contains(Entity))
|
||||
ConditionalDataBinding.Entity.Values.Add(Entity);
|
||||
|
||||
Entity.Condition = Condition.Entity;
|
||||
Condition.Save();
|
||||
|
||||
Entity.Value = CoreJson.SerializeObject(Value);
|
||||
Entity.Order = Order;
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Load()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataBindingCondition");
|
||||
|
||||
Condition = Entity.Condition != null
|
||||
? new DataModelConditionGroup(null, Entity.Condition)
|
||||
: new DataModelConditionGroup(null);
|
||||
|
||||
Value = (Entity.Value == null ? default : CoreJson.DeserializeObject<TProperty>(Entity.Value))!;
|
||||
Order = Entity.Order;
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">
|
||||
/// <see langword="true" /> to release both managed and unmanaged resources;
|
||||
/// <see langword="false" /> to release only unmanaged resources.
|
||||
/// </param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_disposed = true;
|
||||
Condition.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
-15
@@ -1,15 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a condition and a value inside a <see cref="ConditionalDataBinding{TLayerProperty,TProperty}" />
|
||||
/// </summary>
|
||||
public interface IDataBindingCondition : IStorageModel, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Evaluates the condition
|
||||
/// </summary>
|
||||
bool Evaluate();
|
||||
}
|
||||
}
|
||||
-94
@@ -1,94 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A modifier that changes the source value of a data binding in some way
|
||||
/// <para>
|
||||
/// To implement your own condition operator, inherit <see cref="DataBindingModifierType{TValue, TParameter}" /> or
|
||||
/// <see cref="DataBindingModifierType{TValue}" />
|
||||
/// </para>
|
||||
/// </summary>
|
||||
public abstract class BaseDataBindingModifierType
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the plugin this data binding modifier belongs to
|
||||
/// <para>Note: Not set until after registering</para>
|
||||
/// </summary>
|
||||
public Plugin? Plugin { get; internal set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the value type of this modifier type
|
||||
/// </summary>
|
||||
public abstract Type ValueType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parameter type of this modifier type. May be null if the modifier type does not support a parameter
|
||||
/// </summary>
|
||||
public abstract Type? ParameterType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the name of this modifier
|
||||
/// </summary>
|
||||
public abstract string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the icon of this modifier
|
||||
/// </summary>
|
||||
public abstract string? Icon { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the description of this modifier
|
||||
/// </summary>
|
||||
public virtual string? Description => null;
|
||||
|
||||
/// <summary>
|
||||
/// Gets the category of this modifier
|
||||
/// </summary>
|
||||
public virtual string? Category => null;
|
||||
|
||||
/// <summary>
|
||||
/// Returns whether the given type is supported by the modifier
|
||||
/// </summary>
|
||||
/// <param name="type">The type to check for, must be either the same or be castable to the target type</param>
|
||||
/// <param name="part">Which part of the modifier to check, the value or the parameter</param>
|
||||
public bool SupportsType(Type type, ModifierTypePart part)
|
||||
{
|
||||
if (type == null)
|
||||
return true;
|
||||
if (part == ModifierTypePart.Value)
|
||||
return ValueType.IsCastableFrom(type);
|
||||
return ParameterType != null && ParameterType.IsCastableFrom(type);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the modifier to the provided current value
|
||||
/// <para>
|
||||
/// This leaves the caller responsible for the types matching <see cref="ValueType" /> and
|
||||
/// <see cref="ParameterType" />
|
||||
/// </para>
|
||||
/// </summary>
|
||||
/// <param name="currentValue">The current value before modification, type should match <see cref="ValueType" /></param>
|
||||
/// <param name="parameterValue">
|
||||
/// The parameter to use for the modification, type should match <see cref="ParameterType" />
|
||||
/// </param>
|
||||
/// <returns>The modified value, with a type of <see cref="ValueType" /></returns>
|
||||
internal abstract object? InternalApply(object? currentValue, object? parameterValue);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Represents a part of a modifier type
|
||||
/// </summary>
|
||||
public enum ModifierTypePart
|
||||
{
|
||||
/// <summary>
|
||||
/// The value part of a modifier, backed by <see cref="DataBindingModifierType{TValue}.ValueType" />
|
||||
/// </summary>
|
||||
Value,
|
||||
|
||||
/// <summary>
|
||||
/// The parameter part of a modifier, backed by <see cref="DataBindingModifierType{TValue,TParameter}.ParameterType" />
|
||||
/// </summary>
|
||||
Parameter
|
||||
}
|
||||
}
|
||||
@@ -1,324 +0,0 @@
|
||||
using System;
|
||||
using Artemis.Storage.Entities.Profile.DataBindings;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public class DataBindingModifier<TLayerProperty, TProperty> : IDataBindingModifier
|
||||
{
|
||||
private bool _disposed;
|
||||
|
||||
internal DataBindingModifier(DirectDataBinding<TLayerProperty, TProperty> directDataBinding, ProfileRightSideType parameterType)
|
||||
{
|
||||
DirectDataBinding = directDataBinding ?? throw new ArgumentNullException(nameof(directDataBinding));
|
||||
Order = directDataBinding.Modifiers.Count + 1;
|
||||
ParameterType = parameterType;
|
||||
Entity = new DataBindingModifierEntity();
|
||||
Initialize();
|
||||
Save();
|
||||
}
|
||||
|
||||
internal DataBindingModifier(DirectDataBinding<TLayerProperty, TProperty> directDataBinding, DataBindingModifierEntity entity)
|
||||
{
|
||||
DirectDataBinding = directDataBinding ?? throw new ArgumentNullException(nameof(directDataBinding));
|
||||
Entity = entity;
|
||||
Load();
|
||||
Initialize();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of modifier that is being applied
|
||||
/// </summary>
|
||||
public BaseDataBindingModifierType? ModifierType { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the direct data binding this modifier is applied to
|
||||
/// </summary>
|
||||
public DirectDataBinding<TLayerProperty, TProperty> DirectDataBinding { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the type of the parameter, can either be dynamic (based on a data model value) or static
|
||||
/// </summary>
|
||||
public ProfileRightSideType ParameterType { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the position at which the modifier appears on the data binding
|
||||
/// </summary>
|
||||
public int Order { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path of the parameter property
|
||||
/// </summary>
|
||||
public DataModelPath? ParameterPath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the parameter static value, only used it <see cref="ParameterType" /> is
|
||||
/// <see cref="ProfileRightSideType.Static" />
|
||||
/// </summary>
|
||||
public object? ParameterStaticValue { get; private set; }
|
||||
|
||||
internal DataBindingModifierEntity Entity { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Applies the modifier to the provided value
|
||||
/// </summary>
|
||||
/// <param name="currentValue">The value to apply the modifier to, should be of the same type as the data binding target</param>
|
||||
/// <returns>The modified value</returns>
|
||||
public object? Apply(object? currentValue)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataBindingModifier");
|
||||
|
||||
if (ModifierType == null)
|
||||
return currentValue;
|
||||
|
||||
if (ModifierType.ParameterType == null)
|
||||
return ModifierType.InternalApply(currentValue, null);
|
||||
|
||||
if (ParameterType == ProfileRightSideType.Dynamic && ParameterPath != null && ParameterPath.IsValid)
|
||||
{
|
||||
object? value = ParameterPath.GetValue();
|
||||
return ModifierType.InternalApply(currentValue, value);
|
||||
}
|
||||
|
||||
if (ParameterType == ProfileRightSideType.Static)
|
||||
return ModifierType.InternalApply(currentValue, ParameterStaticValue);
|
||||
|
||||
return currentValue;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the modifier type of the modifier and re-compiles the expression
|
||||
/// </summary>
|
||||
/// <param name="modifierType"></param>
|
||||
public void UpdateModifierType(BaseDataBindingModifierType? modifierType)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataBindingModifier");
|
||||
|
||||
if (modifierType == null)
|
||||
{
|
||||
ModifierType = null;
|
||||
return;
|
||||
}
|
||||
|
||||
Type? targetType = DirectDataBinding.DataBinding.GetTargetType();
|
||||
if (targetType != null && !modifierType.SupportsType(targetType, ModifierTypePart.Value))
|
||||
throw new ArtemisCoreException($"Cannot apply modifier type {modifierType.GetType().Name} to this modifier because " +
|
||||
$"it does not support this data binding's type {targetType.Name}");
|
||||
|
||||
ModifierType = modifierType;
|
||||
ValidateParameter();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the parameter of the modifier and makes the modifier dynamic
|
||||
/// </summary>
|
||||
/// <param name="path">The path pointing to the parameter</param>
|
||||
public void UpdateParameterDynamic(DataModelPath? path)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataBindingModifier");
|
||||
|
||||
if (path != null && !path.IsValid)
|
||||
throw new ArtemisCoreException("Cannot update parameter of data binding modifier to an invalid path");
|
||||
|
||||
ParameterPath?.Dispose();
|
||||
ParameterPath = path != null ? new DataModelPath(path) : null;
|
||||
|
||||
ParameterType = ProfileRightSideType.Dynamic;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the parameter of the modifier, makes the modifier static and re-compiles the expression
|
||||
/// </summary>
|
||||
/// <param name="staticValue">The static value to use as a parameter</param>
|
||||
public void UpdateParameterStatic(object? staticValue)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataBindingModifier");
|
||||
|
||||
ParameterType = ProfileRightSideType.Static;
|
||||
ParameterPath?.Dispose();
|
||||
ParameterPath = null;
|
||||
|
||||
Type? parameterType = ModifierType?.ParameterType ?? DirectDataBinding.DataBinding.GetTargetType();
|
||||
|
||||
// If not null ensure the types match and if not, convert it
|
||||
if (parameterType == null || staticValue != null && staticValue.GetType() == parameterType)
|
||||
ParameterStaticValue = staticValue;
|
||||
else if (staticValue != null)
|
||||
ParameterStaticValue = Convert.ChangeType(staticValue, parameterType);
|
||||
// If null create a default instance for value types or simply make it null for reference types
|
||||
else if (parameterType.IsValueType)
|
||||
ParameterStaticValue = Activator.CreateInstance(parameterType);
|
||||
else
|
||||
ParameterStaticValue = null;
|
||||
}
|
||||
|
||||
private void ValidateParameter()
|
||||
{
|
||||
if (ModifierType == null)
|
||||
return;
|
||||
|
||||
if (ParameterType == ProfileRightSideType.Dynamic)
|
||||
{
|
||||
if (ParameterPath == null || !ParameterPath.IsValid)
|
||||
return;
|
||||
|
||||
Type parameterType = ParameterPath.GetPropertyType()!;
|
||||
if (!ModifierType.SupportsType(parameterType, ModifierTypePart.Parameter))
|
||||
UpdateParameterDynamic(null);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ParameterStaticValue == null)
|
||||
return;
|
||||
|
||||
if (!ModifierType.SupportsType(ParameterStaticValue.GetType(), ModifierTypePart.Parameter))
|
||||
UpdateParameterStatic(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void Initialize()
|
||||
{
|
||||
DataBindingModifierTypeStore.DataBindingModifierAdded += DataBindingModifierTypeStoreOnDataBindingModifierAdded;
|
||||
DataBindingModifierTypeStore.DataBindingModifierRemoved += DataBindingModifierTypeStoreOnDataBindingModifierRemoved;
|
||||
|
||||
// Modifier type
|
||||
if (Entity.ModifierTypePluginGuid != null && ModifierType == null)
|
||||
{
|
||||
BaseDataBindingModifierType? modifierType = DataBindingModifierTypeStore.Get(Entity.ModifierTypePluginGuid.Value, Entity.ModifierType)?.DataBindingModifierType;
|
||||
if (modifierType != null)
|
||||
UpdateModifierType(modifierType);
|
||||
}
|
||||
|
||||
// Dynamic parameter
|
||||
if (ParameterType == ProfileRightSideType.Dynamic && Entity.ParameterPath != null)
|
||||
{
|
||||
ParameterPath = new DataModelPath(null, Entity.ParameterPath);
|
||||
}
|
||||
// Static parameter
|
||||
else if (ParameterType == ProfileRightSideType.Static && Entity.ParameterStaticValue != null)
|
||||
{
|
||||
// Use the target type so JSON.NET has a better idea what to do
|
||||
Type? parameterType = ModifierType?.ParameterType ?? DirectDataBinding.DataBinding.GetTargetType();
|
||||
object? staticValue = null;
|
||||
|
||||
try
|
||||
{
|
||||
staticValue = parameterType != null
|
||||
? CoreJson.DeserializeObject(Entity.ParameterStaticValue, parameterType)
|
||||
: CoreJson.DeserializeObject(Entity.ParameterStaticValue);
|
||||
}
|
||||
// If deserialization fails, use the type's default
|
||||
catch (JsonSerializationException e)
|
||||
{
|
||||
DeserializationLogger.LogModifierDeserializationFailure(GetType().Name, e);
|
||||
if (parameterType != null)
|
||||
staticValue = Activator.CreateInstance(parameterType);
|
||||
}
|
||||
|
||||
UpdateParameterStatic(staticValue);
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataBindingModifier");
|
||||
|
||||
// Don't save an invalid state
|
||||
if (ParameterPath != null && !ParameterPath.IsValid)
|
||||
return;
|
||||
|
||||
if (!DirectDataBinding.Entity.Modifiers.Contains(Entity))
|
||||
DirectDataBinding.Entity.Modifiers.Add(Entity);
|
||||
|
||||
// Modifier
|
||||
if (ModifierType?.Plugin != null)
|
||||
{
|
||||
Entity.ModifierType = ModifierType.GetType().Name;
|
||||
Entity.ModifierTypePluginGuid = ModifierType.Plugin.Guid;
|
||||
}
|
||||
|
||||
// General
|
||||
Entity.Order = Order;
|
||||
Entity.ParameterType = (int) ParameterType;
|
||||
|
||||
// Parameter
|
||||
ParameterPath?.Save();
|
||||
Entity.ParameterPath = ParameterPath?.Entity;
|
||||
|
||||
Entity.ParameterStaticValue = CoreJson.SerializeObject(ParameterStaticValue);
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Load()
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DataBindingModifier");
|
||||
|
||||
// Modifier type is done during Initialize
|
||||
|
||||
// General
|
||||
Order = Entity.Order;
|
||||
ParameterType = (ProfileRightSideType) Entity.ParameterType;
|
||||
|
||||
// Parameter is done during initialize
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">
|
||||
/// <see langword="true" /> to release both managed and unmanaged resources;
|
||||
/// <see langword="false" /> to release only unmanaged resources.
|
||||
/// </param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_disposed = true;
|
||||
|
||||
DataBindingModifierTypeStore.DataBindingModifierAdded -= DataBindingModifierTypeStoreOnDataBindingModifierAdded;
|
||||
DataBindingModifierTypeStore.DataBindingModifierRemoved -= DataBindingModifierTypeStoreOnDataBindingModifierRemoved;
|
||||
|
||||
ParameterPath?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Event handlers
|
||||
|
||||
private void DataBindingModifierTypeStoreOnDataBindingModifierAdded(object? sender, DataBindingModifierTypeStoreEvent e)
|
||||
{
|
||||
if (ModifierType != null)
|
||||
return;
|
||||
|
||||
BaseDataBindingModifierType modifierType = e.TypeRegistration.DataBindingModifierType;
|
||||
if (modifierType.Plugin!.Guid == Entity.ModifierTypePluginGuid && modifierType.GetType().Name == Entity.ModifierType)
|
||||
UpdateModifierType(modifierType);
|
||||
}
|
||||
|
||||
private void DataBindingModifierTypeStoreOnDataBindingModifierRemoved(object? sender, DataBindingModifierTypeStoreEvent e)
|
||||
{
|
||||
if (e.TypeRegistration.DataBindingModifierType == ModifierType)
|
||||
UpdateModifierType(null);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,81 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// A modifier that changes the source value of a data binding in some way using a parameter
|
||||
/// </summary>
|
||||
public abstract class DataBindingModifierType<TValue, TParameter> : BaseDataBindingModifierType
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override Type ValueType => typeof(TValue);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type ParameterType => typeof(TParameter);
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever the modifier must apply to a specific value
|
||||
/// </summary>
|
||||
/// <param name="currentValue">
|
||||
/// The current value before modification
|
||||
/// </param>
|
||||
/// <param name="parameterValue">
|
||||
/// The parameter to use for the modification
|
||||
/// </param>
|
||||
/// <returns>The modified value></returns>
|
||||
public abstract TValue Apply(TValue currentValue, TParameter parameterValue);
|
||||
|
||||
/// <inheritdoc />
|
||||
internal override object? InternalApply(object? currentValue, object? parameterValue)
|
||||
{
|
||||
// TODO: Can we avoid boxing/unboxing?
|
||||
TValue current;
|
||||
if (currentValue != null)
|
||||
current = (TValue) Convert.ChangeType(currentValue, typeof(TValue));
|
||||
else
|
||||
current = default;
|
||||
|
||||
TParameter parameter;
|
||||
if (parameterValue != null)
|
||||
parameter = (TParameter) Convert.ChangeType(parameterValue, typeof(TParameter));
|
||||
else
|
||||
parameter = default;
|
||||
|
||||
return Apply(current!, parameter!);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A modifier that changes the source value of a data binding in some way
|
||||
/// </summary>
|
||||
public abstract class DataBindingModifierType<TValue> : BaseDataBindingModifierType
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public override Type ValueType => typeof(TValue);
|
||||
|
||||
/// <inheritdoc />
|
||||
public override Type? ParameterType => null;
|
||||
|
||||
/// <summary>
|
||||
/// Called whenever the modifier must apply to a specific value
|
||||
/// </summary>
|
||||
/// <param name="currentValue">
|
||||
/// The current value before modification
|
||||
/// </param>
|
||||
/// <returns>The modified value</returns>
|
||||
public abstract TValue Apply(TValue currentValue);
|
||||
|
||||
/// <inheritdoc />
|
||||
internal override object? InternalApply(object? currentValue, object? parameterValue)
|
||||
{
|
||||
// TODO: Can we avoid boxing/unboxing?
|
||||
TValue current;
|
||||
if (currentValue != null)
|
||||
current = (TValue) Convert.ChangeType(currentValue, typeof(TValue));
|
||||
else
|
||||
current = default;
|
||||
|
||||
return Apply(current!);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,11 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Modifies a data model value in a way defined by the modifier type
|
||||
/// </summary>
|
||||
public interface IDataBindingModifier : IStorageModel, IDisposable
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,217 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using Artemis.Storage.Entities.Profile.DataBindings;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a data binding mode that directly applies a data model value to a data binding
|
||||
/// </summary>
|
||||
public class DirectDataBinding<TLayerProperty, TProperty> : IDataBindingMode<TLayerProperty, TProperty>
|
||||
{
|
||||
private readonly List<DataBindingModifier<TLayerProperty, TProperty>> _modifiers = new();
|
||||
private bool _disposed;
|
||||
|
||||
internal DirectDataBinding(DataBinding<TLayerProperty, TProperty> dataBinding, DirectDataBindingEntity entity)
|
||||
{
|
||||
DataBinding = dataBinding;
|
||||
Entity = entity;
|
||||
|
||||
Load();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the path of the source property
|
||||
/// </summary>
|
||||
public DataModelPath? SourcePath { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets a list of modifiers applied to this data binding
|
||||
/// </summary>
|
||||
public ReadOnlyCollection<DataBindingModifier<TLayerProperty, TProperty>> Modifiers => _modifiers.AsReadOnly();
|
||||
|
||||
internal DirectDataBindingEntity Entity { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public DataBinding<TLayerProperty, TProperty> DataBinding { get; }
|
||||
|
||||
/// <inheritdoc />
|
||||
public TProperty GetValue(TProperty baseValue)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DirectDataBinding");
|
||||
|
||||
if (SourcePath == null || !SourcePath.IsValid || DataBinding.Converter == null)
|
||||
return baseValue;
|
||||
|
||||
object? dataBindingValue = SourcePath.GetValue();
|
||||
foreach (DataBindingModifier<TLayerProperty, TProperty> dataBindingModifier in Modifiers)
|
||||
dataBindingValue = dataBindingModifier.Apply(dataBindingValue);
|
||||
|
||||
return DataBinding.Converter.ConvertFromObject(dataBindingValue);
|
||||
}
|
||||
|
||||
#region IDisposable
|
||||
|
||||
/// <summary>
|
||||
/// Releases the unmanaged resources used by the object and optionally releases the managed resources.
|
||||
/// </summary>
|
||||
/// <param name="disposing">
|
||||
/// <see langword="true" /> to release both managed and unmanaged resources;
|
||||
/// <see langword="false" /> to release only unmanaged resources.
|
||||
/// </param>
|
||||
protected virtual void Dispose(bool disposing)
|
||||
{
|
||||
if (disposing)
|
||||
{
|
||||
_disposed = true;
|
||||
|
||||
foreach (DataBindingModifier<TLayerProperty, TProperty> dataBindingModifier in Modifiers)
|
||||
dataBindingModifier.Dispose();
|
||||
|
||||
SourcePath?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Dispose()
|
||||
{
|
||||
Dispose(true);
|
||||
GC.SuppressFinalize(this);
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Storage
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Load()
|
||||
{
|
||||
// Source
|
||||
if (Entity.SourcePath != null)
|
||||
SourcePath = new DataModelPath(null, Entity.SourcePath);
|
||||
|
||||
// Modifiers
|
||||
foreach (DataBindingModifierEntity dataBindingModifierEntity in Entity.Modifiers)
|
||||
_modifiers.Add(new DataBindingModifier<TLayerProperty, TProperty>(this, dataBindingModifierEntity));
|
||||
|
||||
ApplyOrder();
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public void Save()
|
||||
{
|
||||
// Don't save an invalid state
|
||||
if (SourcePath != null && !SourcePath.IsValid)
|
||||
return;
|
||||
|
||||
SourcePath?.Save();
|
||||
Entity.SourcePath = SourcePath?.Entity;
|
||||
|
||||
// Modifiers
|
||||
Entity.Modifiers.Clear();
|
||||
foreach (DataBindingModifier<TLayerProperty, TProperty> dataBindingModifier in Modifiers)
|
||||
dataBindingModifier.Save();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Source
|
||||
|
||||
/// <summary>
|
||||
/// Returns the type of the source property of this data binding
|
||||
/// </summary>
|
||||
public Type? GetSourceType()
|
||||
{
|
||||
return SourcePath?.GetPropertyType();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Updates the source of the data binding
|
||||
/// </summary>
|
||||
/// <param name="path">The path pointing to the source</param>
|
||||
public void UpdateSource(DataModelPath? path)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DirectDataBinding");
|
||||
|
||||
if (path != null && !path.IsValid)
|
||||
throw new ArtemisCoreException("Cannot update source of data binding to an invalid path");
|
||||
|
||||
SourcePath?.Dispose();
|
||||
SourcePath = path != null ? new DataModelPath(path) : null;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Modifiers
|
||||
|
||||
/// <summary>
|
||||
/// Adds a modifier to the direct data binding's <see cref="Modifiers" /> collection
|
||||
/// </summary>
|
||||
/// <param name="type">The type of the parameter, can either be dynamic (based on a data model value) or static</param>
|
||||
public DataBindingModifier<TLayerProperty, TProperty> AddModifier(ProfileRightSideType type)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DirectDataBinding");
|
||||
|
||||
DataBindingModifier<TLayerProperty, TProperty> modifier = new(this, type);
|
||||
_modifiers.Add(modifier);
|
||||
|
||||
ApplyOrder();
|
||||
OnModifiersUpdated();
|
||||
|
||||
return modifier;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Removes a modifier from the direct data binding's <see cref="Modifiers" /> collection and disposes it
|
||||
/// </summary>
|
||||
public void RemoveModifier(DataBindingModifier<TLayerProperty, TProperty> modifier)
|
||||
{
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("DirectDataBinding");
|
||||
if (!_modifiers.Contains(modifier))
|
||||
return;
|
||||
|
||||
_modifiers.Remove(modifier);
|
||||
modifier.Dispose();
|
||||
|
||||
ApplyOrder();
|
||||
OnModifiersUpdated();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies the current order of conditions to the <see cref="Modifiers" /> collection
|
||||
/// </summary>
|
||||
public void ApplyOrder()
|
||||
{
|
||||
_modifiers.Sort((a, b) => a.Order.CompareTo(b.Order));
|
||||
for (int index = 0; index < _modifiers.Count; index++)
|
||||
{
|
||||
DataBindingModifier<TLayerProperty, TProperty> modifier = _modifiers[index];
|
||||
modifier.Order = index + 1;
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region Events
|
||||
|
||||
/// <summary>
|
||||
/// Occurs when a modifier is added or removed
|
||||
/// </summary>
|
||||
public event EventHandler? ModifiersUpdated;
|
||||
|
||||
/// <summary>
|
||||
/// Invokes the <see cref="ModifiersUpdated" /> event
|
||||
/// </summary>
|
||||
protected virtual void OnModifiersUpdated()
|
||||
{
|
||||
ModifiersUpdated?.Invoke(this, EventArgs.Empty);
|
||||
}
|
||||
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -1,22 +0,0 @@
|
||||
using System;
|
||||
|
||||
namespace Artemis.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// Represents a data binding mode
|
||||
/// </summary>
|
||||
public interface IDataBindingMode<TLayerProperty, TProperty> : IStorageModel, IDisposable
|
||||
{
|
||||
/// <summary>
|
||||
/// Gets the data binding this mode is applied to
|
||||
/// </summary>
|
||||
DataBinding<TLayerProperty, TProperty> DataBinding { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets the current value of the data binding
|
||||
/// </summary>
|
||||
/// <param name="baseValue">The base value of the property the data binding is applied to</param>
|
||||
/// <returns></returns>
|
||||
TProperty GetValue(TProperty baseValue);
|
||||
}
|
||||
}
|
||||
@@ -363,13 +363,6 @@ namespace Artemis.Core
|
||||
|
||||
Entity.Path = Path;
|
||||
Entity.DataModelId = DataModelId;
|
||||
|
||||
Entity.WrapperType = Target switch
|
||||
{
|
||||
ListPredicateWrapperDataModel _ => PathWrapperType.List,
|
||||
EventPredicateWrapperDataModel _ => PathWrapperType.Event,
|
||||
_ => PathWrapperType.None
|
||||
};
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -7,7 +7,6 @@ using Artemis.Core.LayerEffects.Placeholder;
|
||||
using Artemis.Core.Properties;
|
||||
using Artemis.Storage.Entities.Profile;
|
||||
using Artemis.Storage.Entities.Profile.Abstract;
|
||||
using Newtonsoft.Json;
|
||||
using SkiaSharp;
|
||||
|
||||
namespace Artemis.Core
|
||||
@@ -68,11 +67,7 @@ namespace Artemis.Core
|
||||
internal void LoadRenderElement()
|
||||
{
|
||||
DisplayCondition = RenderElementEntity.NodeScript != null ? new NodeScript<bool>(RenderElementEntity.NodeScript) : null;
|
||||
|
||||
// DisplayCondition = RenderElementEntity.DisplayCondition != null
|
||||
// ? new DataModelConditionGroup(null, RenderElementEntity.DisplayCondition)
|
||||
// : new DataModelConditionGroup(null);
|
||||
|
||||
|
||||
Timeline = RenderElementEntity.Timeline != null
|
||||
? new Timeline(RenderElementEntity.Timeline)
|
||||
: new Timeline();
|
||||
|
||||
@@ -130,7 +130,7 @@ namespace Artemis.Core
|
||||
/// Gets the data model condition that must evaluate to <see langword="true" /> for this profile to be activated
|
||||
/// alongside any activation requirements of the <see cref="Module" />, if set
|
||||
/// </summary>
|
||||
public DataModelConditionGroup? ActivationCondition { get; set; }
|
||||
public NodeScript<bool>? ActivationCondition { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the module this profile uses
|
||||
@@ -168,7 +168,13 @@ namespace Artemis.Core
|
||||
if (_disposed)
|
||||
throw new ObjectDisposedException("ProfileConfiguration");
|
||||
|
||||
ActivationConditionMet = ActivationCondition == null || ActivationCondition.Evaluate();
|
||||
if (ActivationCondition == null)
|
||||
ActivationConditionMet = true;
|
||||
else
|
||||
{
|
||||
ActivationCondition.Run();
|
||||
ActivationConditionMet = ActivationCondition.Result;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -231,7 +237,8 @@ namespace Artemis.Core
|
||||
|
||||
Icon.Load();
|
||||
|
||||
ActivationCondition = Entity.ActivationCondition != null ? new DataModelConditionGroup(null, Entity.ActivationCondition) : null;
|
||||
ActivationCondition?.Dispose();
|
||||
ActivationCondition = Entity.ActivationCondition != null ? new NodeScript<bool>(Entity.ActivationCondition) : null;
|
||||
|
||||
EnableHotkey = Entity.EnableHotkey != null ? new ProfileConfigurationHotkey(Entity.EnableHotkey) : null;
|
||||
DisableHotkey = Entity.DisableHotkey != null ? new ProfileConfigurationHotkey(Entity.DisableHotkey) : null;
|
||||
|
||||
Reference in new issue
Block a user