Conditions - Removed old systems

Data bindings - Removed old systems
Data bindings - Implemented node system
This commit is contained in:
Robert committed 2021-08-18 23:47:14 +02:00
1 parent 32d4ec2812
commit 5675d1895b
157 files changed
+784 -8802

No files matched your search

@@ -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
}
}
@@ -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
}
}
@@ -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
}
}
@@ -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();
}
}
@@ -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);
}
}