mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Condition operators - Marked built-in operators as internal
Display conditions - Added docs to the service and profile types Color gradient - Added docs Storage - Moved profile entities to separate namespaces Data bindings - Added entities to storage Data bindings - Started implementing in the core
This commit is contained in:
parent
8d88b14d78
commit
68b61cbcb2
@ -3,35 +3,35 @@ using System.Collections.Generic;
|
|||||||
using System.ComponentModel;
|
using System.ComponentModel;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using System.Windows.Documents;
|
|
||||||
using Artemis.Core.Annotations;
|
using Artemis.Core.Annotations;
|
||||||
using SkiaSharp;
|
using SkiaSharp;
|
||||||
using Stylet;
|
using Stylet;
|
||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Colors
|
namespace Artemis.Core.Models.Profile.Colors
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A gradient containing a list of <see cref="ColorGradientStop" />s
|
||||||
|
/// </summary>
|
||||||
public class ColorGradient : INotifyPropertyChanged
|
public class ColorGradient : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
private float _rotation;
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="ColorGradient" /> class
|
||||||
|
/// </summary>
|
||||||
public ColorGradient()
|
public ColorGradient()
|
||||||
{
|
{
|
||||||
Stops = new BindableCollection<ColorGradientStop>();
|
Stops = new BindableCollection<ColorGradientStop>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of all the <see cref="ColorGradientStop" />s in the gradient
|
||||||
|
/// </summary>
|
||||||
public BindableCollection<ColorGradientStop> Stops { get; }
|
public BindableCollection<ColorGradientStop> Stops { get; }
|
||||||
|
|
||||||
public float Rotation
|
/// <summary>
|
||||||
{
|
/// Gets all the colors in the color gradient
|
||||||
get => _rotation;
|
/// </summary>
|
||||||
set
|
/// <param name="timesToRepeat">The amount of times to repeat the colors</param>
|
||||||
{
|
/// <returns></returns>
|
||||||
if (value.Equals(_rotation)) return;
|
|
||||||
_rotation = value;
|
|
||||||
OnPropertyChanged();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public SKColor[] GetColorsArray(int timesToRepeat = 0)
|
public SKColor[] GetColorsArray(int timesToRepeat = 0)
|
||||||
{
|
{
|
||||||
if (timesToRepeat == 0)
|
if (timesToRepeat == 0)
|
||||||
@ -46,6 +46,14 @@ namespace Artemis.Core.Models.Profile.Colors
|
|||||||
return result.ToArray();
|
return result.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all the positions in the color gradient
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="timesToRepeat">
|
||||||
|
/// The amount of times to repeat the positions, positions will get squished together and
|
||||||
|
/// always stay between 0.0 and 1.0
|
||||||
|
/// </param>
|
||||||
|
/// <returns></returns>
|
||||||
public float[] GetPositionsArray(int timesToRepeat = 0)
|
public float[] GetPositionsArray(int timesToRepeat = 0)
|
||||||
{
|
{
|
||||||
if (timesToRepeat == 0)
|
if (timesToRepeat == 0)
|
||||||
@ -65,11 +73,18 @@ namespace Artemis.Core.Models.Profile.Colors
|
|||||||
return result.ToArray();
|
return result.ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Triggers a property changed event of the <see cref="Stops" /> collection
|
||||||
|
/// </summary>
|
||||||
public void OnColorValuesUpdated()
|
public void OnColorValuesUpdated()
|
||||||
{
|
{
|
||||||
OnPropertyChanged(nameof(Stops));
|
OnPropertyChanged(nameof(Stops));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a color at any position between 0.0 and 1.0 using interpolation
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="position">A position between 0.0 and 1.0</param>
|
||||||
public SKColor GetColor(float position)
|
public SKColor GetColor(float position)
|
||||||
{
|
{
|
||||||
if (!Stops.Any())
|
if (!Stops.Any())
|
||||||
@ -119,6 +134,7 @@ namespace Artemis.Core.Models.Profile.Colors
|
|||||||
|
|
||||||
#region PropertyChanged
|
#region PropertyChanged
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public event PropertyChangedEventHandler PropertyChanged;
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
||||||
[NotifyPropertyChangedInvocator]
|
[NotifyPropertyChangedInvocator]
|
||||||
|
|||||||
@ -5,17 +5,26 @@ using SkiaSharp;
|
|||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Colors
|
namespace Artemis.Core.Models.Profile.Colors
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A color with a position, usually contained in a <see cref="ColorGradient" />
|
||||||
|
/// </summary>
|
||||||
public class ColorGradientStop : INotifyPropertyChanged
|
public class ColorGradientStop : INotifyPropertyChanged
|
||||||
{
|
{
|
||||||
private SKColor _color;
|
private SKColor _color;
|
||||||
private float _position;
|
private float _position;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="ColorGradientStop" /> class
|
||||||
|
/// </summary>
|
||||||
public ColorGradientStop(SKColor color, float position)
|
public ColorGradientStop(SKColor color, float position)
|
||||||
{
|
{
|
||||||
Color = color;
|
Color = color;
|
||||||
Position = position;
|
Position = position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the color of the stop
|
||||||
|
/// </summary>
|
||||||
public SKColor Color
|
public SKColor Color
|
||||||
{
|
{
|
||||||
get => _color;
|
get => _color;
|
||||||
@ -27,6 +36,9 @@ namespace Artemis.Core.Models.Profile.Colors
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the position of the stop
|
||||||
|
/// </summary>
|
||||||
public float Position
|
public float Position
|
||||||
{
|
{
|
||||||
get => _position;
|
get => _position;
|
||||||
@ -40,6 +52,7 @@ namespace Artemis.Core.Models.Profile.Colors
|
|||||||
|
|
||||||
#region PropertyChanged
|
#region PropertyChanged
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public event PropertyChangedEventHandler PropertyChanged;
|
public event PropertyChangedEventHandler PropertyChanged;
|
||||||
|
|
||||||
[NotifyPropertyChangedInvocator]
|
[NotifyPropertyChangedInvocator]
|
||||||
|
|||||||
@ -1,22 +1,30 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq.Expressions;
|
|
||||||
using Artemis.Core.Services.Interfaces;
|
using Artemis.Core.Services.Interfaces;
|
||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Conditions.Abstract
|
namespace Artemis.Core.Models.Profile.Conditions.Abstract
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An abstract class for display condition parts
|
||||||
|
/// </summary>
|
||||||
public abstract class DisplayConditionPart
|
public abstract class DisplayConditionPart
|
||||||
{
|
{
|
||||||
private readonly List<DisplayConditionPart> _children;
|
private readonly List<DisplayConditionPart> _children = new List<DisplayConditionPart>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the parent of this part
|
||||||
|
/// </summary>
|
||||||
|
public DisplayConditionPart Parent { get; internal set; }
|
||||||
|
|
||||||
protected DisplayConditionPart()
|
/// <summary>
|
||||||
{
|
/// Gets the children of this part
|
||||||
_children = new List<DisplayConditionPart>();
|
/// </summary>
|
||||||
}
|
|
||||||
|
|
||||||
public DisplayConditionPart Parent { get; set; }
|
|
||||||
public IReadOnlyList<DisplayConditionPart> Children => _children.AsReadOnly();
|
public IReadOnlyList<DisplayConditionPart> Children => _children.AsReadOnly();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a child to the display condition part's <see cref="Children" /> collection
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="displayConditionPart"></param>
|
||||||
public void AddChild(DisplayConditionPart displayConditionPart)
|
public void AddChild(DisplayConditionPart displayConditionPart)
|
||||||
{
|
{
|
||||||
if (!_children.Contains(displayConditionPart))
|
if (!_children.Contains(displayConditionPart))
|
||||||
@ -26,6 +34,10 @@ namespace Artemis.Core.Models.Profile.Conditions.Abstract
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a child from the display condition part's <see cref="Children" /> collection
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="displayConditionPart">The child to remove</param>
|
||||||
public void RemoveChild(DisplayConditionPart displayConditionPart)
|
public void RemoveChild(DisplayConditionPart displayConditionPart)
|
||||||
{
|
{
|
||||||
if (_children.Contains(displayConditionPart))
|
if (_children.Contains(displayConditionPart))
|
||||||
@ -36,13 +48,13 @@ namespace Artemis.Core.Models.Profile.Conditions.Abstract
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Evaluates the condition part on the data model
|
/// Evaluates the condition part on the data model
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public abstract bool Evaluate();
|
public abstract bool Evaluate();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Evaluates the condition part on the given target (currently only for lists)
|
/// Evaluates the condition part on the given target (currently only for lists)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="target"></param>
|
/// <param name="target"></param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
|
|||||||
@ -1,12 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
using Artemis.Core.Plugins.DataModelExpansions;
|
|
||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Conditions
|
|
||||||
{
|
|
||||||
public class DisplayCondition
|
|
||||||
{
|
|
||||||
public Expression<Func<DataModel, bool>> ExpressionTree { get; set; }
|
|
||||||
public DisplayConditionGroup RootGroup { get; set; }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -4,17 +4,31 @@ using Artemis.Core.Models.Profile.Conditions.Abstract;
|
|||||||
using Artemis.Core.Services.Interfaces;
|
using Artemis.Core.Services.Interfaces;
|
||||||
using Artemis.Storage.Entities.Profile;
|
using Artemis.Storage.Entities.Profile;
|
||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
|
using Artemis.Storage.Entities.Profile.Conditions;
|
||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Conditions
|
namespace Artemis.Core.Models.Profile.Conditions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A group containing zero to many <see cref="DisplayConditionPart" />s which it evaluates using a boolean specific
|
||||||
|
/// operator
|
||||||
|
/// </summary>
|
||||||
public class DisplayConditionGroup : DisplayConditionPart
|
public class DisplayConditionGroup : DisplayConditionPart
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="DisplayConditionGroup" /> class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent"></param>
|
||||||
public DisplayConditionGroup(DisplayConditionPart parent)
|
public DisplayConditionGroup(DisplayConditionPart parent)
|
||||||
{
|
{
|
||||||
Parent = parent;
|
Parent = parent;
|
||||||
Entity = new DisplayConditionGroupEntity();
|
Entity = new DisplayConditionGroupEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="DisplayConditionGroup" /> class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent"></param>
|
||||||
|
/// <param name="entity"></param>
|
||||||
public DisplayConditionGroup(DisplayConditionPart parent, DisplayConditionGroupEntity entity)
|
public DisplayConditionGroup(DisplayConditionPart parent, DisplayConditionGroupEntity entity)
|
||||||
{
|
{
|
||||||
Parent = parent;
|
Parent = parent;
|
||||||
@ -34,9 +48,14 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the boolean operator of this group
|
||||||
|
/// </summary>
|
||||||
public BooleanOperator BooleanOperator { get; set; }
|
public BooleanOperator BooleanOperator { get; set; }
|
||||||
public DisplayConditionGroupEntity Entity { get; set; }
|
|
||||||
|
|
||||||
|
internal DisplayConditionGroupEntity Entity { get; set; }
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override bool Evaluate()
|
public override bool Evaluate()
|
||||||
{
|
{
|
||||||
// Empty groups are always true
|
// Empty groups are always true
|
||||||
@ -61,6 +80,7 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
public override bool EvaluateObject(object target)
|
public override bool EvaluateObject(object target)
|
||||||
{
|
{
|
||||||
// Empty groups are always true
|
// Empty groups are always true
|
||||||
|
|||||||
@ -8,6 +8,7 @@ using Artemis.Core.Plugins.DataModelExpansions;
|
|||||||
using Artemis.Core.Services.Interfaces;
|
using Artemis.Core.Services.Interfaces;
|
||||||
using Artemis.Storage.Entities.Profile;
|
using Artemis.Storage.Entities.Profile;
|
||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
|
using Artemis.Storage.Entities.Profile.Conditions;
|
||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Conditions
|
namespace Artemis.Core.Models.Profile.Conditions
|
||||||
{
|
{
|
||||||
|
|||||||
@ -6,15 +6,15 @@ using Artemis.Core.Extensions;
|
|||||||
using Artemis.Core.Models.Profile.Conditions.Abstract;
|
using Artemis.Core.Models.Profile.Conditions.Abstract;
|
||||||
using Artemis.Core.Plugins.DataModelExpansions;
|
using Artemis.Core.Plugins.DataModelExpansions;
|
||||||
using Artemis.Core.Services.Interfaces;
|
using Artemis.Core.Services.Interfaces;
|
||||||
using Artemis.Storage.Entities.Profile;
|
|
||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
|
using Artemis.Storage.Entities.Profile.Conditions;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Conditions
|
namespace Artemis.Core.Models.Profile.Conditions
|
||||||
{
|
{
|
||||||
public class DisplayConditionListPredicate : DisplayConditionPart
|
public class DisplayConditionListPredicate : DisplayConditionPart
|
||||||
{
|
{
|
||||||
public DisplayConditionListPredicate(DisplayConditionPart parent, PredicateType predicateType)
|
public DisplayConditionListPredicate(DisplayConditionPart parent, ProfileRightSideType predicateType)
|
||||||
{
|
{
|
||||||
Parent = parent;
|
Parent = parent;
|
||||||
PredicateType = predicateType;
|
PredicateType = predicateType;
|
||||||
@ -27,14 +27,14 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
{
|
{
|
||||||
Parent = parent;
|
Parent = parent;
|
||||||
Entity = entity;
|
Entity = entity;
|
||||||
PredicateType = (PredicateType) entity.PredicateType;
|
PredicateType = (ProfileRightSideType) entity.PredicateType;
|
||||||
|
|
||||||
ApplyParentList();
|
ApplyParentList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DisplayConditionListPredicateEntity Entity { get; set; }
|
public DisplayConditionListPredicateEntity Entity { get; set; }
|
||||||
|
|
||||||
public PredicateType PredicateType { get; set; }
|
public ProfileRightSideType PredicateType { get; set; }
|
||||||
public DisplayConditionOperator Operator { get; private set; }
|
public DisplayConditionOperator Operator { get; private set; }
|
||||||
|
|
||||||
public Type ListType { get; private set; }
|
public Type ListType { get; private set; }
|
||||||
@ -57,6 +57,7 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
UpdateList(parentList.ListDataModel, parentList.ListPropertyPath);
|
UpdateList(parentList.ListDataModel, parentList.ListPropertyPath);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
current = current.Parent;
|
current = current.Parent;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -77,9 +78,7 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
ListType = listType;
|
ListType = listType;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
|
||||||
ListType = null;
|
ListType = null;
|
||||||
}
|
|
||||||
|
|
||||||
ListDataModel = dataModel;
|
ListDataModel = dataModel;
|
||||||
ListPropertyPath = path;
|
ListPropertyPath = path;
|
||||||
@ -109,7 +108,7 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
if (!ListContainsInnerPath(path))
|
if (!ListContainsInnerPath(path))
|
||||||
throw new ArtemisCoreException($"List type {ListType.Name} does not contain path {path}");
|
throw new ArtemisCoreException($"List type {ListType.Name} does not contain path {path}");
|
||||||
|
|
||||||
PredicateType = PredicateType.Dynamic;
|
PredicateType = ProfileRightSideType.Dynamic;
|
||||||
RightPropertyPath = path;
|
RightPropertyPath = path;
|
||||||
|
|
||||||
CreateExpression();
|
CreateExpression();
|
||||||
@ -117,7 +116,7 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
|
|
||||||
public void UpdateRightSideStatic(object staticValue)
|
public void UpdateRightSideStatic(object staticValue)
|
||||||
{
|
{
|
||||||
PredicateType = PredicateType.Static;
|
PredicateType = ProfileRightSideType.Static;
|
||||||
RightPropertyPath = null;
|
RightPropertyPath = null;
|
||||||
|
|
||||||
SetStaticValue(staticValue);
|
SetStaticValue(staticValue);
|
||||||
@ -145,18 +144,52 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
CreateExpression();
|
CreateExpression();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateExpression()
|
public override bool Evaluate()
|
||||||
{
|
{
|
||||||
CompiledListPredicate = null;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (Operator == null)
|
public override bool EvaluateObject(object target)
|
||||||
return;
|
{
|
||||||
|
return CompiledListPredicate != null && CompiledListPredicate(target);
|
||||||
|
}
|
||||||
|
|
||||||
// If the operator does not support a right side, create a static expression because the right side will simply be null
|
public bool ListContainsInnerPath(string path)
|
||||||
if (PredicateType == PredicateType.Dynamic && Operator.SupportsRightSide)
|
{
|
||||||
CreateDynamicExpression();
|
if (ListType == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
CreateStaticExpression();
|
var parts = path.Split('.');
|
||||||
|
var current = ListType;
|
||||||
|
foreach (var part in parts)
|
||||||
|
{
|
||||||
|
var property = current.GetProperty(part);
|
||||||
|
current = property?.PropertyType;
|
||||||
|
|
||||||
|
if (property == null)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Type GetTypeAtInnerPath(string path)
|
||||||
|
{
|
||||||
|
if (!ListContainsInnerPath(path))
|
||||||
|
return null;
|
||||||
|
|
||||||
|
var parts = path.Split('.');
|
||||||
|
var current = ListType;
|
||||||
|
|
||||||
|
Type result = null;
|
||||||
|
foreach (var part in parts)
|
||||||
|
{
|
||||||
|
var property = current.GetProperty(part);
|
||||||
|
current = property.PropertyType;
|
||||||
|
result = property.PropertyType;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void ApplyToEntity()
|
internal override void ApplyToEntity()
|
||||||
@ -173,16 +206,6 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
Entity.OperatorType = Operator?.GetType().Name;
|
Entity.OperatorType = Operator?.GetType().Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Evaluate()
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool EvaluateObject(object target)
|
|
||||||
{
|
|
||||||
return CompiledListPredicate != null && CompiledListPredicate(target);
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void Initialize(IDataModelService dataModelService)
|
internal override void Initialize(IDataModelService dataModelService)
|
||||||
{
|
{
|
||||||
// Left side
|
// Left side
|
||||||
@ -198,13 +221,13 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Right side dynamic
|
// Right side dynamic
|
||||||
if (PredicateType == PredicateType.Dynamic && Entity.RightPropertyPath != null)
|
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightPropertyPath != null)
|
||||||
{
|
{
|
||||||
if (ListContainsInnerPath(Entity.RightPropertyPath))
|
if (ListContainsInnerPath(Entity.RightPropertyPath))
|
||||||
UpdateLeftSide(Entity.LeftPropertyPath);
|
UpdateLeftSide(Entity.LeftPropertyPath);
|
||||||
}
|
}
|
||||||
// Right side static
|
// Right side static
|
||||||
else if (PredicateType == PredicateType.Static && Entity.RightStaticValue != null)
|
else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -245,6 +268,20 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
return Entity;
|
return Entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CreateExpression()
|
||||||
|
{
|
||||||
|
CompiledListPredicate = null;
|
||||||
|
|
||||||
|
if (Operator == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If the operator does not support a right side, create a static expression because the right side will simply be null
|
||||||
|
if (PredicateType == ProfileRightSideType.Dynamic && Operator.SupportsRightSide)
|
||||||
|
CreateDynamicExpression();
|
||||||
|
|
||||||
|
CreateStaticExpression();
|
||||||
|
}
|
||||||
|
|
||||||
private void ValidateOperator()
|
private void ValidateOperator()
|
||||||
{
|
{
|
||||||
if (LeftPropertyPath == null || Operator == null)
|
if (LeftPropertyPath == null || Operator == null)
|
||||||
@ -258,7 +295,7 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
private void ValidateRightSide()
|
private void ValidateRightSide()
|
||||||
{
|
{
|
||||||
var leftSideType = GetTypeAtInnerPath(LeftPropertyPath);
|
var leftSideType = GetTypeAtInnerPath(LeftPropertyPath);
|
||||||
if (PredicateType == PredicateType.Dynamic)
|
if (PredicateType == ProfileRightSideType.Dynamic)
|
||||||
{
|
{
|
||||||
if (RightPropertyPath == null)
|
if (RightPropertyPath == null)
|
||||||
return;
|
return;
|
||||||
@ -349,43 +386,5 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
Expression.Property
|
Expression.Property
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool ListContainsInnerPath(string path)
|
|
||||||
{
|
|
||||||
if (ListType == null)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
var parts = path.Split('.');
|
|
||||||
var current = ListType;
|
|
||||||
foreach (var part in parts)
|
|
||||||
{
|
|
||||||
var property = current.GetProperty(part);
|
|
||||||
current = property?.PropertyType;
|
|
||||||
|
|
||||||
if (property == null)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Type GetTypeAtInnerPath(string path)
|
|
||||||
{
|
|
||||||
if (!ListContainsInnerPath(path))
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var parts = path.Split('.');
|
|
||||||
var current = ListType;
|
|
||||||
|
|
||||||
Type result = null;
|
|
||||||
foreach (var part in parts)
|
|
||||||
{
|
|
||||||
var property = current.GetProperty(part);
|
|
||||||
current = property.PropertyType;
|
|
||||||
result = property.PropertyType;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -8,6 +8,9 @@ using Artemis.Core.Services.Interfaces;
|
|||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Conditions
|
namespace Artemis.Core.Models.Profile.Conditions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A display condition operator is used by the conditions system to perform a specific boolean check
|
||||||
|
/// </summary>
|
||||||
public abstract class DisplayConditionOperator
|
public abstract class DisplayConditionOperator
|
||||||
{
|
{
|
||||||
private IDataModelService _dataModelService;
|
private IDataModelService _dataModelService;
|
||||||
@ -39,6 +42,9 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public bool SupportsRightSide { get; protected set; } = true;
|
public bool SupportsRightSide { get; protected set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether the given type is supported by the operator
|
||||||
|
/// </summary>
|
||||||
public bool SupportsType(Type type)
|
public bool SupportsType(Type type)
|
||||||
{
|
{
|
||||||
if (type == null)
|
if (type == null)
|
||||||
@ -54,16 +60,6 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public abstract BinaryExpression CreateExpression(Expression leftSide, Expression rightSide);
|
public abstract BinaryExpression CreateExpression(Expression leftSide, Expression rightSide);
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns an expression that checks the given expression for null
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="expression"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
protected Expression CreateNullCheckExpression(Expression expression)
|
|
||||||
{
|
|
||||||
return Expression.NotEqual(expression, Expression.Constant(null));
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void Register(PluginInfo pluginInfo, IDataModelService dataModelService)
|
internal void Register(PluginInfo pluginInfo, IDataModelService dataModelService)
|
||||||
{
|
{
|
||||||
if (_registered)
|
if (_registered)
|
||||||
@ -90,7 +86,7 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
|
|
||||||
private void InstanceOnPluginDisabled(object sender, EventArgs e)
|
private void InstanceOnPluginDisabled(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
// Profile editor service will call Unsubscribe
|
// The service will call Unsubscribe
|
||||||
_dataModelService.RemoveConditionOperator(this);
|
_dataModelService.RemoveConditionOperator(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@ -6,45 +6,95 @@ using Artemis.Core.Extensions;
|
|||||||
using Artemis.Core.Models.Profile.Conditions.Abstract;
|
using Artemis.Core.Models.Profile.Conditions.Abstract;
|
||||||
using Artemis.Core.Plugins.DataModelExpansions;
|
using Artemis.Core.Plugins.DataModelExpansions;
|
||||||
using Artemis.Core.Services.Interfaces;
|
using Artemis.Core.Services.Interfaces;
|
||||||
using Artemis.Storage.Entities.Profile;
|
|
||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
|
using Artemis.Storage.Entities.Profile.Conditions;
|
||||||
using Newtonsoft.Json;
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Conditions
|
namespace Artemis.Core.Models.Profile.Conditions
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A predicate in a display condition using either two data model values or one data model value and a
|
||||||
|
/// static value
|
||||||
|
/// </summary>
|
||||||
public class DisplayConditionPredicate : DisplayConditionPart
|
public class DisplayConditionPredicate : DisplayConditionPart
|
||||||
{
|
{
|
||||||
public DisplayConditionPredicate(DisplayConditionPart parent, PredicateType predicateType)
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="DisplayConditionPredicate" /> class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent"></param>
|
||||||
|
/// <param name="predicateType"></param>
|
||||||
|
public DisplayConditionPredicate(DisplayConditionPart parent, ProfileRightSideType predicateType)
|
||||||
{
|
{
|
||||||
Parent = parent;
|
Parent = parent;
|
||||||
PredicateType = predicateType;
|
PredicateType = predicateType;
|
||||||
Entity = new DisplayConditionPredicateEntity();
|
Entity = new DisplayConditionPredicateEntity();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="DisplayConditionPredicate" /> class
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="parent"></param>
|
||||||
|
/// <param name="entity"></param>
|
||||||
public DisplayConditionPredicate(DisplayConditionPart parent, DisplayConditionPredicateEntity entity)
|
public DisplayConditionPredicate(DisplayConditionPart parent, DisplayConditionPredicateEntity entity)
|
||||||
{
|
{
|
||||||
Parent = parent;
|
Parent = parent;
|
||||||
Entity = entity;
|
Entity = entity;
|
||||||
PredicateType = (PredicateType) entity.PredicateType;
|
PredicateType = (ProfileRightSideType) entity.PredicateType;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DisplayConditionPredicateEntity Entity { get; set; }
|
/// <summary>
|
||||||
|
/// Gets or sets the predicate type
|
||||||
|
/// </summary>
|
||||||
|
public ProfileRightSideType PredicateType { get; set; }
|
||||||
|
|
||||||
public PredicateType PredicateType { get; set; }
|
/// <summary>
|
||||||
|
/// Gets the operator
|
||||||
|
/// </summary>
|
||||||
public DisplayConditionOperator Operator { get; private set; }
|
public DisplayConditionOperator Operator { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the currently used instance of the left data model
|
||||||
|
/// </summary>
|
||||||
public DataModel LeftDataModel { get; private set; }
|
public DataModel LeftDataModel { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the path of the left property in the <see cref="LeftDataModel" />
|
||||||
|
/// </summary>
|
||||||
public string LeftPropertyPath { get; private set; }
|
public string LeftPropertyPath { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the currently used instance of the right data model
|
||||||
|
/// </summary>
|
||||||
public DataModel RightDataModel { get; private set; }
|
public DataModel RightDataModel { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the path of the right property in the <see cref="RightDataModel" />
|
||||||
|
/// </summary>
|
||||||
public string RightPropertyPath { get; private set; }
|
public string RightPropertyPath { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the right static value, only used it <see cref="PredicateType" /> is
|
||||||
|
/// <see cref="ProfileRightSideType.Static" />
|
||||||
|
/// </summary>
|
||||||
public object RightStaticValue { get; private set; }
|
public object RightStaticValue { get; private set; }
|
||||||
public DataModel ListDataModel { get; private set; }
|
|
||||||
public string ListPropertyPath { get; private set; }
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the compiled function that evaluates this predicate if it of a dynamic <see cref="PredicateType" />
|
||||||
|
/// </summary>
|
||||||
public Func<DataModel, DataModel, bool> CompiledDynamicPredicate { get; private set; }
|
public Func<DataModel, DataModel, bool> CompiledDynamicPredicate { get; private set; }
|
||||||
public Func<DataModel, bool> CompiledStaticPredicate { get; private set; }
|
|
||||||
public Func<object, bool> CompiledListPredicate { get; private set; }
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the compiled function that evaluates this predicate if it is of a static <see cref="PredicateType" />
|
||||||
|
/// </summary>
|
||||||
|
public Func<DataModel, bool> CompiledStaticPredicate { get; private set; }
|
||||||
|
|
||||||
|
internal DisplayConditionPredicateEntity Entity { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the left side of the predicate
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataModel">The data model of the left side value</param>
|
||||||
|
/// <param name="path">The path pointing to the left side value inside the data model</param>
|
||||||
public void UpdateLeftSide(DataModel dataModel, string path)
|
public void UpdateLeftSide(DataModel dataModel, string path)
|
||||||
{
|
{
|
||||||
if (dataModel != null && path == null)
|
if (dataModel != null && path == null)
|
||||||
@ -67,6 +117,11 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
CreateExpression();
|
CreateExpression();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the right side of the predicate, makes the predicate dynamic and re-compiles the expression
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataModel">The data model of the right side value</param>
|
||||||
|
/// <param name="path">The path pointing to the right side value inside the data model</param>
|
||||||
public void UpdateRightSide(DataModel dataModel, string path)
|
public void UpdateRightSide(DataModel dataModel, string path)
|
||||||
{
|
{
|
||||||
if (dataModel != null && path == null)
|
if (dataModel != null && path == null)
|
||||||
@ -80,32 +135,61 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
throw new ArtemisCoreException($"Data model of type {dataModel.GetType().Name} does not contain a property at path '{path}'");
|
throw new ArtemisCoreException($"Data model of type {dataModel.GetType().Name} does not contain a property at path '{path}'");
|
||||||
}
|
}
|
||||||
|
|
||||||
PredicateType = PredicateType.Dynamic;
|
PredicateType = ProfileRightSideType.Dynamic;
|
||||||
RightDataModel = dataModel;
|
RightDataModel = dataModel;
|
||||||
RightPropertyPath = path;
|
RightPropertyPath = path;
|
||||||
|
|
||||||
CreateExpression();
|
CreateExpression();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <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 UpdateRightSide(object staticValue)
|
public void UpdateRightSide(object staticValue)
|
||||||
{
|
{
|
||||||
PredicateType = PredicateType.Static;
|
PredicateType = ProfileRightSideType.Static;
|
||||||
RightDataModel = null;
|
RightDataModel = null;
|
||||||
RightPropertyPath = null;
|
RightPropertyPath = null;
|
||||||
|
|
||||||
SetStaticValue(staticValue);
|
// If the left side is empty simply apply the value, any validation will wait
|
||||||
|
if (LeftDataModel == null)
|
||||||
|
{
|
||||||
|
RightStaticValue = staticValue;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var leftSideType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
|
||||||
|
|
||||||
|
// If not null ensure the types match and if not, convert it
|
||||||
|
if (staticValue != null && staticValue.GetType() == leftSideType)
|
||||||
|
RightStaticValue = staticValue;
|
||||||
|
else if (staticValue != null)
|
||||||
|
RightStaticValue = Convert.ChangeType(staticValue, leftSideType);
|
||||||
|
// If null create a default instance for value types or simply make it null for reference types
|
||||||
|
else if (leftSideType.IsValueType)
|
||||||
|
RightStaticValue = Activator.CreateInstance(leftSideType);
|
||||||
|
else
|
||||||
|
RightStaticValue = null;
|
||||||
|
|
||||||
CreateExpression();
|
CreateExpression();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the operator of the predicate and re-compiles the expression
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="displayConditionOperator"></param>
|
||||||
public void UpdateOperator(DisplayConditionOperator displayConditionOperator)
|
public void UpdateOperator(DisplayConditionOperator displayConditionOperator)
|
||||||
{
|
{
|
||||||
|
// Calling CreateExpression will clear compiled expressions
|
||||||
if (displayConditionOperator == null)
|
if (displayConditionOperator == null)
|
||||||
{
|
{
|
||||||
Operator = null;
|
Operator = null;
|
||||||
|
CreateExpression();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// No need to clear compiled expressions, without a left data model they are already null
|
||||||
if (LeftDataModel == null)
|
if (LeftDataModel == null)
|
||||||
{
|
{
|
||||||
Operator = displayConditionOperator;
|
Operator = displayConditionOperator;
|
||||||
@ -113,26 +197,37 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
}
|
}
|
||||||
|
|
||||||
var leftType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
|
var leftType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
|
||||||
if (displayConditionOperator.SupportsType(leftType))
|
if (!displayConditionOperator.SupportsType(leftType))
|
||||||
Operator = displayConditionOperator;
|
throw new ArtemisCoreException($"Cannot apply operator {displayConditionOperator.GetType().Name} to this predicate because " +
|
||||||
|
$"it does not support left side type {leftType.Name}");
|
||||||
|
|
||||||
|
Operator = displayConditionOperator;
|
||||||
CreateExpression();
|
CreateExpression();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateExpression()
|
/// <inheritdoc />
|
||||||
|
public override bool Evaluate()
|
||||||
{
|
{
|
||||||
CompiledDynamicPredicate = null;
|
if (CompiledDynamicPredicate != null)
|
||||||
CompiledStaticPredicate = null;
|
return CompiledDynamicPredicate(LeftDataModel, RightDataModel);
|
||||||
CompiledListPredicate = null;
|
if (CompiledStaticPredicate != null)
|
||||||
|
return CompiledStaticPredicate(LeftDataModel);
|
||||||
|
|
||||||
if (Operator == null)
|
return false;
|
||||||
return;
|
}
|
||||||
|
|
||||||
// If the operator does not support a right side, create a static expression because the right side will simply be null
|
/// <inheritdoc />
|
||||||
if (PredicateType == PredicateType.Dynamic && Operator.SupportsRightSide)
|
public override bool EvaluateObject(object target)
|
||||||
CreateDynamicExpression();
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
CreateStaticExpression();
|
/// <inheritdoc />
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
if (PredicateType == ProfileRightSideType.Dynamic)
|
||||||
|
return $"[Dynamic] {LeftPropertyPath} {Operator.Description} {RightPropertyPath}";
|
||||||
|
return $"[Static] {LeftPropertyPath} {Operator.Description} {RightStaticValue}";
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override void ApplyToEntity()
|
internal override void ApplyToEntity()
|
||||||
@ -149,24 +244,6 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
Entity.OperatorType = Operator?.GetType().Name;
|
Entity.OperatorType = Operator?.GetType().Name;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override bool Evaluate()
|
|
||||||
{
|
|
||||||
if (CompiledDynamicPredicate != null)
|
|
||||||
return CompiledDynamicPredicate(LeftDataModel, RightDataModel);
|
|
||||||
if (CompiledStaticPredicate != null)
|
|
||||||
return CompiledStaticPredicate(LeftDataModel);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override bool EvaluateObject(object target)
|
|
||||||
{
|
|
||||||
if (CompiledListPredicate != null)
|
|
||||||
return CompiledListPredicate(target);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void Initialize(IDataModelService dataModelService)
|
internal override void Initialize(IDataModelService dataModelService)
|
||||||
{
|
{
|
||||||
// Left side
|
// Left side
|
||||||
@ -186,14 +263,14 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Right side dynamic
|
// Right side dynamic
|
||||||
if (PredicateType == PredicateType.Dynamic && Entity.RightDataModelGuid != null)
|
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightDataModelGuid != null)
|
||||||
{
|
{
|
||||||
var dataModel = dataModelService.GetPluginDataModelByGuid(Entity.RightDataModelGuid.Value);
|
var dataModel = dataModelService.GetPluginDataModelByGuid(Entity.RightDataModelGuid.Value);
|
||||||
if (dataModel != null && dataModel.ContainsPath(Entity.RightPropertyPath))
|
if (dataModel != null && dataModel.ContainsPath(Entity.RightPropertyPath))
|
||||||
UpdateRightSide(dataModel, Entity.RightPropertyPath);
|
UpdateRightSide(dataModel, Entity.RightPropertyPath);
|
||||||
}
|
}
|
||||||
// Right side static
|
// Right side static
|
||||||
else if (PredicateType == PredicateType.Static && Entity.RightStaticValue != null)
|
else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -235,6 +312,21 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
return Entity;
|
return Entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void CreateExpression()
|
||||||
|
{
|
||||||
|
CompiledDynamicPredicate = null;
|
||||||
|
CompiledStaticPredicate = null;
|
||||||
|
|
||||||
|
if (Operator == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// If the operator does not support a right side, create a static expression because the right side will simply be null
|
||||||
|
if (PredicateType == ProfileRightSideType.Dynamic && Operator.SupportsRightSide)
|
||||||
|
CreateDynamicExpression();
|
||||||
|
else
|
||||||
|
CreateStaticExpression();
|
||||||
|
}
|
||||||
|
|
||||||
private void ValidateOperator()
|
private void ValidateOperator()
|
||||||
{
|
{
|
||||||
if (LeftDataModel == null || Operator == null)
|
if (LeftDataModel == null || Operator == null)
|
||||||
@ -248,7 +340,7 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
private void ValidateRightSide()
|
private void ValidateRightSide()
|
||||||
{
|
{
|
||||||
var leftSideType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
|
var leftSideType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
|
||||||
if (PredicateType == PredicateType.Dynamic)
|
if (PredicateType == ProfileRightSideType.Dynamic)
|
||||||
{
|
{
|
||||||
if (RightDataModel == null)
|
if (RightDataModel == null)
|
||||||
return;
|
return;
|
||||||
@ -266,70 +358,22 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetStaticValue(object staticValue)
|
|
||||||
{
|
|
||||||
// If the left side is empty simply apply the value, any validation will wait
|
|
||||||
if (LeftDataModel == null)
|
|
||||||
{
|
|
||||||
RightStaticValue = staticValue;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var leftSideType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
|
|
||||||
|
|
||||||
// If not null ensure the types match and if not, convert it
|
|
||||||
if (staticValue != null && staticValue.GetType() == leftSideType)
|
|
||||||
RightStaticValue = staticValue;
|
|
||||||
else if (staticValue != null)
|
|
||||||
RightStaticValue = Convert.ChangeType(staticValue, leftSideType);
|
|
||||||
// If null create a default instance for value types or simply make it null for reference types
|
|
||||||
else if (leftSideType.IsValueType)
|
|
||||||
RightStaticValue = Activator.CreateInstance(leftSideType);
|
|
||||||
else
|
|
||||||
RightStaticValue = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateDynamicExpression()
|
private void CreateDynamicExpression()
|
||||||
{
|
{
|
||||||
if (LeftDataModel == null || RightDataModel == null || Operator == null)
|
if (LeftDataModel == null || RightDataModel == null || Operator == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var isListExpression = LeftDataModel.GetListTypeInPath(LeftPropertyPath) != null;
|
var leftSideAccessor = CreateAccessor(LeftDataModel, LeftPropertyPath, "left", out var leftSideParameter);
|
||||||
|
var rightSideAccessor = CreateAccessor(RightDataModel, RightPropertyPath, "right", out var rightSideParameter);
|
||||||
|
|
||||||
Expression leftSideAccessor;
|
|
||||||
Expression rightSideAccessor;
|
|
||||||
ParameterExpression leftSideParameter;
|
|
||||||
ParameterExpression rightSideParameter = null;
|
|
||||||
if (isListExpression)
|
|
||||||
{
|
|
||||||
// List accessors share the same parameter because a list always contains one item per entry
|
|
||||||
leftSideParameter = Expression.Parameter(typeof(object), "listItem");
|
|
||||||
leftSideAccessor = CreateListAccessor(LeftDataModel, LeftPropertyPath, leftSideParameter);
|
|
||||||
rightSideAccessor = CreateListAccessor(RightDataModel, RightPropertyPath, leftSideParameter);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
leftSideAccessor = CreateAccessor(LeftDataModel, LeftPropertyPath, "left", out leftSideParameter);
|
|
||||||
rightSideAccessor = CreateAccessor(RightDataModel, RightPropertyPath, "right", out rightSideParameter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// A conversion may be required if the types differ
|
// A conversion may be required if the types differ
|
||||||
// This can cause issues if the DisplayConditionOperator wasn't accurate in it's supported types but that is not a concern here
|
// This can cause issues if the DisplayConditionOperator wasn't accurate in it's supported types but that is not a concern here
|
||||||
if (rightSideAccessor.Type != leftSideAccessor.Type)
|
if (rightSideAccessor.Type != leftSideAccessor.Type)
|
||||||
rightSideAccessor = Expression.Convert(rightSideAccessor, leftSideAccessor.Type);
|
rightSideAccessor = Expression.Convert(rightSideAccessor, leftSideAccessor.Type);
|
||||||
|
|
||||||
var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideAccessor);
|
var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideAccessor);
|
||||||
|
var lambda = Expression.Lambda<Func<DataModel, DataModel, bool>>(conditionExpression, leftSideParameter, rightSideParameter);
|
||||||
if (isListExpression)
|
CompiledDynamicPredicate = lambda.Compile();
|
||||||
{
|
|
||||||
var lambda = Expression.Lambda<Func<object, bool>>(conditionExpression, leftSideParameter);
|
|
||||||
CompiledListPredicate = lambda.Compile();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var lambda = Expression.Lambda<Func<DataModel, DataModel, bool>>(conditionExpression, leftSideParameter, rightSideParameter);
|
|
||||||
CompiledDynamicPredicate = lambda.Compile();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateStaticExpression()
|
private void CreateStaticExpression()
|
||||||
@ -337,18 +381,7 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
if (LeftDataModel == null || Operator == null)
|
if (LeftDataModel == null || Operator == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var isListExpression = LeftDataModel.GetListTypeInPath(LeftPropertyPath) != null;
|
var leftSideAccessor = CreateAccessor(LeftDataModel, LeftPropertyPath, "left", out var leftSideParameter);
|
||||||
|
|
||||||
Expression leftSideAccessor;
|
|
||||||
ParameterExpression leftSideParameter;
|
|
||||||
if (isListExpression)
|
|
||||||
{
|
|
||||||
// List accessors share the same parameter because a list always contains one item per entry
|
|
||||||
leftSideParameter = Expression.Parameter(typeof(object), "listItem");
|
|
||||||
leftSideAccessor = CreateListAccessor(LeftDataModel, LeftPropertyPath, leftSideParameter);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
leftSideAccessor = CreateAccessor(LeftDataModel, LeftPropertyPath, "left", out leftSideParameter);
|
|
||||||
|
|
||||||
// If the left side is a value type but the input is empty, this isn't a valid expression
|
// If the left side is a value type but the input is empty, this isn't a valid expression
|
||||||
if (leftSideAccessor.Type.IsValueType && RightStaticValue == null)
|
if (leftSideAccessor.Type.IsValueType && RightStaticValue == null)
|
||||||
@ -360,19 +393,10 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
: Expression.Constant(null, leftSideAccessor.Type);
|
: Expression.Constant(null, leftSideAccessor.Type);
|
||||||
|
|
||||||
var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideConstant);
|
var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideConstant);
|
||||||
|
var lambda = Expression.Lambda<Func<DataModel, bool>>(conditionExpression, leftSideParameter);
|
||||||
if (isListExpression)
|
CompiledStaticPredicate = lambda.Compile();
|
||||||
{
|
|
||||||
var lambda = Expression.Lambda<Func<object, bool>>(conditionExpression, leftSideParameter);
|
|
||||||
CompiledListPredicate = lambda.Compile();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var lambda = Expression.Lambda<Func<DataModel, bool>>(conditionExpression, leftSideParameter);
|
|
||||||
CompiledStaticPredicate = lambda.Compile();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private Expression CreateAccessor(DataModel dataModel, string path, string parameterName, out ParameterExpression parameter)
|
private Expression CreateAccessor(DataModel dataModel, string path, string parameterName, out ParameterExpression parameter)
|
||||||
{
|
{
|
||||||
var listType = dataModel.GetListTypeInPath(path);
|
var listType = dataModel.GetListTypeInPath(path);
|
||||||
@ -398,18 +422,5 @@ namespace Artemis.Core.Models.Profile.Conditions
|
|||||||
Expression.Property
|
Expression.Property
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
if (PredicateType == PredicateType.Dynamic)
|
|
||||||
return $"[Dynamic] {LeftPropertyPath} {Operator.Description} {RightPropertyPath}";
|
|
||||||
return $"[Static] {LeftPropertyPath} {Operator.Description} {RightStaticValue}";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum PredicateType
|
|
||||||
{
|
|
||||||
Static,
|
|
||||||
Dynamic
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -4,7 +4,7 @@ using System.Linq.Expressions;
|
|||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
||||||
{
|
{
|
||||||
public class EqualsConditionOperator : DisplayConditionOperator
|
internal class EqualsConditionOperator : DisplayConditionOperator
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(object)};
|
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(object)};
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ using System.Linq.Expressions;
|
|||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
||||||
{
|
{
|
||||||
public class GreaterThanConditionOperator : DisplayConditionOperator
|
internal class GreaterThanConditionOperator : DisplayConditionOperator
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ using System.Linq.Expressions;
|
|||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
||||||
{
|
{
|
||||||
public class GreaterThanOrEqualConditionOperator : DisplayConditionOperator
|
internal class GreaterThanOrEqualConditionOperator : DisplayConditionOperator
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ using System.Linq.Expressions;
|
|||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
||||||
{
|
{
|
||||||
public class LessThanConditionOperator : DisplayConditionOperator
|
internal class LessThanConditionOperator : DisplayConditionOperator
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ using System.Linq.Expressions;
|
|||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
||||||
{
|
{
|
||||||
public class LessThanOrEqualConditionOperator : DisplayConditionOperator
|
internal class LessThanOrEqualConditionOperator : DisplayConditionOperator
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ using System.Linq.Expressions;
|
|||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
||||||
{
|
{
|
||||||
public class NotEqualConditionOperator : DisplayConditionOperator
|
internal class NotEqualConditionOperator : DisplayConditionOperator
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> { typeof(object) };
|
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> { typeof(object) };
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ using System.Reflection;
|
|||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
||||||
{
|
{
|
||||||
public class StringContainsConditionOperator : DisplayConditionOperator
|
internal class StringContainsConditionOperator : DisplayConditionOperator
|
||||||
{
|
{
|
||||||
private readonly MethodInfo _toLower;
|
private readonly MethodInfo _toLower;
|
||||||
private readonly MethodInfo _contains;
|
private readonly MethodInfo _contains;
|
||||||
|
|||||||
@ -5,7 +5,7 @@ using System.Reflection;
|
|||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
||||||
{
|
{
|
||||||
public class StringEndsWithConditionOperator : DisplayConditionOperator
|
internal class StringEndsWithConditionOperator : DisplayConditionOperator
|
||||||
{
|
{
|
||||||
private readonly MethodInfo _toLower;
|
private readonly MethodInfo _toLower;
|
||||||
private readonly MethodInfo _endsWith;
|
private readonly MethodInfo _endsWith;
|
||||||
|
|||||||
@ -5,7 +5,7 @@ using System.Reflection;
|
|||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
||||||
{
|
{
|
||||||
public class StringEqualsConditionOperator : DisplayConditionOperator
|
internal class StringEqualsConditionOperator : DisplayConditionOperator
|
||||||
{
|
{
|
||||||
private readonly MethodInfo _toLower;
|
private readonly MethodInfo _toLower;
|
||||||
|
|
||||||
|
|||||||
@ -5,7 +5,7 @@ using System.Reflection;
|
|||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
||||||
{
|
{
|
||||||
public class StringNotContainsConditionOperator : DisplayConditionOperator
|
internal class StringNotContainsConditionOperator : DisplayConditionOperator
|
||||||
{
|
{
|
||||||
private readonly MethodInfo _toLower;
|
private readonly MethodInfo _toLower;
|
||||||
private readonly MethodInfo _contains;
|
private readonly MethodInfo _contains;
|
||||||
|
|||||||
@ -5,7 +5,7 @@ using System.Reflection;
|
|||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
||||||
{
|
{
|
||||||
public class StringNotEqualConditionOperator : DisplayConditionOperator
|
internal class StringNotEqualConditionOperator : DisplayConditionOperator
|
||||||
{
|
{
|
||||||
private readonly MethodInfo _toLower;
|
private readonly MethodInfo _toLower;
|
||||||
|
|
||||||
|
|||||||
@ -4,7 +4,7 @@ using System.Linq.Expressions;
|
|||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
||||||
{
|
{
|
||||||
public class StringNullConditionOperator : DisplayConditionOperator
|
internal class StringNullConditionOperator : DisplayConditionOperator
|
||||||
{
|
{
|
||||||
public StringNullConditionOperator()
|
public StringNullConditionOperator()
|
||||||
{
|
{
|
||||||
|
|||||||
@ -5,7 +5,7 @@ using System.Reflection;
|
|||||||
|
|
||||||
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
namespace Artemis.Core.Models.Profile.Conditions.Operators
|
||||||
{
|
{
|
||||||
public class StringStartsWithConditionOperator : DisplayConditionOperator
|
internal class StringStartsWithConditionOperator : DisplayConditionOperator
|
||||||
{
|
{
|
||||||
private readonly MethodInfo _toLower;
|
private readonly MethodInfo _toLower;
|
||||||
private readonly MethodInfo _startsWith;
|
private readonly MethodInfo _startsWith;
|
||||||
|
|||||||
60
src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs
Normal file
60
src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Artemis.Core.Models.Profile.LayerProperties;
|
||||||
|
using Artemis.Core.Plugins.DataModelExpansions;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Models.Profile.DataBindings
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A data binding that binds a certain <see cref="BaseLayerProperty" /> to a value inside a <see cref="DataModel" />
|
||||||
|
/// </summary>
|
||||||
|
public class DataBinding
|
||||||
|
{
|
||||||
|
private readonly List<DataBindingModifier> _modifiers = new List<DataBindingModifier>();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The <see cref="BaseLayerProperty" /> that the data binding targets
|
||||||
|
/// </summary>
|
||||||
|
public BaseLayerProperty Target { get; set; } // BIG FAT TODO: Take into account X and Y of SkPosition etc., forgot about it again :>
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the currently used instance of the data model that contains the source of the data binding
|
||||||
|
/// </summary>
|
||||||
|
public DataModel SourceDataModel { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the path of the source property in the <see cref="SourceDataModel" />
|
||||||
|
/// </summary>
|
||||||
|
public string SourcePropertyPath { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a list of modifiers applied to this data binding
|
||||||
|
/// </summary>
|
||||||
|
public IReadOnlyList<DataBindingModifier> Modifiers => _modifiers.AsReadOnly();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a modifier to the data binding's <see cref="Modifiers" /> collection
|
||||||
|
/// </summary>
|
||||||
|
public void AddModifier(DataBindingModifier modifier)
|
||||||
|
{
|
||||||
|
if (!_modifiers.Contains(modifier))
|
||||||
|
{
|
||||||
|
modifier.DataBinding = this;
|
||||||
|
modifier.CreateExpression();
|
||||||
|
_modifiers.Add(modifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a modifier from the data binding's <see cref="Modifiers" /> collection
|
||||||
|
/// </summary>
|
||||||
|
public void RemoveModifier(DataBindingModifier modifier)
|
||||||
|
{
|
||||||
|
if (_modifiers.Contains(modifier))
|
||||||
|
{
|
||||||
|
modifier.DataBinding = null;
|
||||||
|
modifier.CreateExpression();
|
||||||
|
_modifiers.Remove(modifier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,287 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using Artemis.Core.Exceptions;
|
||||||
|
using Artemis.Core.Models.Profile.DataBindings.Modifiers;
|
||||||
|
using Artemis.Core.Plugins.DataModelExpansions;
|
||||||
|
using Artemis.Core.Services.Interfaces;
|
||||||
|
using Artemis.Storage.Entities.Profile.DataBindings;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Models.Profile.DataBindings
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Modifies a data model value in a way defined by the modifier type
|
||||||
|
/// </summary>
|
||||||
|
public class DataBindingModifier
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="DataBindingModifier" /> class
|
||||||
|
/// </summary>
|
||||||
|
public DataBindingModifier(DataBinding dataBinding, ProfileRightSideType parameterType)
|
||||||
|
{
|
||||||
|
DataBinding = dataBinding;
|
||||||
|
ParameterType = parameterType;
|
||||||
|
Entity = new DataBindingModifierEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="DataBindingModifier" /> class
|
||||||
|
/// </summary>
|
||||||
|
public DataBindingModifier(DataBinding dataBinding, DataBindingModifierEntity entity)
|
||||||
|
{
|
||||||
|
DataBinding = dataBinding;
|
||||||
|
ParameterType = (ProfileRightSideType) entity.ParameterType;
|
||||||
|
Order = entity.Order;
|
||||||
|
Entity = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the data binding this modifier is applied to
|
||||||
|
/// </summary>
|
||||||
|
public DataBinding DataBinding { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the type of modifier that is being applied
|
||||||
|
/// </summary>
|
||||||
|
public DataBindingModifierType ModifierType { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the type of the parameter, can either be dynamic (based on a data model value) or static
|
||||||
|
/// </summary>
|
||||||
|
public ProfileRightSideType ParameterType { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the position at which the modifier appears on the data binding
|
||||||
|
/// </summary>
|
||||||
|
public int Order { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the currently used instance of the parameter data model
|
||||||
|
/// </summary>
|
||||||
|
public DataModel ParameterDataModel { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the path of the parameter property in the <see cref="ParameterDataModel" />
|
||||||
|
/// </summary>
|
||||||
|
public string ParameterPropertyPath { get; private 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; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the compiled function that evaluates this predicate if it of a dynamic <see cref="ParameterType" />
|
||||||
|
/// </summary>
|
||||||
|
public Func<object, DataModel, object> CompiledDynamicPredicate { get; private set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the compiled function that evaluates this predicate if it is of a static <see cref="ParameterType" />
|
||||||
|
/// </summary>
|
||||||
|
public Func<object, object> CompiledStaticPredicate { 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)
|
||||||
|
{
|
||||||
|
var targetType = DataBinding.Target.GetPropertyType();
|
||||||
|
if (currentValue.GetType() != targetType)
|
||||||
|
{
|
||||||
|
throw new ArtemisCoreException("The current value of the data binding does not match the type of the target property." +
|
||||||
|
$" {targetType.Name} expected, received {currentValue.GetType().Name}.");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CompiledDynamicPredicate != null)
|
||||||
|
return CompiledDynamicPredicate(currentValue, ParameterDataModel);
|
||||||
|
if (CompiledStaticPredicate != null)
|
||||||
|
return CompiledStaticPredicate(currentValue);
|
||||||
|
|
||||||
|
return currentValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the modifier type of the modifier and re-compiles the expression
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="modifierType"></param>
|
||||||
|
public void UpdateModifierType(DataBindingModifierType modifierType)
|
||||||
|
{
|
||||||
|
// Calling CreateExpression will clear compiled expressions
|
||||||
|
if (modifierType == null)
|
||||||
|
{
|
||||||
|
ModifierType = null;
|
||||||
|
CreateExpression();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetType = DataBinding.Target.GetPropertyType();
|
||||||
|
if (!modifierType.SupportsType(targetType))
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
CreateExpression();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the parameter of the modifier, makes the modifier dynamic and re-compiles the expression
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataModel">The data model of the parameter</param>
|
||||||
|
/// <param name="path">The path pointing to the parameter inside the data model</param>
|
||||||
|
public void UpdateParameter(DataModel dataModel, string path)
|
||||||
|
{
|
||||||
|
if (dataModel != null && path == null)
|
||||||
|
throw new ArtemisCoreException("If a data model is provided, a path is also required");
|
||||||
|
if (dataModel == null && path != null)
|
||||||
|
throw new ArtemisCoreException("If path is provided, a data model is also required");
|
||||||
|
|
||||||
|
if (dataModel != null)
|
||||||
|
{
|
||||||
|
if (!dataModel.ContainsPath(path))
|
||||||
|
throw new ArtemisCoreException($"Data model of type {dataModel.GetType().Name} does not contain a property at path '{path}'");
|
||||||
|
}
|
||||||
|
|
||||||
|
ParameterType = ProfileRightSideType.Dynamic;
|
||||||
|
ParameterDataModel = dataModel;
|
||||||
|
ParameterPropertyPath = path;
|
||||||
|
|
||||||
|
CreateExpression();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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 UpdateParameter(object staticValue)
|
||||||
|
{
|
||||||
|
ParameterType = ProfileRightSideType.Static;
|
||||||
|
ParameterDataModel = null;
|
||||||
|
ParameterPropertyPath = null;
|
||||||
|
|
||||||
|
var targetType = DataBinding.Target.GetPropertyType();
|
||||||
|
|
||||||
|
// If not null ensure the types match and if not, convert it
|
||||||
|
if (staticValue != null && staticValue.GetType() == targetType)
|
||||||
|
ParameterStaticValue = staticValue;
|
||||||
|
else if (staticValue != null)
|
||||||
|
ParameterStaticValue = Convert.ChangeType(staticValue, targetType);
|
||||||
|
// If null create a default instance for value types or simply make it null for reference types
|
||||||
|
else if (targetType.IsValueType)
|
||||||
|
ParameterStaticValue = Activator.CreateInstance(targetType);
|
||||||
|
else
|
||||||
|
ParameterStaticValue = null;
|
||||||
|
|
||||||
|
CreateExpression();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Initialize(IDataModelService dataModelService, IDataBindingService dataBindingService)
|
||||||
|
{
|
||||||
|
// Modifier type
|
||||||
|
if (Entity.ModifierTypePluginGuid != null)
|
||||||
|
{
|
||||||
|
var modifierType = dataBindingService.GetModifierType(Entity.ModifierTypePluginGuid.Value, Entity.ModifierType);
|
||||||
|
if (modifierType != null)
|
||||||
|
UpdateModifierType(modifierType);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Dynamic parameter
|
||||||
|
if (ParameterType == ProfileRightSideType.Dynamic && Entity.ParameterDataModelGuid != null)
|
||||||
|
{
|
||||||
|
var dataModel = dataModelService.GetPluginDataModelByGuid(Entity.ParameterDataModelGuid.Value);
|
||||||
|
if (dataModel != null && dataModel.ContainsPath(Entity.ParameterPropertyPath))
|
||||||
|
UpdateParameter(dataModel, Entity.ParameterPropertyPath);
|
||||||
|
}
|
||||||
|
else if (ParameterType == ProfileRightSideType.Static && Entity.ParameterStaticValue != null)
|
||||||
|
{
|
||||||
|
// Use the target type so JSON.NET has a better idea what to do
|
||||||
|
var targetType = DataBinding.Target.GetPropertyType();
|
||||||
|
object staticValue;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
staticValue = JsonConvert.DeserializeObject(Entity.ParameterStaticValue, targetType);
|
||||||
|
}
|
||||||
|
// If deserialization fails, use the type's default
|
||||||
|
catch (JsonSerializationException e)
|
||||||
|
{
|
||||||
|
dataBindingService.LogModifierDeserializationFailure(this, e);
|
||||||
|
staticValue = Activator.CreateInstance(targetType);
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateParameter(staticValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static parameter
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal void CreateExpression()
|
||||||
|
{
|
||||||
|
CompiledDynamicPredicate = null;
|
||||||
|
CompiledStaticPredicate = null;
|
||||||
|
|
||||||
|
if (ModifierType == null || DataBinding == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (ParameterType == ProfileRightSideType.Dynamic && ModifierType.SupportsParameter)
|
||||||
|
CreateDynamicExpression();
|
||||||
|
else
|
||||||
|
CreateStaticExpression();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateDynamicExpression()
|
||||||
|
{
|
||||||
|
if (ParameterDataModel == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var currentValueParameter = Expression.Parameter(DataBinding.Target.GetPropertyType());
|
||||||
|
|
||||||
|
// If the right side value is null, the constant type cannot be inferred and must be provided based on the data binding target
|
||||||
|
var rightSideAccessor = CreateAccessor(ParameterDataModel, ParameterPropertyPath, "right", out var rightSideParameter);
|
||||||
|
|
||||||
|
// A conversion may be required if the types differ
|
||||||
|
// This can cause issues if the DisplayConditionOperator wasn't accurate in it's supported types but that is not a concern here
|
||||||
|
if (rightSideAccessor.Type != DataBinding.Target.GetPropertyType())
|
||||||
|
rightSideAccessor = Expression.Convert(rightSideAccessor, DataBinding.Target.GetPropertyType());
|
||||||
|
|
||||||
|
var modifierExpression = ModifierType.CreateExpression(currentValueParameter, rightSideAccessor);
|
||||||
|
var lambda = Expression.Lambda<Func<object, DataModel, object>>(modifierExpression, currentValueParameter, rightSideParameter);
|
||||||
|
CompiledDynamicPredicate = lambda.Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void CreateStaticExpression()
|
||||||
|
{
|
||||||
|
var currentValueParameter = Expression.Parameter(DataBinding.Target.GetPropertyType());
|
||||||
|
|
||||||
|
// If the right side value is null, the constant type cannot be inferred and must be provided based on the data binding target
|
||||||
|
var rightSideConstant = ParameterStaticValue != null
|
||||||
|
? Expression.Constant(ParameterStaticValue)
|
||||||
|
: Expression.Constant(null, DataBinding.Target.GetPropertyType());
|
||||||
|
|
||||||
|
var modifierExpression = ModifierType.CreateExpression(currentValueParameter, rightSideConstant);
|
||||||
|
var lambda = Expression.Lambda<Func<object, object>>(modifierExpression, currentValueParameter);
|
||||||
|
CompiledStaticPredicate = lambda.Compile();
|
||||||
|
}
|
||||||
|
|
||||||
|
private Expression CreateAccessor(DataModel dataModel, string path, string parameterName, out ParameterExpression parameter)
|
||||||
|
{
|
||||||
|
var listType = dataModel.GetListTypeInPath(path);
|
||||||
|
if (listType != null)
|
||||||
|
throw new ArtemisCoreException($"Cannot create a regular accessor at path {path} because the path contains a list");
|
||||||
|
|
||||||
|
parameter = Expression.Parameter(typeof(object), parameterName + "DataModel");
|
||||||
|
return path.Split('.').Aggregate<string, Expression>(
|
||||||
|
Expression.Convert(parameter, dataModel.GetType()), // Cast to the appropriate type
|
||||||
|
Expression.Property
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,99 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using Artemis.Core.Extensions;
|
||||||
|
using Artemis.Core.Plugins;
|
||||||
|
using Artemis.Core.Services;
|
||||||
|
using Artemis.Core.Services.Interfaces;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Models.Profile.DataBindings.Modifiers
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A modifier that changes the source value of a data binding in some way
|
||||||
|
/// </summary>
|
||||||
|
public abstract class DataBindingModifierType
|
||||||
|
{
|
||||||
|
private bool _registered;
|
||||||
|
private IDataBindingService _dataBindingService;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the plugin info this data binding modifier belongs to
|
||||||
|
/// <para>Note: Not set until after registering</para>
|
||||||
|
/// </summary>
|
||||||
|
public PluginInfo PluginInfo { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the data binding modifier this modifier type is applied to
|
||||||
|
/// </summary>
|
||||||
|
public DataBindingModifier Modifier { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the types this modifier supports
|
||||||
|
/// </summary>
|
||||||
|
public abstract IReadOnlyCollection<Type> CompatibleTypes { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the description of this modifier
|
||||||
|
/// </summary>
|
||||||
|
public abstract string Description { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the icon of this modifier
|
||||||
|
/// </summary>
|
||||||
|
public abstract string Icon { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets whether this modifier supports a parameter, defaults to true
|
||||||
|
/// </summary>
|
||||||
|
public bool SupportsParameter { get; protected set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether the given type is supported by the modifier
|
||||||
|
/// </summary>
|
||||||
|
public bool SupportsType(Type type)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
return true;
|
||||||
|
return CompatibleTypes.Any(t => t.IsCastableFrom(type));
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Creates a binary expression comparing two types
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="currentValue">The current value of the data binding</param>
|
||||||
|
/// <param name="modifierArgument">An argument passed to the modifier, either static of dynamic</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public abstract Expression<object> CreateExpression(ParameterExpression currentValue, Expression modifierArgument);
|
||||||
|
|
||||||
|
internal void Register(PluginInfo pluginInfo, IDataBindingService dataBindingService)
|
||||||
|
{
|
||||||
|
if (_registered)
|
||||||
|
return;
|
||||||
|
|
||||||
|
PluginInfo = pluginInfo;
|
||||||
|
_dataBindingService = dataBindingService;
|
||||||
|
|
||||||
|
if (PluginInfo != Constants.CorePluginInfo)
|
||||||
|
PluginInfo.Instance.PluginDisabled += InstanceOnPluginDisabled;
|
||||||
|
|
||||||
|
_registered = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
internal void Unsubscribe()
|
||||||
|
{
|
||||||
|
if (!_registered)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (PluginInfo != Constants.CorePluginInfo)
|
||||||
|
PluginInfo.Instance.PluginDisabled -= InstanceOnPluginDisabled;
|
||||||
|
_registered = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InstanceOnPluginDisabled(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
// The service will call Unsubscribe
|
||||||
|
_dataBindingService.RemoveModifierType(this);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
18
src/Artemis.Core/Models/Profile/ProfileRightSideType.cs
Normal file
18
src/Artemis.Core/Models/Profile/ProfileRightSideType.cs
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
namespace Artemis.Core.Models.Profile
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// An enum defining the right side type of a profile entity
|
||||||
|
/// </summary>
|
||||||
|
public enum ProfileRightSideType
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A static right side value
|
||||||
|
/// </summary>
|
||||||
|
Static,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A dynamic right side value based on a path in a data model
|
||||||
|
/// </summary>
|
||||||
|
Dynamic
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,11 +1,107 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Text;
|
using System.Linq;
|
||||||
|
using Artemis.Core.Extensions;
|
||||||
|
using Artemis.Core.Models.Profile.DataBindings;
|
||||||
|
using Artemis.Core.Models.Profile.DataBindings.Modifiers;
|
||||||
|
using Artemis.Core.Plugins;
|
||||||
using Artemis.Core.Services.Interfaces;
|
using Artemis.Core.Services.Interfaces;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
using Serilog;
|
||||||
|
|
||||||
namespace Artemis.Core.Services
|
namespace Artemis.Core.Services
|
||||||
{
|
{
|
||||||
public class DataBindingService : IDataBindingService
|
internal class DataBindingService : IDataBindingService
|
||||||
{
|
{
|
||||||
|
private readonly ILogger _logger;
|
||||||
|
private readonly List<DataBindingModifierType> _registeredDataBindingModifierTypes;
|
||||||
|
|
||||||
|
public DataBindingService(ILogger logger)
|
||||||
|
{
|
||||||
|
_logger = logger;
|
||||||
|
_registeredDataBindingModifierTypes = new List<DataBindingModifierType>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IReadOnlyCollection<DataBindingModifierType> RegisteredDataBindingModifierTypes
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
lock (_registeredDataBindingModifierTypes)
|
||||||
|
{
|
||||||
|
return _registeredDataBindingModifierTypes.AsReadOnly();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RegisterModifierType(PluginInfo pluginInfo, DataBindingModifierType dataBindingModifierType)
|
||||||
|
{
|
||||||
|
if (pluginInfo == null)
|
||||||
|
throw new ArgumentNullException(nameof(pluginInfo));
|
||||||
|
if (dataBindingModifierType == null)
|
||||||
|
throw new ArgumentNullException(nameof(dataBindingModifierType));
|
||||||
|
|
||||||
|
lock (_registeredDataBindingModifierTypes)
|
||||||
|
{
|
||||||
|
if (_registeredDataBindingModifierTypes.Contains(dataBindingModifierType))
|
||||||
|
return;
|
||||||
|
|
||||||
|
dataBindingModifierType.Register(pluginInfo, this);
|
||||||
|
_registeredDataBindingModifierTypes.Add(dataBindingModifierType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveModifierType(DataBindingModifierType dataBindingModifierType)
|
||||||
|
{
|
||||||
|
if (dataBindingModifierType == null)
|
||||||
|
throw new ArgumentNullException(nameof(dataBindingModifierType));
|
||||||
|
|
||||||
|
lock (_registeredDataBindingModifierTypes)
|
||||||
|
{
|
||||||
|
if (!_registeredDataBindingModifierTypes.Contains(dataBindingModifierType))
|
||||||
|
return;
|
||||||
|
|
||||||
|
dataBindingModifierType.Unsubscribe();
|
||||||
|
_registeredDataBindingModifierTypes.Remove(dataBindingModifierType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<DataBindingModifierType> GetCompatibleModifierTypes(Type type)
|
||||||
|
{
|
||||||
|
lock (_registeredDataBindingModifierTypes)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
return new List<DataBindingModifierType>(_registeredDataBindingModifierTypes);
|
||||||
|
|
||||||
|
var candidates = _registeredDataBindingModifierTypes.Where(c => c.CompatibleTypes.Any(t => t.IsCastableFrom(type))).ToList();
|
||||||
|
|
||||||
|
// If there are multiple modifier types with the same description, use the closest match
|
||||||
|
foreach (var dataBindingModifierTypes in candidates.GroupBy(c => c.Description).Where(g => g.Count() > 1).ToList())
|
||||||
|
{
|
||||||
|
var bestCandidate = dataBindingModifierTypes.OrderByDescending(c => c.CompatibleTypes.Contains(type)).FirstOrDefault();
|
||||||
|
foreach (var dataBindingModifierType in dataBindingModifierTypes)
|
||||||
|
{
|
||||||
|
if (dataBindingModifierType != bestCandidate)
|
||||||
|
candidates.Remove(dataBindingModifierType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return candidates;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public DataBindingModifierType GetModifierType(Guid modifierTypePluginGuid, string modifierType)
|
||||||
|
{
|
||||||
|
return RegisteredDataBindingModifierTypes.FirstOrDefault(o => o.PluginInfo.Guid == modifierTypePluginGuid && o.GetType().Name == modifierType);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void LogModifierDeserializationFailure(DataBindingModifier dataBindingModifier, JsonSerializationException exception)
|
||||||
|
{
|
||||||
|
_logger.Warning(
|
||||||
|
exception,
|
||||||
|
"Failed to deserialize static parameter for operator {order}. {operatorType}",
|
||||||
|
dataBindingModifier.Entity.Order,
|
||||||
|
dataBindingModifier.Entity.ModifierType
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,6 +1,51 @@
|
|||||||
namespace Artemis.Core.Services.Interfaces
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using Artemis.Core.Annotations;
|
||||||
|
using Artemis.Core.Models.Profile.DataBindings;
|
||||||
|
using Artemis.Core.Models.Profile.DataBindings.Modifiers;
|
||||||
|
using Artemis.Core.Plugins;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Artemis.Core.Services.Interfaces
|
||||||
{
|
{
|
||||||
public interface IDataBindingService : IArtemisService
|
public interface IDataBindingService : IArtemisService
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a read-only collection of all registered modifier types
|
||||||
|
/// </summary>
|
||||||
|
IReadOnlyCollection<DataBindingModifierType> RegisteredDataBindingModifierTypes { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Registers a new modifier type for use in data bindings
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="pluginInfo">The PluginInfo of the plugin this modifier type belongs to</param>
|
||||||
|
/// <param name="dataBindingModifierType">The modifier type to register</param>
|
||||||
|
void RegisterModifierType([NotNull] PluginInfo pluginInfo, [NotNull] DataBindingModifierType dataBindingModifierType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Removes a modifier type so it is no longer available for use in data bindings
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataBindingModifierType">The modifier type to remove</param>
|
||||||
|
void RemoveModifierType([NotNull] DataBindingModifierType dataBindingModifierType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns all the data binding modifier types compatible with the provided type
|
||||||
|
/// </summary>
|
||||||
|
List<DataBindingModifierType> GetCompatibleModifierTypes(Type type);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a modifier type by its plugin GUID and type name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="modifierTypePluginGuid">The modifier type's plugin GUID</param>
|
||||||
|
/// <param name="modifierType">The type name of the modifier type</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
DataBindingModifierType GetModifierType(Guid modifierTypePluginGuid, string modifierType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Logs a modifier deserialization failure
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataBindingModifier">The modifier that failed to deserialize</param>
|
||||||
|
/// <param name="exception">The JSON exception that occurred</param>
|
||||||
|
void LogModifierDeserializationFailure(DataBindingModifier dataBindingModifier, JsonSerializationException exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -10,7 +10,14 @@ namespace Artemis.Core.Services.Interfaces
|
|||||||
{
|
{
|
||||||
public interface IDataModelService : IArtemisService
|
public interface IDataModelService : IArtemisService
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a read-only collection of all registered condition operators
|
||||||
|
/// </summary>
|
||||||
IReadOnlyCollection<DisplayConditionOperator> RegisteredConditionOperators { get; }
|
IReadOnlyCollection<DisplayConditionOperator> RegisteredConditionOperators { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a read-only collection of all registered data model expansions
|
||||||
|
/// </summary>
|
||||||
IReadOnlyCollection<DataModel> DataModelExpansions { get; }
|
IReadOnlyCollection<DataModel> DataModelExpansions { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -57,9 +64,30 @@ namespace Artemis.Core.Services.Interfaces
|
|||||||
/// <param name="displayConditionOperator">The layer condition operator to remove</param>
|
/// <param name="displayConditionOperator">The layer condition operator to remove</param>
|
||||||
void RemoveConditionOperator([NotNull] DisplayConditionOperator displayConditionOperator);
|
void RemoveConditionOperator([NotNull] DisplayConditionOperator displayConditionOperator);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns all the display condition operators compatible with the provided type
|
||||||
|
/// </summary>
|
||||||
List<DisplayConditionOperator> GetCompatibleConditionOperators(Type type);
|
List<DisplayConditionOperator> GetCompatibleConditionOperators(Type type);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a condition operator by its plugin GUID and type name
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="operatorPluginGuid">The operator's plugin GUID</param>
|
||||||
|
/// <param name="operatorType">The type name of the operator</param>
|
||||||
DisplayConditionOperator GetConditionOperator(Guid operatorPluginGuid, string operatorType);
|
DisplayConditionOperator GetConditionOperator(Guid operatorPluginGuid, string operatorType);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Logs a predicate deserialization failure
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="displayConditionPredicate">The predicate that failed to deserialize</param>
|
||||||
|
/// <param name="exception">The JSON exception that occurred</param>
|
||||||
void LogPredicateDeserializationFailure(DisplayConditionPredicate displayConditionPredicate, JsonException exception);
|
void LogPredicateDeserializationFailure(DisplayConditionPredicate displayConditionPredicate, JsonException exception);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Logs a list predicate deserialization failure
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="displayConditionListPredicate">The list predicate that failed to deserialize</param>
|
||||||
|
/// <param name="exception">The JSON exception that occurred</param>
|
||||||
void LogListPredicateDeserializationFailure(DisplayConditionListPredicate displayConditionListPredicate, JsonException exception);
|
void LogListPredicateDeserializationFailure(DisplayConditionListPredicate displayConditionListPredicate, JsonException exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Artemis.Storage.Entities.Profile.Conditions;
|
||||||
|
|
||||||
namespace Artemis.Storage.Entities.Profile.Abstract
|
namespace Artemis.Storage.Entities.Profile.Abstract
|
||||||
{
|
{
|
||||||
|
|||||||
@ -1,9 +1,7 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Text;
|
|
||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
|
|
||||||
namespace Artemis.Storage.Entities.Profile
|
namespace Artemis.Storage.Entities.Profile.Conditions
|
||||||
{
|
{
|
||||||
public class DisplayConditionGroupEntity : DisplayConditionPartEntity
|
public class DisplayConditionGroupEntity : DisplayConditionPartEntity
|
||||||
{
|
{
|
||||||
@ -2,7 +2,7 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
|
|
||||||
namespace Artemis.Storage.Entities.Profile
|
namespace Artemis.Storage.Entities.Profile.Conditions
|
||||||
{
|
{
|
||||||
public class DisplayConditionListEntity : DisplayConditionPartEntity
|
public class DisplayConditionListEntity : DisplayConditionPartEntity
|
||||||
{
|
{
|
||||||
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
|
|
||||||
namespace Artemis.Storage.Entities.Profile
|
namespace Artemis.Storage.Entities.Profile.Conditions
|
||||||
{
|
{
|
||||||
public class DisplayConditionListPredicateEntity : DisplayConditionPartEntity
|
public class DisplayConditionListPredicateEntity : DisplayConditionPartEntity
|
||||||
{
|
{
|
||||||
@ -1,7 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
|
|
||||||
namespace Artemis.Storage.Entities.Profile
|
namespace Artemis.Storage.Entities.Profile.Conditions
|
||||||
{
|
{
|
||||||
public class DisplayConditionPredicateEntity : DisplayConditionPartEntity
|
public class DisplayConditionPredicateEntity : DisplayConditionPartEntity
|
||||||
{
|
{
|
||||||
@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
|
||||||
namespace Artemis.Storage.Entities.Profile
|
namespace Artemis.Storage.Entities.Profile.Conditions
|
||||||
{
|
{
|
||||||
public class ProfileConditionEntity
|
public class ProfileConditionEntity
|
||||||
{
|
{
|
||||||
@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
|
namespace Artemis.Storage.Entities.Profile.DataBindings
|
||||||
|
{
|
||||||
|
public class DataBindingEntity
|
||||||
|
{
|
||||||
|
public DataBindingEntity()
|
||||||
|
{
|
||||||
|
Modifiers = new List<DataBindingModifierEntity>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public Guid? SourceDataModelGuid { get; set; }
|
||||||
|
public string SourcePropertyPath { get; set; }
|
||||||
|
public int DataBindingMode { get; set; }
|
||||||
|
public TimeSpan EasingTime { get; set; }
|
||||||
|
public int EasingFunction { get; set; }
|
||||||
|
|
||||||
|
public List<DataBindingModifierEntity> Modifiers { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Artemis.Storage.Entities.Profile.DataBindings
|
||||||
|
{
|
||||||
|
public class DataBindingModifierEntity
|
||||||
|
{
|
||||||
|
public string ModifierType { get; set; }
|
||||||
|
public Guid? ModifierTypePluginGuid { get; set; }
|
||||||
|
|
||||||
|
public int Order { get; set; }
|
||||||
|
public int ParameterType { get; set; }
|
||||||
|
|
||||||
|
public Guid? ParameterDataModelGuid { get; set; }
|
||||||
|
public string ParameterPropertyPath { get; set; }
|
||||||
|
|
||||||
|
// Stored as a string to be able to control serialization and deserialization ourselves
|
||||||
|
public string ParameterStaticValue { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
12
src/Artemis.Storage/Entities/Profile/KeyframeEntity.cs
Normal file
12
src/Artemis.Storage/Entities/Profile/KeyframeEntity.cs
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Artemis.Storage.Entities.Profile
|
||||||
|
{
|
||||||
|
public class KeyframeEntity
|
||||||
|
{
|
||||||
|
public TimeSpan Position { get; set; }
|
||||||
|
public int Timeline { get; set; }
|
||||||
|
public string Value { get; set; }
|
||||||
|
public int EasingFunction { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
|
using Artemis.Storage.Entities.Profile.DataBindings;
|
||||||
|
|
||||||
namespace Artemis.Storage.Entities.Profile
|
namespace Artemis.Storage.Entities.Profile
|
||||||
{
|
{
|
||||||
@ -17,13 +18,6 @@ namespace Artemis.Storage.Entities.Profile
|
|||||||
public bool KeyframesEnabled { get; set; }
|
public bool KeyframesEnabled { get; set; }
|
||||||
|
|
||||||
public List<KeyframeEntity> KeyframeEntities { get; set; }
|
public List<KeyframeEntity> KeyframeEntities { get; set; }
|
||||||
}
|
public DataBindingEntity DataBindingEntity { get; set; }
|
||||||
|
|
||||||
public class KeyframeEntity
|
|
||||||
{
|
|
||||||
public TimeSpan Position { get; set; }
|
|
||||||
public int Timeline { get; set; }
|
|
||||||
public string Value { get; set; }
|
|
||||||
public int EasingFunction { get; set; }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2,6 +2,7 @@
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
|
using Artemis.Core.Models.Profile;
|
||||||
using Artemis.Core.Models.Profile.Conditions;
|
using Artemis.Core.Models.Profile.Conditions;
|
||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
|
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
|
||||||
@ -74,16 +75,16 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
|||||||
if (type == "Static")
|
if (type == "Static")
|
||||||
{
|
{
|
||||||
if (!IsListGroup)
|
if (!IsListGroup)
|
||||||
DisplayConditionGroup.AddChild(new DisplayConditionPredicate(DisplayConditionGroup, PredicateType.Static));
|
DisplayConditionGroup.AddChild(new DisplayConditionPredicate(DisplayConditionGroup, ProfileRightSideType.Static));
|
||||||
else
|
else
|
||||||
DisplayConditionGroup.AddChild(new DisplayConditionListPredicate(DisplayConditionGroup, PredicateType.Static));
|
DisplayConditionGroup.AddChild(new DisplayConditionListPredicate(DisplayConditionGroup, ProfileRightSideType.Static));
|
||||||
}
|
}
|
||||||
else if (type == "Dynamic")
|
else if (type == "Dynamic")
|
||||||
{
|
{
|
||||||
if (!IsListGroup)
|
if (!IsListGroup)
|
||||||
DisplayConditionGroup.AddChild(new DisplayConditionPredicate(DisplayConditionGroup, PredicateType.Dynamic));
|
DisplayConditionGroup.AddChild(new DisplayConditionPredicate(DisplayConditionGroup, ProfileRightSideType.Dynamic));
|
||||||
else
|
else
|
||||||
DisplayConditionGroup.AddChild(new DisplayConditionListPredicate(DisplayConditionGroup, PredicateType.Dynamic));
|
DisplayConditionGroup.AddChild(new DisplayConditionListPredicate(DisplayConditionGroup, ProfileRightSideType.Dynamic));
|
||||||
}
|
}
|
||||||
else if (type == "List" && !IsListGroup)
|
else if (type == "List" && !IsListGroup)
|
||||||
DisplayConditionGroup.AddChild(new DisplayConditionList(DisplayConditionGroup));
|
DisplayConditionGroup.AddChild(new DisplayConditionList(DisplayConditionGroup));
|
||||||
|
|||||||
@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
|||||||
using System.Timers;
|
using System.Timers;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
using Artemis.Core.Models.Profile;
|
||||||
using Artemis.Core.Models.Profile.Conditions;
|
using Artemis.Core.Models.Profile.Conditions;
|
||||||
using Artemis.Core.Plugins.Settings;
|
using Artemis.Core.Plugins.Settings;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
@ -14,7 +15,6 @@ using Artemis.UI.Exceptions;
|
|||||||
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
|
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
|
||||||
using Artemis.UI.Shared.DataModelVisualization;
|
using Artemis.UI.Shared.DataModelVisualization;
|
||||||
using Artemis.UI.Shared.DataModelVisualization.Shared;
|
using Artemis.UI.Shared.DataModelVisualization.Shared;
|
||||||
using Artemis.UI.Shared.Services;
|
|
||||||
using Artemis.UI.Shared.Services.Interfaces;
|
using Artemis.UI.Shared.Services.Interfaces;
|
||||||
using Artemis.UI.Utilities;
|
using Artemis.UI.Utilities;
|
||||||
using Stylet;
|
using Stylet;
|
||||||
@ -69,7 +69,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
|||||||
}
|
}
|
||||||
|
|
||||||
public DisplayConditionListPredicate DisplayConditionListPredicate => (DisplayConditionListPredicate) Model;
|
public DisplayConditionListPredicate DisplayConditionListPredicate => (DisplayConditionListPredicate) Model;
|
||||||
public bool ShowRightSidePropertySelection => DisplayConditionListPredicate.PredicateType == PredicateType.Dynamic;
|
public bool ShowRightSidePropertySelection => DisplayConditionListPredicate.PredicateType == ProfileRightSideType.Dynamic;
|
||||||
public bool CanActivateRightSideInputViewModel => SelectedLeftSideProperty?.PropertyInfo != null;
|
public bool CanActivateRightSideInputViewModel => SelectedLeftSideProperty?.PropertyInfo != null;
|
||||||
public PluginSetting<bool> ShowDataModelValues { get; }
|
public PluginSetting<bool> ShowDataModelValues { get; }
|
||||||
|
|
||||||
@ -201,7 +201,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
|||||||
var listDataModelGuid = DisplayConditionListPredicate.ListDataModel.PluginInfo.Guid;
|
var listDataModelGuid = DisplayConditionListPredicate.ListDataModel.PluginInfo.Guid;
|
||||||
|
|
||||||
// If static, only allow selecting properties also supported by input
|
// If static, only allow selecting properties also supported by input
|
||||||
if (DisplayConditionListPredicate.PredicateType == PredicateType.Static)
|
if (DisplayConditionListPredicate.PredicateType == ProfileRightSideType.Static)
|
||||||
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
|
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
|
||||||
|
|
||||||
// Determine the left side property first
|
// Determine the left side property first
|
||||||
@ -216,7 +216,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
|||||||
SelectedOperator = DisplayConditionListPredicate.Operator;
|
SelectedOperator = DisplayConditionListPredicate.Operator;
|
||||||
|
|
||||||
// Determine the right side
|
// Determine the right side
|
||||||
if (DisplayConditionListPredicate.PredicateType == PredicateType.Dynamic)
|
if (DisplayConditionListPredicate.PredicateType == ProfileRightSideType.Dynamic)
|
||||||
{
|
{
|
||||||
SelectedRightSideProperty = RightSideDataModel.GetChildByPath(listDataModelGuid, DisplayConditionListPredicate.RightPropertyPath);
|
SelectedRightSideProperty = RightSideDataModel.GetChildByPath(listDataModelGuid, DisplayConditionListPredicate.RightPropertyPath);
|
||||||
RightSideDataModel.ApplyTypeFilter(true, leftSideType);
|
RightSideDataModel.ApplyTypeFilter(true, leftSideType);
|
||||||
@ -310,7 +310,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
|||||||
|
|
||||||
private void LeftDataModelUpdateRequested(object sender, EventArgs e)
|
private void LeftDataModelUpdateRequested(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (DisplayConditionListPredicate.PredicateType == PredicateType.Static)
|
if (DisplayConditionListPredicate.PredicateType == ProfileRightSideType.Static)
|
||||||
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
|
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -3,13 +3,13 @@ using System.Collections;
|
|||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using System.Timers;
|
using System.Timers;
|
||||||
|
using Artemis.Core.Models.Profile;
|
||||||
using Artemis.Core.Models.Profile.Conditions;
|
using Artemis.Core.Models.Profile.Conditions;
|
||||||
using Artemis.Core.Plugins.Settings;
|
using Artemis.Core.Plugins.Settings;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Ninject.Factories;
|
using Artemis.UI.Ninject.Factories;
|
||||||
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
|
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
|
||||||
using Artemis.UI.Shared.DataModelVisualization.Shared;
|
using Artemis.UI.Shared.DataModelVisualization.Shared;
|
||||||
using Artemis.UI.Shared.Services;
|
|
||||||
using Artemis.UI.Shared.Services.Interfaces;
|
using Artemis.UI.Shared.Services.Interfaces;
|
||||||
using Artemis.UI.Utilities;
|
using Artemis.UI.Utilities;
|
||||||
using Humanizer;
|
using Humanizer;
|
||||||
@ -18,13 +18,13 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
|||||||
{
|
{
|
||||||
public class DisplayConditionListViewModel : DisplayConditionViewModel
|
public class DisplayConditionListViewModel : DisplayConditionViewModel
|
||||||
{
|
{
|
||||||
private readonly IProfileEditorService _profileEditorService;
|
|
||||||
private readonly IDataModelUIService _dataModelUIService;
|
private readonly IDataModelUIService _dataModelUIService;
|
||||||
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
|
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
|
||||||
|
private readonly IProfileEditorService _profileEditorService;
|
||||||
|
private readonly Timer _updateTimer;
|
||||||
private bool _isInitialized;
|
private bool _isInitialized;
|
||||||
private DataModelListViewModel _selectedListProperty;
|
private DataModelListViewModel _selectedListProperty;
|
||||||
private DataModelPropertiesViewModel _targetDataModel;
|
private DataModelPropertiesViewModel _targetDataModel;
|
||||||
private readonly Timer _updateTimer;
|
|
||||||
|
|
||||||
public DisplayConditionListViewModel(
|
public DisplayConditionListViewModel(
|
||||||
DisplayConditionList displayConditionList,
|
DisplayConditionList displayConditionList,
|
||||||
@ -73,7 +73,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
|||||||
}
|
}
|
||||||
|
|
||||||
public string SelectedListOperator => DisplayConditionList.ListOperator.Humanize();
|
public string SelectedListOperator => DisplayConditionList.ListOperator.Humanize();
|
||||||
|
|
||||||
public void SelectListOperator(string type)
|
public void SelectListOperator(string type)
|
||||||
{
|
{
|
||||||
var enumValue = Enum.Parse<ListOperator>(type);
|
var enumValue = Enum.Parse<ListOperator>(type);
|
||||||
@ -86,9 +86,9 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
|||||||
public void AddCondition(string type)
|
public void AddCondition(string type)
|
||||||
{
|
{
|
||||||
if (type == "Static")
|
if (type == "Static")
|
||||||
DisplayConditionList.AddChild(new DisplayConditionPredicate(DisplayConditionList, PredicateType.Static));
|
DisplayConditionList.AddChild(new DisplayConditionPredicate(DisplayConditionList, ProfileRightSideType.Static));
|
||||||
else if (type == "Dynamic")
|
else if (type == "Dynamic")
|
||||||
DisplayConditionList.AddChild(new DisplayConditionPredicate(DisplayConditionList, PredicateType.Dynamic));
|
DisplayConditionList.AddChild(new DisplayConditionPredicate(DisplayConditionList, ProfileRightSideType.Dynamic));
|
||||||
|
|
||||||
Update();
|
Update();
|
||||||
_profileEditorService.UpdateSelectedProfileElement();
|
_profileEditorService.UpdateSelectedProfileElement();
|
||||||
@ -125,26 +125,6 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
|||||||
IsInitialized = true;
|
IsInitialized = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
_updateTimer.Stop();
|
|
||||||
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
|
|
||||||
{
|
|
||||||
if (TargetDataModelOpen)
|
|
||||||
{
|
|
||||||
TargetDataModel?.Update(_dataModelUIService);
|
|
||||||
SelectedListProperty?.Update(_dataModelUIService);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TargetDataModelUpdateRequested(object sender, EventArgs e)
|
|
||||||
{
|
|
||||||
TargetDataModel.ApplyTypeFilter(true, typeof(IList));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ApplyList()
|
public void ApplyList()
|
||||||
{
|
{
|
||||||
DisplayConditionList.UpdateList(SelectedListProperty.DataModel, SelectedListProperty.PropertyPath);
|
DisplayConditionList.UpdateList(SelectedListProperty.DataModel, SelectedListProperty.PropertyPath);
|
||||||
@ -198,6 +178,26 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
|||||||
childViewModel.Update();
|
childViewModel.Update();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
_updateTimer.Stop();
|
||||||
|
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUpdateTimerOnElapsed(object sender, ElapsedEventArgs e)
|
||||||
|
{
|
||||||
|
if (TargetDataModelOpen)
|
||||||
|
{
|
||||||
|
TargetDataModel?.Update(_dataModelUIService);
|
||||||
|
SelectedListProperty?.Update(_dataModelUIService);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TargetDataModelUpdateRequested(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
TargetDataModel.ApplyTypeFilter(true, typeof(IList));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private void ExecuteSelectListProperty(object context)
|
private void ExecuteSelectListProperty(object context)
|
||||||
{
|
{
|
||||||
|
|||||||
@ -5,6 +5,7 @@ using System.Threading.Tasks;
|
|||||||
using System.Timers;
|
using System.Timers;
|
||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Input;
|
using System.Windows.Input;
|
||||||
|
using Artemis.Core.Models.Profile;
|
||||||
using Artemis.Core.Models.Profile.Conditions;
|
using Artemis.Core.Models.Profile.Conditions;
|
||||||
using Artemis.Core.Plugins.Settings;
|
using Artemis.Core.Plugins.Settings;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
@ -13,7 +14,6 @@ using Artemis.UI.Events;
|
|||||||
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
|
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
|
||||||
using Artemis.UI.Shared.DataModelVisualization;
|
using Artemis.UI.Shared.DataModelVisualization;
|
||||||
using Artemis.UI.Shared.DataModelVisualization.Shared;
|
using Artemis.UI.Shared.DataModelVisualization.Shared;
|
||||||
using Artemis.UI.Shared.Services;
|
|
||||||
using Artemis.UI.Shared.Services.Interfaces;
|
using Artemis.UI.Shared.Services.Interfaces;
|
||||||
using Artemis.UI.Utilities;
|
using Artemis.UI.Utilities;
|
||||||
using Stylet;
|
using Stylet;
|
||||||
@ -26,6 +26,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
|||||||
private readonly IDataModelUIService _dataModelUIService;
|
private readonly IDataModelUIService _dataModelUIService;
|
||||||
private readonly IEventAggregator _eventAggregator;
|
private readonly IEventAggregator _eventAggregator;
|
||||||
private readonly IProfileEditorService _profileEditorService;
|
private readonly IProfileEditorService _profileEditorService;
|
||||||
|
private readonly Timer _updateTimer;
|
||||||
private bool _isInitialized;
|
private bool _isInitialized;
|
||||||
private DataModelPropertiesViewModel _leftSideDataModel;
|
private DataModelPropertiesViewModel _leftSideDataModel;
|
||||||
private BindableCollection<DisplayConditionOperator> _operators;
|
private BindableCollection<DisplayConditionOperator> _operators;
|
||||||
@ -38,7 +39,6 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
|||||||
private DataModelVisualizationViewModel _selectedRightSideProperty;
|
private DataModelVisualizationViewModel _selectedRightSideProperty;
|
||||||
|
|
||||||
private List<Type> _supportedInputTypes;
|
private List<Type> _supportedInputTypes;
|
||||||
private readonly Timer _updateTimer;
|
|
||||||
|
|
||||||
public DisplayConditionPredicateViewModel(
|
public DisplayConditionPredicateViewModel(
|
||||||
DisplayConditionPredicate displayConditionPredicate,
|
DisplayConditionPredicate displayConditionPredicate,
|
||||||
@ -68,7 +68,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
|||||||
}
|
}
|
||||||
|
|
||||||
public DisplayConditionPredicate DisplayConditionPredicate => (DisplayConditionPredicate) Model;
|
public DisplayConditionPredicate DisplayConditionPredicate => (DisplayConditionPredicate) Model;
|
||||||
public bool ShowRightSidePropertySelection => DisplayConditionPredicate.PredicateType == PredicateType.Dynamic;
|
public bool ShowRightSidePropertySelection => DisplayConditionPredicate.PredicateType == ProfileRightSideType.Dynamic;
|
||||||
public bool CanActivateRightSideInputViewModel => SelectedLeftSideProperty?.PropertyInfo != null;
|
public bool CanActivateRightSideInputViewModel => SelectedLeftSideProperty?.PropertyInfo != null;
|
||||||
public PluginSetting<bool> ShowDataModelValues { get; }
|
public PluginSetting<bool> ShowDataModelValues { get; }
|
||||||
|
|
||||||
@ -199,11 +199,11 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
|||||||
|
|
||||||
public override void Update()
|
public override void Update()
|
||||||
{
|
{
|
||||||
if (LeftSideDataModel == null || DisplayConditionPredicate.PredicateType == PredicateType.Dynamic && RightSideDataModel == null)
|
if (LeftSideDataModel == null || DisplayConditionPredicate.PredicateType == ProfileRightSideType.Dynamic && RightSideDataModel == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// If static, only allow selecting properties also supported by input
|
// If static, only allow selecting properties also supported by input
|
||||||
if (DisplayConditionPredicate.PredicateType == PredicateType.Static)
|
if (DisplayConditionPredicate.PredicateType == ProfileRightSideType.Static)
|
||||||
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
|
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
|
||||||
|
|
||||||
// Determine the left side property first
|
// Determine the left side property first
|
||||||
@ -218,7 +218,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
|||||||
SelectedOperator = DisplayConditionPredicate.Operator;
|
SelectedOperator = DisplayConditionPredicate.Operator;
|
||||||
|
|
||||||
// Determine the right side
|
// Determine the right side
|
||||||
if (DisplayConditionPredicate.PredicateType == PredicateType.Dynamic)
|
if (DisplayConditionPredicate.PredicateType == ProfileRightSideType.Dynamic)
|
||||||
{
|
{
|
||||||
SelectedRightSideProperty = LeftSideDataModel.GetChildForCondition(DisplayConditionPredicate, DisplayConditionSide.Right);
|
SelectedRightSideProperty = LeftSideDataModel.GetChildForCondition(DisplayConditionPredicate, DisplayConditionSide.Right);
|
||||||
RightSideDataModel.ApplyTypeFilter(true, leftSideType);
|
RightSideDataModel.ApplyTypeFilter(true, leftSideType);
|
||||||
@ -300,7 +300,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
|||||||
private void RightDataModelUpdateRequested(object sender, EventArgs e)
|
private void RightDataModelUpdateRequested(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType;
|
var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType;
|
||||||
if (DisplayConditionPredicate.PredicateType == PredicateType.Dynamic)
|
if (DisplayConditionPredicate.PredicateType == ProfileRightSideType.Dynamic)
|
||||||
SelectedRightSideProperty = LeftSideDataModel.GetChildForCondition(DisplayConditionPredicate, DisplayConditionSide.Right);
|
SelectedRightSideProperty = LeftSideDataModel.GetChildForCondition(DisplayConditionPredicate, DisplayConditionSide.Right);
|
||||||
|
|
||||||
// With the data model updated, also reapply the filter
|
// With the data model updated, also reapply the filter
|
||||||
@ -309,7 +309,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
|
|||||||
|
|
||||||
private void LeftDataModelUpdateRequested(object sender, EventArgs e)
|
private void LeftDataModelUpdateRequested(object sender, EventArgs e)
|
||||||
{
|
{
|
||||||
if (DisplayConditionPredicate.PredicateType == PredicateType.Static)
|
if (DisplayConditionPredicate.PredicateType == ProfileRightSideType.Static)
|
||||||
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
|
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user