Data model events - WIP commit

This commit is contained in:
SpoinkyNL committed 2020-10-25 00:21:43 +02:00
1 parent b8112601df
commit 03b0e83ed8
19 files changed
+554 -28

No files matched your search

@@ -13,20 +13,30 @@ namespace Artemis.Core
/// <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)
leftSide = (TLeftSide) Convert.ChangeType(leftSideValue, typeof(TLeftSide));
{
if (leftSideValue.GetType() != typeof(TLeftSide))
leftSide = (TLeftSide) Convert.ChangeType(leftSideValue, typeof(TLeftSide));
else
leftSide = (TLeftSide) leftSideValue;
}
else
leftSide = default;
TRightSide rightSide;
if (rightSideValue != null)
rightSide = (TRightSide) Convert.ChangeType(rightSideValue, typeof(TRightSide));
{
if (rightSideValue.GetType() != typeof(TRightSide))
rightSide = (TRightSide) Convert.ChangeType(rightSideValue, typeof(TRightSide));
else
rightSide = (TRightSide) rightSideValue;
}
else
rightSide = default;
@@ -57,7 +67,12 @@ namespace Artemis.Core
// TODO: Can we avoid boxing/unboxing?
TLeftSide leftSide;
if (leftSideValue != null)
leftSide = (TLeftSide) Convert.ChangeType(leftSideValue, typeof(TLeftSide));
{
if (leftSideValue.GetType() != typeof(TLeftSide))
leftSide = (TLeftSide)Convert.ChangeType(leftSideValue, typeof(TLeftSide));
else
leftSide = (TLeftSide)leftSideValue;
}
else
leftSide = default;
@@ -0,0 +1,189 @@
using System;
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? _event;
private bool _eventTriggered;
/// <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; }
internal DataModelConditionEventEntity Entity { get; set; }
/// <inheritdoc />
public override bool Evaluate()
{
if (_disposed)
throw new ObjectDisposedException("DataModelConditionEvent");
// Ensure the event has not been replaced
if (EventPath?.GetValue() is IDataModelEvent dataModelEvent && _event != dataModelEvent)
SubscribeToDataModelEvent(dataModelEvent);
// Only evaluate to true once every time the event has been triggered
if (!_eventTriggered)
return false;
_eventTriggered = false;
return true;
}
private void SubscribeToDataModelEvent(IDataModelEvent dataModelEvent)
{
if (_event != null)
_event.EventTriggered -= OnEventTriggered;
_event = dataModelEvent;
if (_event != null)
_event.EventTriggered += OnEventTriggered;
}
/// <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();
}
#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;
}
internal override DataModelConditionPartEntity GetEntity()
{
return Entity;
}
internal void Initialize()
{
if (Entity.EventPath == null)
return;
// Ensure the list path is valid and points to a list
DataModelPath eventPath = new DataModelPath(null, Entity.EventPath);
// Can't check this on an invalid list, if it becomes valid later lets hope for the best
if (eventPath.IsValid && !PointsToEvent(eventPath))
return;
EventPath = eventPath;
SubscribeToEventPath();
}
private bool PointsToEvent(DataModelPath dataModelPath)
{
Type? type = dataModelPath.GetPropertyType();
if (type == null)
return false;
return typeof(IDataModelEvent).IsAssignableFrom(type);
}
private void SubscribeToEventPath()
{
if (EventPath == null) return;
EventPath.PathValidated += EventPathOnPathValidated;
EventPath.PathInvalidated += EventPathOnPathInvalidated;
}
#region Event handlers
private void OnEventTriggered(object? sender, EventArgs e)
{
_eventTriggered = true;
}
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
}
}
@@ -180,7 +180,7 @@ namespace Artemis.Core
DataModelPath listPath = new DataModelPath(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 && !listPath.PointsToList)
if (listPath.IsValid && !PointsToList(listPath))
return;
ListPath = listPath;
@@ -208,6 +208,12 @@ namespace Artemis.Core
}
}
private bool PointsToList(DataModelPath dataModelPath)
{
Type? type = dataModelPath.GetPropertyType();
return type?.IsGenericEnumerable() ?? false;
}
private void SubscribeToListPath()
{
if (ListPath == null) return;
@@ -0,0 +1,85 @@
using System;
using Artemis.Core.DataModelExpansions;
namespace Artemis.Core
{
/// <summary>
/// Represents a data model event with event arguments of type <typeparamref name="T" />
/// </summary>
public class DataModelEvent<T> : IDataModelEvent where T : DataModelEventArgs
{
/// <summary>
/// Trigger the event with the given <paramref name="eventArgs" />
/// </summary>
/// <param name="eventArgs">The event argument to pass to the event</param>
public void Trigger(T eventArgs)
{
if (eventArgs == null) throw new ArgumentNullException(nameof(eventArgs));
eventArgs.TriggerTime = DateTime.Now;
LastEventArguments = eventArgs;
LastTrigger = DateTime.Now;
TriggerCount++;
OnEventTriggered();
}
internal virtual void OnEventTriggered()
{
EventTriggered?.Invoke(this, EventArgs.Empty);
}
/// <inheritdoc />
public DateTime LastTrigger { get; private set; }
/// <inheritdoc />
public int TriggerCount { get; private set; }
/// <summary>
/// Gets the event arguments of the last time the event was triggered
/// </summary>
public T? LastEventArguments { get; private set; }
/// <inheritdoc />
[DataModelIgnore]
public Type ArgumentsType => typeof(T);
/// <inheritdoc />
public event EventHandler? EventTriggered;
}
/// <summary>
/// Represents a data model event without event arguments
/// </summary>
public class DataModelEvent : IDataModelEvent
{
/// <summary>
/// Trigger the event
/// </summary>
public void Trigger()
{
LastTrigger = DateTime.Now;
TriggerCount++;
OnEventTriggered();
}
internal virtual void OnEventTriggered()
{
EventTriggered?.Invoke(this, EventArgs.Empty);
}
/// <inheritdoc />
public DateTime LastTrigger { get; private set; }
/// <inheritdoc />
public int TriggerCount { get; private set; }
/// <inheritdoc />
[DataModelIgnore]
public Type? ArgumentsType => null;
/// <inheritdoc />
public event EventHandler? EventTriggered;
}
}
@@ -0,0 +1,15 @@
using System;
namespace Artemis.Core
{
/// <summary>
/// Represents the base class for data model events that contain event data
/// </summary>
public class DataModelEventArgs
{
/// <summary>
/// Gets the time at which the event with these arguments was triggered
/// </summary>
public DateTime TriggerTime { get; internal set; }
}
}
@@ -111,18 +111,6 @@ namespace Artemis.Core
/// </summary>
public IReadOnlyCollection<DataModelPathSegment> Segments => _segments.ToList().AsReadOnly();
/// <summary>
/// Gets a boolean indicating whether this data model path points to a list
/// </summary>
public bool PointsToList
{
get
{
Type? type = GetPropertyType();
return type?.IsGenericEnumerable() ?? false;
}
}
internal DataModelPathEntity Entity { get; }
internal Func<object, object>? Accessor { get; private set; }
@@ -0,0 +1,27 @@
using System;
namespace Artemis.Core
{
internal interface IDataModelEvent
{
/// <summary>
/// Gets the last time the event was triggered
/// </summary>
DateTime LastTrigger { get; }
/// <summary>
/// Gets the amount of times the event was triggered
/// </summary>
int TriggerCount { get; }
/// <summary>
/// Gets the type of arguments this event contains
/// </summary>
Type? ArgumentsType { get; }
/// <summary>
/// Fires when the event is triggered
/// </summary>
event EventHandler EventTriggered;
}
}