diff --git a/src/Artemis.Core/Models/Profile/Colors/ColorGradient.cs b/src/Artemis.Core/Models/Profile/Colors/ColorGradient.cs
index fc7108775..3307dcac6 100644
--- a/src/Artemis.Core/Models/Profile/Colors/ColorGradient.cs
+++ b/src/Artemis.Core/Models/Profile/Colors/ColorGradient.cs
@@ -3,35 +3,35 @@ using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Runtime.CompilerServices;
-using System.Windows.Documents;
using Artemis.Core.Annotations;
using SkiaSharp;
using Stylet;
namespace Artemis.Core.Models.Profile.Colors
{
+ ///
+ /// A gradient containing a list of s
+ ///
public class ColorGradient : INotifyPropertyChanged
{
- private float _rotation;
-
+ ///
+ /// Creates a new instance of the class
+ ///
public ColorGradient()
{
Stops = new BindableCollection();
}
+ ///
+ /// Gets a list of all the s in the gradient
+ ///
public BindableCollection Stops { get; }
- public float Rotation
- {
- get => _rotation;
- set
- {
- if (value.Equals(_rotation)) return;
- _rotation = value;
- OnPropertyChanged();
- }
- }
-
+ ///
+ /// Gets all the colors in the color gradient
+ ///
+ /// The amount of times to repeat the colors
+ ///
public SKColor[] GetColorsArray(int timesToRepeat = 0)
{
if (timesToRepeat == 0)
@@ -46,6 +46,14 @@ namespace Artemis.Core.Models.Profile.Colors
return result.ToArray();
}
+ ///
+ /// Gets all the positions in the color gradient
+ ///
+ ///
+ /// The amount of times to repeat the positions, positions will get squished together and
+ /// always stay between 0.0 and 1.0
+ ///
+ ///
public float[] GetPositionsArray(int timesToRepeat = 0)
{
if (timesToRepeat == 0)
@@ -65,11 +73,18 @@ namespace Artemis.Core.Models.Profile.Colors
return result.ToArray();
}
+ ///
+ /// Triggers a property changed event of the collection
+ ///
public void OnColorValuesUpdated()
{
OnPropertyChanged(nameof(Stops));
}
+ ///
+ /// Gets a color at any position between 0.0 and 1.0 using interpolation
+ ///
+ /// A position between 0.0 and 1.0
public SKColor GetColor(float position)
{
if (!Stops.Any())
@@ -119,6 +134,7 @@ namespace Artemis.Core.Models.Profile.Colors
#region PropertyChanged
+ ///
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
diff --git a/src/Artemis.Core/Models/Profile/Colors/ColorGradientStop.cs b/src/Artemis.Core/Models/Profile/Colors/ColorGradientStop.cs
index abc89947a..71bc35f52 100644
--- a/src/Artemis.Core/Models/Profile/Colors/ColorGradientStop.cs
+++ b/src/Artemis.Core/Models/Profile/Colors/ColorGradientStop.cs
@@ -5,17 +5,26 @@ using SkiaSharp;
namespace Artemis.Core.Models.Profile.Colors
{
+ ///
+ /// A color with a position, usually contained in a
+ ///
public class ColorGradientStop : INotifyPropertyChanged
{
private SKColor _color;
private float _position;
+ ///
+ /// Creates a new instance of the class
+ ///
public ColorGradientStop(SKColor color, float position)
{
Color = color;
Position = position;
}
+ ///
+ /// Gets or sets the color of the stop
+ ///
public SKColor Color
{
get => _color;
@@ -27,6 +36,9 @@ namespace Artemis.Core.Models.Profile.Colors
}
}
+ ///
+ /// Gets or sets the position of the stop
+ ///
public float Position
{
get => _position;
@@ -40,6 +52,7 @@ namespace Artemis.Core.Models.Profile.Colors
#region PropertyChanged
+ ///
public event PropertyChangedEventHandler PropertyChanged;
[NotifyPropertyChangedInvocator]
diff --git a/src/Artemis.Core/Models/Profile/Conditions/Abstract/DisplayConditionPart.cs b/src/Artemis.Core/Models/Profile/Conditions/Abstract/DisplayConditionPart.cs
index 0531e1a5b..898e824cc 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Abstract/DisplayConditionPart.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Abstract/DisplayConditionPart.cs
@@ -1,22 +1,30 @@
using System.Collections.Generic;
-using System.Linq.Expressions;
using Artemis.Core.Services.Interfaces;
using Artemis.Storage.Entities.Profile.Abstract;
namespace Artemis.Core.Models.Profile.Conditions.Abstract
{
+ ///
+ /// An abstract class for display condition parts
+ ///
public abstract class DisplayConditionPart
{
- private readonly List _children;
+ private readonly List _children = new List();
+
+ ///
+ /// Gets the parent of this part
+ ///
+ public DisplayConditionPart Parent { get; internal set; }
- protected DisplayConditionPart()
- {
- _children = new List();
- }
-
- public DisplayConditionPart Parent { get; set; }
+ ///
+ /// Gets the children of this part
+ ///
public IReadOnlyList Children => _children.AsReadOnly();
+ ///
+ /// Adds a child to the display condition part's collection
+ ///
+ ///
public void AddChild(DisplayConditionPart displayConditionPart)
{
if (!_children.Contains(displayConditionPart))
@@ -26,6 +34,10 @@ namespace Artemis.Core.Models.Profile.Conditions.Abstract
}
}
+ ///
+ /// Removes a child from the display condition part's collection
+ ///
+ /// The child to remove
public void RemoveChild(DisplayConditionPart displayConditionPart)
{
if (_children.Contains(displayConditionPart))
@@ -36,13 +48,13 @@ namespace Artemis.Core.Models.Profile.Conditions.Abstract
}
///
- /// Evaluates the condition part on the data model
+ /// Evaluates the condition part on the data model
///
///
public abstract bool Evaluate();
///
- /// Evaluates the condition part on the given target (currently only for lists)
+ /// Evaluates the condition part on the given target (currently only for lists)
///
///
///
diff --git a/src/Artemis.Core/Models/Profile/Conditions/DisplayCondition.cs b/src/Artemis.Core/Models/Profile/Conditions/DisplayCondition.cs
deleted file mode 100644
index 96bf9e33f..000000000
--- a/src/Artemis.Core/Models/Profile/Conditions/DisplayCondition.cs
+++ /dev/null
@@ -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> ExpressionTree { get; set; }
- public DisplayConditionGroup RootGroup { get; set; }
- }
-}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionGroup.cs b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionGroup.cs
index 8250aa911..640216e7e 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionGroup.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionGroup.cs
@@ -4,17 +4,31 @@ using Artemis.Core.Models.Profile.Conditions.Abstract;
using Artemis.Core.Services.Interfaces;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract;
+using Artemis.Storage.Entities.Profile.Conditions;
namespace Artemis.Core.Models.Profile.Conditions
{
+ ///
+ /// A group containing zero to many s which it evaluates using a boolean specific
+ /// operator
+ ///
public class DisplayConditionGroup : DisplayConditionPart
{
+ ///
+ /// Creates a new instance of the class
+ ///
+ ///
public DisplayConditionGroup(DisplayConditionPart parent)
{
Parent = parent;
Entity = new DisplayConditionGroupEntity();
}
+ ///
+ /// Creates a new instance of the class
+ ///
+ ///
+ ///
public DisplayConditionGroup(DisplayConditionPart parent, DisplayConditionGroupEntity entity)
{
Parent = parent;
@@ -34,9 +48,14 @@ namespace Artemis.Core.Models.Profile.Conditions
}
}
+ ///
+ /// Gets or sets the boolean operator of this group
+ ///
public BooleanOperator BooleanOperator { get; set; }
- public DisplayConditionGroupEntity Entity { get; set; }
+ internal DisplayConditionGroupEntity Entity { get; set; }
+
+ ///
public override bool Evaluate()
{
// Empty groups are always true
@@ -61,6 +80,7 @@ namespace Artemis.Core.Models.Profile.Conditions
}
}
+ ///
public override bool EvaluateObject(object target)
{
// Empty groups are always true
diff --git a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionList.cs b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionList.cs
index 42b7b33ed..1bcd9f271 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionList.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionList.cs
@@ -8,6 +8,7 @@ using Artemis.Core.Plugins.DataModelExpansions;
using Artemis.Core.Services.Interfaces;
using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract;
+using Artemis.Storage.Entities.Profile.Conditions;
namespace Artemis.Core.Models.Profile.Conditions
{
diff --git a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionListPredicate.cs b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionListPredicate.cs
index cff1a145b..4c0f9dbb9 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionListPredicate.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionListPredicate.cs
@@ -6,15 +6,15 @@ using Artemis.Core.Extensions;
using Artemis.Core.Models.Profile.Conditions.Abstract;
using Artemis.Core.Plugins.DataModelExpansions;
using Artemis.Core.Services.Interfaces;
-using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract;
+using Artemis.Storage.Entities.Profile.Conditions;
using Newtonsoft.Json;
namespace Artemis.Core.Models.Profile.Conditions
{
public class DisplayConditionListPredicate : DisplayConditionPart
{
- public DisplayConditionListPredicate(DisplayConditionPart parent, PredicateType predicateType)
+ public DisplayConditionListPredicate(DisplayConditionPart parent, ProfileRightSideType predicateType)
{
Parent = parent;
PredicateType = predicateType;
@@ -27,14 +27,14 @@ namespace Artemis.Core.Models.Profile.Conditions
{
Parent = parent;
Entity = entity;
- PredicateType = (PredicateType) entity.PredicateType;
+ PredicateType = (ProfileRightSideType) entity.PredicateType;
ApplyParentList();
}
public DisplayConditionListPredicateEntity Entity { get; set; }
- public PredicateType PredicateType { get; set; }
+ public ProfileRightSideType PredicateType { get; set; }
public DisplayConditionOperator Operator { get; private set; }
public Type ListType { get; private set; }
@@ -57,6 +57,7 @@ namespace Artemis.Core.Models.Profile.Conditions
UpdateList(parentList.ListDataModel, parentList.ListPropertyPath);
return;
}
+
current = current.Parent;
}
}
@@ -77,9 +78,7 @@ namespace Artemis.Core.Models.Profile.Conditions
ListType = listType;
}
else
- {
ListType = null;
- }
ListDataModel = dataModel;
ListPropertyPath = path;
@@ -109,7 +108,7 @@ namespace Artemis.Core.Models.Profile.Conditions
if (!ListContainsInnerPath(path))
throw new ArtemisCoreException($"List type {ListType.Name} does not contain path {path}");
- PredicateType = PredicateType.Dynamic;
+ PredicateType = ProfileRightSideType.Dynamic;
RightPropertyPath = path;
CreateExpression();
@@ -117,7 +116,7 @@ namespace Artemis.Core.Models.Profile.Conditions
public void UpdateRightSideStatic(object staticValue)
{
- PredicateType = PredicateType.Static;
+ PredicateType = ProfileRightSideType.Static;
RightPropertyPath = null;
SetStaticValue(staticValue);
@@ -145,18 +144,52 @@ namespace Artemis.Core.Models.Profile.Conditions
CreateExpression();
}
- private void CreateExpression()
+ public override bool Evaluate()
{
- CompiledListPredicate = null;
+ return false;
+ }
- if (Operator == null)
- return;
+ public override bool EvaluateObject(object target)
+ {
+ 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
- if (PredicateType == PredicateType.Dynamic && Operator.SupportsRightSide)
- CreateDynamicExpression();
+ public bool ListContainsInnerPath(string path)
+ {
+ 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()
@@ -173,16 +206,6 @@ namespace Artemis.Core.Models.Profile.Conditions
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)
{
// Left side
@@ -198,13 +221,13 @@ namespace Artemis.Core.Models.Profile.Conditions
}
// Right side dynamic
- if (PredicateType == PredicateType.Dynamic && Entity.RightPropertyPath != null)
+ if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightPropertyPath != null)
{
if (ListContainsInnerPath(Entity.RightPropertyPath))
UpdateLeftSide(Entity.LeftPropertyPath);
}
// Right side static
- else if (PredicateType == PredicateType.Static && Entity.RightStaticValue != null)
+ else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null)
{
try
{
@@ -245,6 +268,20 @@ namespace Artemis.Core.Models.Profile.Conditions
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()
{
if (LeftPropertyPath == null || Operator == null)
@@ -258,7 +295,7 @@ namespace Artemis.Core.Models.Profile.Conditions
private void ValidateRightSide()
{
var leftSideType = GetTypeAtInnerPath(LeftPropertyPath);
- if (PredicateType == PredicateType.Dynamic)
+ if (PredicateType == ProfileRightSideType.Dynamic)
{
if (RightPropertyPath == null)
return;
@@ -349,43 +386,5 @@ namespace Artemis.Core.Models.Profile.Conditions
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;
- }
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionOperator.cs
index 535168b46..235fe13f0 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionOperator.cs
@@ -8,6 +8,9 @@ using Artemis.Core.Services.Interfaces;
namespace Artemis.Core.Models.Profile.Conditions
{
+ ///
+ /// A display condition operator is used by the conditions system to perform a specific boolean check
+ ///
public abstract class DisplayConditionOperator
{
private IDataModelService _dataModelService;
@@ -39,6 +42,9 @@ namespace Artemis.Core.Models.Profile.Conditions
///
public bool SupportsRightSide { get; protected set; } = true;
+ ///
+ /// Returns whether the given type is supported by the operator
+ ///
public bool SupportsType(Type type)
{
if (type == null)
@@ -54,16 +60,6 @@ namespace Artemis.Core.Models.Profile.Conditions
///
public abstract BinaryExpression CreateExpression(Expression leftSide, Expression rightSide);
- ///
- /// Returns an expression that checks the given expression for null
- ///
- ///
- ///
- protected Expression CreateNullCheckExpression(Expression expression)
- {
- return Expression.NotEqual(expression, Expression.Constant(null));
- }
-
internal void Register(PluginInfo pluginInfo, IDataModelService dataModelService)
{
if (_registered)
@@ -90,7 +86,7 @@ namespace Artemis.Core.Models.Profile.Conditions
private void InstanceOnPluginDisabled(object sender, EventArgs e)
{
- // Profile editor service will call Unsubscribe
+ // The service will call Unsubscribe
_dataModelService.RemoveConditionOperator(this);
}
}
diff --git a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionPredicate.cs b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionPredicate.cs
index 0b8e5eb85..a1e34f885 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionPredicate.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionPredicate.cs
@@ -6,45 +6,95 @@ using Artemis.Core.Extensions;
using Artemis.Core.Models.Profile.Conditions.Abstract;
using Artemis.Core.Plugins.DataModelExpansions;
using Artemis.Core.Services.Interfaces;
-using Artemis.Storage.Entities.Profile;
using Artemis.Storage.Entities.Profile.Abstract;
+using Artemis.Storage.Entities.Profile.Conditions;
using Newtonsoft.Json;
namespace Artemis.Core.Models.Profile.Conditions
{
+ ///
+ /// A predicate in a display condition using either two data model values or one data model value and a
+ /// static value
+ ///
public class DisplayConditionPredicate : DisplayConditionPart
{
- public DisplayConditionPredicate(DisplayConditionPart parent, PredicateType predicateType)
+ ///
+ /// Creates a new instance of the class
+ ///
+ ///
+ ///
+ public DisplayConditionPredicate(DisplayConditionPart parent, ProfileRightSideType predicateType)
{
Parent = parent;
PredicateType = predicateType;
Entity = new DisplayConditionPredicateEntity();
}
+ ///
+ /// Creates a new instance of the class
+ ///
+ ///
+ ///
public DisplayConditionPredicate(DisplayConditionPart parent, DisplayConditionPredicateEntity entity)
{
Parent = parent;
Entity = entity;
- PredicateType = (PredicateType) entity.PredicateType;
+ PredicateType = (ProfileRightSideType) entity.PredicateType;
}
- public DisplayConditionPredicateEntity Entity { get; set; }
+ ///
+ /// Gets or sets the predicate type
+ ///
+ public ProfileRightSideType PredicateType { get; set; }
- public PredicateType PredicateType { get; set; }
+ ///
+ /// Gets the operator
+ ///
public DisplayConditionOperator Operator { get; private set; }
+ ///
+ /// Gets the currently used instance of the left data model
+ ///
public DataModel LeftDataModel { get; private set; }
+
+ ///
+ /// Gets the path of the left property in the
+ ///
public string LeftPropertyPath { get; private set; }
+
+ ///
+ /// Gets the currently used instance of the right data model
+ ///
public DataModel RightDataModel { get; private set; }
+
+ ///
+ /// Gets the path of the right property in the
+ ///
public string RightPropertyPath { get; private set; }
+
+ ///
+ /// Gets the right static value, only used it is
+ ///
+ ///
public object RightStaticValue { get; private set; }
- public DataModel ListDataModel { get; private set; }
- public string ListPropertyPath { get; private set; }
+ ///
+ /// Gets the compiled function that evaluates this predicate if it of a dynamic
+ ///
public Func CompiledDynamicPredicate { get; private set; }
- public Func CompiledStaticPredicate { get; private set; }
- public Func CompiledListPredicate { get; private set; }
+ ///
+ /// Gets the compiled function that evaluates this predicate if it is of a static
+ ///
+ public Func CompiledStaticPredicate { get; private set; }
+
+ internal DisplayConditionPredicateEntity Entity { get; set; }
+
+ ///
+ /// Updates the left side of the predicate
+ ///
+ /// The data model of the left side value
+ /// The path pointing to the left side value inside the data model
public void UpdateLeftSide(DataModel dataModel, string path)
{
if (dataModel != null && path == null)
@@ -67,6 +117,11 @@ namespace Artemis.Core.Models.Profile.Conditions
CreateExpression();
}
+ ///
+ /// Updates the right side of the predicate, makes the predicate dynamic and re-compiles the expression
+ ///
+ /// The data model of the right side value
+ /// The path pointing to the right side value inside the data model
public void UpdateRightSide(DataModel dataModel, string path)
{
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}'");
}
- PredicateType = PredicateType.Dynamic;
+ PredicateType = ProfileRightSideType.Dynamic;
RightDataModel = dataModel;
RightPropertyPath = path;
CreateExpression();
}
+ ///
+ /// Updates the right side of the predicate, makes the predicate static and re-compiles the expression
+ ///
+ /// The right side value to use
public void UpdateRightSide(object staticValue)
{
- PredicateType = PredicateType.Static;
+ PredicateType = ProfileRightSideType.Static;
RightDataModel = 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();
}
+ ///
+ /// Updates the operator of the predicate and re-compiles the expression
+ ///
+ ///
public void UpdateOperator(DisplayConditionOperator displayConditionOperator)
{
+ // Calling CreateExpression will clear compiled expressions
if (displayConditionOperator == null)
{
Operator = null;
+ CreateExpression();
return;
}
+ // No need to clear compiled expressions, without a left data model they are already null
if (LeftDataModel == null)
{
Operator = displayConditionOperator;
@@ -113,26 +197,37 @@ namespace Artemis.Core.Models.Profile.Conditions
}
var leftType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
- if (displayConditionOperator.SupportsType(leftType))
- Operator = displayConditionOperator;
+ if (!displayConditionOperator.SupportsType(leftType))
+ 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();
}
- private void CreateExpression()
+ ///
+ public override bool Evaluate()
{
- CompiledDynamicPredicate = null;
- CompiledStaticPredicate = null;
- CompiledListPredicate = null;
+ if (CompiledDynamicPredicate != null)
+ return CompiledDynamicPredicate(LeftDataModel, RightDataModel);
+ if (CompiledStaticPredicate != null)
+ return CompiledStaticPredicate(LeftDataModel);
- if (Operator == null)
- return;
+ return false;
+ }
- // If the operator does not support a right side, create a static expression because the right side will simply be null
- if (PredicateType == PredicateType.Dynamic && Operator.SupportsRightSide)
- CreateDynamicExpression();
+ ///
+ public override bool EvaluateObject(object target)
+ {
+ return false;
+ }
- CreateStaticExpression();
+ ///
+ public override string ToString()
+ {
+ if (PredicateType == ProfileRightSideType.Dynamic)
+ return $"[Dynamic] {LeftPropertyPath} {Operator.Description} {RightPropertyPath}";
+ return $"[Static] {LeftPropertyPath} {Operator.Description} {RightStaticValue}";
}
internal override void ApplyToEntity()
@@ -149,24 +244,6 @@ namespace Artemis.Core.Models.Profile.Conditions
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)
{
// Left side
@@ -186,14 +263,14 @@ namespace Artemis.Core.Models.Profile.Conditions
}
// Right side dynamic
- if (PredicateType == PredicateType.Dynamic && Entity.RightDataModelGuid != null)
+ if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightDataModelGuid != null)
{
var dataModel = dataModelService.GetPluginDataModelByGuid(Entity.RightDataModelGuid.Value);
if (dataModel != null && dataModel.ContainsPath(Entity.RightPropertyPath))
UpdateRightSide(dataModel, Entity.RightPropertyPath);
}
// Right side static
- else if (PredicateType == PredicateType.Static && Entity.RightStaticValue != null)
+ else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null)
{
try
{
@@ -235,6 +312,21 @@ namespace Artemis.Core.Models.Profile.Conditions
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()
{
if (LeftDataModel == null || Operator == null)
@@ -248,7 +340,7 @@ namespace Artemis.Core.Models.Profile.Conditions
private void ValidateRightSide()
{
var leftSideType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
- if (PredicateType == PredicateType.Dynamic)
+ if (PredicateType == ProfileRightSideType.Dynamic)
{
if (RightDataModel == null)
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()
{
if (LeftDataModel == null || RightDataModel == null || Operator == null)
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
// 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)
rightSideAccessor = Expression.Convert(rightSideAccessor, leftSideAccessor.Type);
var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideAccessor);
-
- if (isListExpression)
- {
- var lambda = Expression.Lambda>(conditionExpression, leftSideParameter);
- CompiledListPredicate = lambda.Compile();
- }
- else
- {
- var lambda = Expression.Lambda>(conditionExpression, leftSideParameter, rightSideParameter);
- CompiledDynamicPredicate = lambda.Compile();
- }
+ var lambda = Expression.Lambda>(conditionExpression, leftSideParameter, rightSideParameter);
+ CompiledDynamicPredicate = lambda.Compile();
}
private void CreateStaticExpression()
@@ -337,18 +381,7 @@ namespace Artemis.Core.Models.Profile.Conditions
if (LeftDataModel == null || Operator == null)
return;
- var isListExpression = LeftDataModel.GetListTypeInPath(LeftPropertyPath) != null;
-
- 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);
+ var leftSideAccessor = CreateAccessor(LeftDataModel, LeftPropertyPath, "left", out var leftSideParameter);
// 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)
@@ -360,19 +393,10 @@ namespace Artemis.Core.Models.Profile.Conditions
: Expression.Constant(null, leftSideAccessor.Type);
var conditionExpression = Operator.CreateExpression(leftSideAccessor, rightSideConstant);
-
- if (isListExpression)
- {
- var lambda = Expression.Lambda>(conditionExpression, leftSideParameter);
- CompiledListPredicate = lambda.Compile();
- }
- else
- {
- var lambda = Expression.Lambda>(conditionExpression, leftSideParameter);
- CompiledStaticPredicate = lambda.Compile();
- }
+ var lambda = Expression.Lambda>(conditionExpression, leftSideParameter);
+ CompiledStaticPredicate = lambda.Compile();
}
-
+
private Expression CreateAccessor(DataModel dataModel, string path, string parameterName, out ParameterExpression parameter)
{
var listType = dataModel.GetListTypeInPath(path);
@@ -398,18 +422,5 @@ namespace Artemis.Core.Models.Profile.Conditions
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
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/EqualsConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/EqualsConditionOperator.cs
index 4868ad996..b70f5dc01 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Operators/EqualsConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/EqualsConditionOperator.cs
@@ -4,7 +4,7 @@ using System.Linq.Expressions;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
- public class EqualsConditionOperator : DisplayConditionOperator
+ internal class EqualsConditionOperator : DisplayConditionOperator
{
public override IReadOnlyCollection CompatibleTypes => new List {typeof(object)};
diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanConditionOperator.cs
index b8618a2b6..121e7f10a 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanConditionOperator.cs
@@ -4,7 +4,7 @@ using System.Linq.Expressions;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
- public class GreaterThanConditionOperator : DisplayConditionOperator
+ internal class GreaterThanConditionOperator : DisplayConditionOperator
{
public override IReadOnlyCollection CompatibleTypes => Constants.NumberTypes;
diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanOrEqualConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanOrEqualConditionOperator.cs
index d4955ee34..444ac15c6 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanOrEqualConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/GreaterThanOrEqualConditionOperator.cs
@@ -4,7 +4,7 @@ using System.Linq.Expressions;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
- public class GreaterThanOrEqualConditionOperator : DisplayConditionOperator
+ internal class GreaterThanOrEqualConditionOperator : DisplayConditionOperator
{
public override IReadOnlyCollection CompatibleTypes => Constants.NumberTypes;
diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanConditionOperator.cs
index fc083f400..0ce64ff72 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanConditionOperator.cs
@@ -4,7 +4,7 @@ using System.Linq.Expressions;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
- public class LessThanConditionOperator : DisplayConditionOperator
+ internal class LessThanConditionOperator : DisplayConditionOperator
{
public override IReadOnlyCollection CompatibleTypes => Constants.NumberTypes;
diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanOrEqualConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanOrEqualConditionOperator.cs
index ebc9dd871..092c5d7af 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanOrEqualConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/LessThanOrEqualConditionOperator.cs
@@ -4,7 +4,7 @@ using System.Linq.Expressions;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
- public class LessThanOrEqualConditionOperator : DisplayConditionOperator
+ internal class LessThanOrEqualConditionOperator : DisplayConditionOperator
{
public override IReadOnlyCollection CompatibleTypes => Constants.NumberTypes;
diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/NotEqualConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/NotEqualConditionOperator.cs
index f4dc78c71..84a025987 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Operators/NotEqualConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/NotEqualConditionOperator.cs
@@ -4,7 +4,7 @@ using System.Linq.Expressions;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
- public class NotEqualConditionOperator : DisplayConditionOperator
+ internal class NotEqualConditionOperator : DisplayConditionOperator
{
public override IReadOnlyCollection CompatibleTypes => new List { typeof(object) };
diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/StringContainsConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/StringContainsConditionOperator.cs
index f82d7e367..cb8f326f3 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Operators/StringContainsConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/StringContainsConditionOperator.cs
@@ -5,7 +5,7 @@ using System.Reflection;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
- public class StringContainsConditionOperator : DisplayConditionOperator
+ internal class StringContainsConditionOperator : DisplayConditionOperator
{
private readonly MethodInfo _toLower;
private readonly MethodInfo _contains;
diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/StringEndsWithConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/StringEndsWithConditionOperator.cs
index 69bcdc5b3..b117b3a4e 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Operators/StringEndsWithConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/StringEndsWithConditionOperator.cs
@@ -5,7 +5,7 @@ using System.Reflection;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
- public class StringEndsWithConditionOperator : DisplayConditionOperator
+ internal class StringEndsWithConditionOperator : DisplayConditionOperator
{
private readonly MethodInfo _toLower;
private readonly MethodInfo _endsWith;
diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/StringEqualsConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/StringEqualsConditionOperator.cs
index 56fce09f7..4c3006e32 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Operators/StringEqualsConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/StringEqualsConditionOperator.cs
@@ -5,7 +5,7 @@ using System.Reflection;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
- public class StringEqualsConditionOperator : DisplayConditionOperator
+ internal class StringEqualsConditionOperator : DisplayConditionOperator
{
private readonly MethodInfo _toLower;
diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/StringNotContainsConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/StringNotContainsConditionOperator.cs
index 11ee0144b..682269b9c 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Operators/StringNotContainsConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/StringNotContainsConditionOperator.cs
@@ -5,7 +5,7 @@ using System.Reflection;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
- public class StringNotContainsConditionOperator : DisplayConditionOperator
+ internal class StringNotContainsConditionOperator : DisplayConditionOperator
{
private readonly MethodInfo _toLower;
private readonly MethodInfo _contains;
diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/StringNotEqualConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/StringNotEqualConditionOperator.cs
index 7235dd5bc..ebef98d93 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Operators/StringNotEqualConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/StringNotEqualConditionOperator.cs
@@ -5,7 +5,7 @@ using System.Reflection;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
- public class StringNotEqualConditionOperator : DisplayConditionOperator
+ internal class StringNotEqualConditionOperator : DisplayConditionOperator
{
private readonly MethodInfo _toLower;
diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/StringNullConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/StringNullConditionOperator.cs
index 589b31223..0fc2c8394 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Operators/StringNullConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/StringNullConditionOperator.cs
@@ -4,7 +4,7 @@ using System.Linq.Expressions;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
- public class StringNullConditionOperator : DisplayConditionOperator
+ internal class StringNullConditionOperator : DisplayConditionOperator
{
public StringNullConditionOperator()
{
diff --git a/src/Artemis.Core/Models/Profile/Conditions/Operators/StringStartsWithConditionOperator.cs b/src/Artemis.Core/Models/Profile/Conditions/Operators/StringStartsWithConditionOperator.cs
index 06259737c..3049d1ec8 100644
--- a/src/Artemis.Core/Models/Profile/Conditions/Operators/StringStartsWithConditionOperator.cs
+++ b/src/Artemis.Core/Models/Profile/Conditions/Operators/StringStartsWithConditionOperator.cs
@@ -5,7 +5,7 @@ using System.Reflection;
namespace Artemis.Core.Models.Profile.Conditions.Operators
{
- public class StringStartsWithConditionOperator : DisplayConditionOperator
+ internal class StringStartsWithConditionOperator : DisplayConditionOperator
{
private readonly MethodInfo _toLower;
private readonly MethodInfo _startsWith;
diff --git a/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs b/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs
new file mode 100644
index 000000000..e15a79785
--- /dev/null
+++ b/src/Artemis.Core/Models/Profile/DataBindings/DataBinding.cs
@@ -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
+{
+ ///
+ /// A data binding that binds a certain to a value inside a
+ ///
+ public class DataBinding
+ {
+ private readonly List _modifiers = new List();
+
+ ///
+ /// The that the data binding targets
+ ///
+ public BaseLayerProperty Target { get; set; } // BIG FAT TODO: Take into account X and Y of SkPosition etc., forgot about it again :>
+
+ ///
+ /// Gets the currently used instance of the data model that contains the source of the data binding
+ ///
+ public DataModel SourceDataModel { get; private set; }
+
+ ///
+ /// Gets the path of the source property in the
+ ///
+ public string SourcePropertyPath { get; private set; }
+
+ ///
+ /// Gets a list of modifiers applied to this data binding
+ ///
+ public IReadOnlyList Modifiers => _modifiers.AsReadOnly();
+
+ ///
+ /// Adds a modifier to the data binding's collection
+ ///
+ public void AddModifier(DataBindingModifier modifier)
+ {
+ if (!_modifiers.Contains(modifier))
+ {
+ modifier.DataBinding = this;
+ modifier.CreateExpression();
+ _modifiers.Add(modifier);
+ }
+ }
+
+ ///
+ /// Removes a modifier from the data binding's collection
+ ///
+ public void RemoveModifier(DataBindingModifier modifier)
+ {
+ if (_modifiers.Contains(modifier))
+ {
+ modifier.DataBinding = null;
+ modifier.CreateExpression();
+ _modifiers.Remove(modifier);
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/DataBindings/DataBindingModifier.cs b/src/Artemis.Core/Models/Profile/DataBindings/DataBindingModifier.cs
new file mode 100644
index 000000000..4e34c3682
--- /dev/null
+++ b/src/Artemis.Core/Models/Profile/DataBindings/DataBindingModifier.cs
@@ -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
+{
+ ///
+ /// Modifies a data model value in a way defined by the modifier type
+ ///
+ public class DataBindingModifier
+ {
+ ///
+ /// Creates a new instance of the class
+ ///
+ public DataBindingModifier(DataBinding dataBinding, ProfileRightSideType parameterType)
+ {
+ DataBinding = dataBinding;
+ ParameterType = parameterType;
+ Entity = new DataBindingModifierEntity();
+ }
+
+ ///
+ /// Creates a new instance of the class
+ ///
+ public DataBindingModifier(DataBinding dataBinding, DataBindingModifierEntity entity)
+ {
+ DataBinding = dataBinding;
+ ParameterType = (ProfileRightSideType) entity.ParameterType;
+ Order = entity.Order;
+ Entity = entity;
+ }
+
+ ///
+ /// Gets the data binding this modifier is applied to
+ ///
+ public DataBinding DataBinding { get; internal set; }
+
+ ///
+ /// Gets the type of modifier that is being applied
+ ///
+ public DataBindingModifierType ModifierType { get; private set; }
+
+ ///
+ /// Gets the type of the parameter, can either be dynamic (based on a data model value) or static
+ ///
+ public ProfileRightSideType ParameterType { get; private set; }
+
+ ///
+ /// Gets the position at which the modifier appears on the data binding
+ ///
+ public int Order { get; internal set; }
+
+ ///
+ /// Gets the currently used instance of the parameter data model
+ ///
+ public DataModel ParameterDataModel { get; private set; }
+
+ ///
+ /// Gets the path of the parameter property in the
+ ///
+ public string ParameterPropertyPath { get; private set; }
+
+ ///
+ /// Gets the parameter static value, only used it is
+ ///
+ ///
+ public object ParameterStaticValue { get; private set; }
+
+ ///
+ /// Gets the compiled function that evaluates this predicate if it of a dynamic
+ ///
+ public Func CompiledDynamicPredicate { get; private set; }
+
+ ///
+ /// Gets the compiled function that evaluates this predicate if it is of a static
+ ///
+ public Func CompiledStaticPredicate { get; private set; }
+
+ internal DataBindingModifierEntity Entity { get; set; }
+
+ ///
+ /// Applies the modifier to the provided value
+ ///
+ /// The value to apply the modifier to, should be of the same type as the data binding target
+ /// The modified value
+ 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;
+ }
+
+ ///
+ /// Updates the modifier type of the modifier and re-compiles the expression
+ ///
+ ///
+ 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();
+ }
+
+ ///
+ /// Updates the parameter of the modifier, makes the modifier dynamic and re-compiles the expression
+ ///
+ /// The data model of the parameter
+ /// The path pointing to the parameter inside the data model
+ 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();
+ }
+
+ ///
+ /// Updates the parameter of the modifier, makes the modifier static and re-compiles the expression
+ ///
+ /// The static value to use as a parameter
+ 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>(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>(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(
+ Expression.Convert(parameter, dataModel.GetType()), // Cast to the appropriate type
+ Expression.Property
+ );
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/DataBindings/Modifiers/DataBindingModifierType.cs b/src/Artemis.Core/Models/Profile/DataBindings/Modifiers/DataBindingModifierType.cs
new file mode 100644
index 000000000..c6ee9ce9e
--- /dev/null
+++ b/src/Artemis.Core/Models/Profile/DataBindings/Modifiers/DataBindingModifierType.cs
@@ -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
+{
+ ///
+ /// A modifier that changes the source value of a data binding in some way
+ ///
+ public abstract class DataBindingModifierType
+ {
+ private bool _registered;
+ private IDataBindingService _dataBindingService;
+
+ ///
+ /// Gets the plugin info this data binding modifier belongs to
+ /// Note: Not set until after registering
+ ///
+ public PluginInfo PluginInfo { get; internal set; }
+
+ ///
+ /// Gets the data binding modifier this modifier type is applied to
+ ///
+ public DataBindingModifier Modifier { get; internal set; }
+
+ ///
+ /// Gets the types this modifier supports
+ ///
+ public abstract IReadOnlyCollection CompatibleTypes { get; }
+
+ ///
+ /// Gets or sets the description of this modifier
+ ///
+ public abstract string Description { get; }
+
+ ///
+ /// Gets or sets the icon of this modifier
+ ///
+ public abstract string Icon { get; }
+
+ ///
+ /// Gets or sets whether this modifier supports a parameter, defaults to true
+ ///
+ public bool SupportsParameter { get; protected set; } = true;
+
+ ///
+ /// Returns whether the given type is supported by the modifier
+ ///
+ public bool SupportsType(Type type)
+ {
+ if (type == null)
+ return true;
+ return CompatibleTypes.Any(t => t.IsCastableFrom(type));
+ }
+
+ ///
+ /// Creates a binary expression comparing two types
+ ///
+ /// The current value of the data binding
+ /// An argument passed to the modifier, either static of dynamic
+ ///
+ public abstract Expression 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);
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Models/Profile/ProfileRightSideType.cs b/src/Artemis.Core/Models/Profile/ProfileRightSideType.cs
new file mode 100644
index 000000000..55f95795c
--- /dev/null
+++ b/src/Artemis.Core/Models/Profile/ProfileRightSideType.cs
@@ -0,0 +1,18 @@
+namespace Artemis.Core.Models.Profile
+{
+ ///
+ /// An enum defining the right side type of a profile entity
+ ///
+ public enum ProfileRightSideType
+ {
+ ///
+ /// A static right side value
+ ///
+ Static,
+
+ ///
+ /// A dynamic right side value based on a path in a data model
+ ///
+ Dynamic
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/DataBindingService.cs b/src/Artemis.Core/Services/DataBindingService.cs
index 1fa8e0e10..c6d1f5e44 100644
--- a/src/Artemis.Core/Services/DataBindingService.cs
+++ b/src/Artemis.Core/Services/DataBindingService.cs
@@ -1,11 +1,107 @@
using System;
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 Newtonsoft.Json;
+using Serilog;
namespace Artemis.Core.Services
{
- public class DataBindingService : IDataBindingService
+ internal class DataBindingService : IDataBindingService
{
+ private readonly ILogger _logger;
+ private readonly List _registeredDataBindingModifierTypes;
+
+ public DataBindingService(ILogger logger)
+ {
+ _logger = logger;
+ _registeredDataBindingModifierTypes = new List();
+ }
+
+ public IReadOnlyCollection 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 GetCompatibleModifierTypes(Type type)
+ {
+ lock (_registeredDataBindingModifierTypes)
+ {
+ if (type == null)
+ return new List(_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
+ );
+ }
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/Interfaces/IDataBindingService.cs b/src/Artemis.Core/Services/Interfaces/IDataBindingService.cs
index b8caaf2ed..1ac5018b9 100644
--- a/src/Artemis.Core/Services/Interfaces/IDataBindingService.cs
+++ b/src/Artemis.Core/Services/Interfaces/IDataBindingService.cs
@@ -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
{
+ ///
+ /// Gets a read-only collection of all registered modifier types
+ ///
+ IReadOnlyCollection RegisteredDataBindingModifierTypes { get; }
+
+ ///
+ /// Registers a new modifier type for use in data bindings
+ ///
+ /// The PluginInfo of the plugin this modifier type belongs to
+ /// The modifier type to register
+ void RegisterModifierType([NotNull] PluginInfo pluginInfo, [NotNull] DataBindingModifierType dataBindingModifierType);
+
+ ///
+ /// Removes a modifier type so it is no longer available for use in data bindings
+ ///
+ /// The modifier type to remove
+ void RemoveModifierType([NotNull] DataBindingModifierType dataBindingModifierType);
+
+ ///
+ /// Returns all the data binding modifier types compatible with the provided type
+ ///
+ List GetCompatibleModifierTypes(Type type);
+
+ ///
+ /// Gets a modifier type by its plugin GUID and type name
+ ///
+ /// The modifier type's plugin GUID
+ /// The type name of the modifier type
+ ///
+ DataBindingModifierType GetModifierType(Guid modifierTypePluginGuid, string modifierType);
+
+ ///
+ /// Logs a modifier deserialization failure
+ ///
+ /// The modifier that failed to deserialize
+ /// The JSON exception that occurred
+ void LogModifierDeserializationFailure(DataBindingModifier dataBindingModifier, JsonSerializationException exception);
}
}
\ No newline at end of file
diff --git a/src/Artemis.Core/Services/Interfaces/IDataModelService.cs b/src/Artemis.Core/Services/Interfaces/IDataModelService.cs
index f1586204d..512fc8710 100644
--- a/src/Artemis.Core/Services/Interfaces/IDataModelService.cs
+++ b/src/Artemis.Core/Services/Interfaces/IDataModelService.cs
@@ -10,7 +10,14 @@ namespace Artemis.Core.Services.Interfaces
{
public interface IDataModelService : IArtemisService
{
+ ///
+ /// Gets a read-only collection of all registered condition operators
+ ///
IReadOnlyCollection RegisteredConditionOperators { get; }
+
+ ///
+ /// Gets a read-only collection of all registered data model expansions
+ ///
IReadOnlyCollection DataModelExpansions { get; }
///
@@ -57,9 +64,30 @@ namespace Artemis.Core.Services.Interfaces
/// The layer condition operator to remove
void RemoveConditionOperator([NotNull] DisplayConditionOperator displayConditionOperator);
+ ///
+ /// Returns all the display condition operators compatible with the provided type
+ ///
List GetCompatibleConditionOperators(Type type);
+
+ ///
+ /// Gets a condition operator by its plugin GUID and type name
+ ///
+ /// The operator's plugin GUID
+ /// The type name of the operator
DisplayConditionOperator GetConditionOperator(Guid operatorPluginGuid, string operatorType);
+
+ ///
+ /// Logs a predicate deserialization failure
+ ///
+ /// The predicate that failed to deserialize
+ /// The JSON exception that occurred
void LogPredicateDeserializationFailure(DisplayConditionPredicate displayConditionPredicate, JsonException exception);
+
+ ///
+ /// Logs a list predicate deserialization failure
+ ///
+ /// The list predicate that failed to deserialize
+ /// The JSON exception that occurred
void LogListPredicateDeserializationFailure(DisplayConditionListPredicate displayConditionListPredicate, JsonException exception);
}
}
\ No newline at end of file
diff --git a/src/Artemis.Storage/Entities/Profile/Abstract/RenderElementEntity.cs b/src/Artemis.Storage/Entities/Profile/Abstract/RenderElementEntity.cs
index f76182c77..41af59cd9 100644
--- a/src/Artemis.Storage/Entities/Profile/Abstract/RenderElementEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/Abstract/RenderElementEntity.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using Artemis.Storage.Entities.Profile.Conditions;
namespace Artemis.Storage.Entities.Profile.Abstract
{
diff --git a/src/Artemis.Storage/Entities/Profile/DisplayConditionGroupEntity.cs b/src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionGroupEntity.cs
similarity index 73%
rename from src/Artemis.Storage/Entities/Profile/DisplayConditionGroupEntity.cs
rename to src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionGroupEntity.cs
index b99d01170..a862a79e4 100644
--- a/src/Artemis.Storage/Entities/Profile/DisplayConditionGroupEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionGroupEntity.cs
@@ -1,9 +1,7 @@
-using System;
-using System.Collections.Generic;
-using System.Text;
+using System.Collections.Generic;
using Artemis.Storage.Entities.Profile.Abstract;
-namespace Artemis.Storage.Entities.Profile
+namespace Artemis.Storage.Entities.Profile.Conditions
{
public class DisplayConditionGroupEntity : DisplayConditionPartEntity
{
diff --git a/src/Artemis.Storage/Entities/Profile/DisplayConditionListEntity.cs b/src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionListEntity.cs
similarity index 89%
rename from src/Artemis.Storage/Entities/Profile/DisplayConditionListEntity.cs
rename to src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionListEntity.cs
index 34907ebc7..925c9a055 100644
--- a/src/Artemis.Storage/Entities/Profile/DisplayConditionListEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionListEntity.cs
@@ -2,7 +2,7 @@
using System.Collections.Generic;
using Artemis.Storage.Entities.Profile.Abstract;
-namespace Artemis.Storage.Entities.Profile
+namespace Artemis.Storage.Entities.Profile.Conditions
{
public class DisplayConditionListEntity : DisplayConditionPartEntity
{
diff --git a/src/Artemis.Storage/Entities/Profile/DisplayConditionListPredicateEntity.cs b/src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionListPredicateEntity.cs
similarity index 92%
rename from src/Artemis.Storage/Entities/Profile/DisplayConditionListPredicateEntity.cs
rename to src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionListPredicateEntity.cs
index 289a7db5e..8a2521389 100644
--- a/src/Artemis.Storage/Entities/Profile/DisplayConditionListPredicateEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionListPredicateEntity.cs
@@ -1,7 +1,7 @@
using System;
using Artemis.Storage.Entities.Profile.Abstract;
-namespace Artemis.Storage.Entities.Profile
+namespace Artemis.Storage.Entities.Profile.Conditions
{
public class DisplayConditionListPredicateEntity : DisplayConditionPartEntity
{
diff --git a/src/Artemis.Storage/Entities/Profile/DisplayConditionPredicateEntity.cs b/src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionPredicateEntity.cs
similarity index 92%
rename from src/Artemis.Storage/Entities/Profile/DisplayConditionPredicateEntity.cs
rename to src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionPredicateEntity.cs
index 2e29e01ad..f31c160f9 100644
--- a/src/Artemis.Storage/Entities/Profile/DisplayConditionPredicateEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/Conditions/DisplayConditionPredicateEntity.cs
@@ -1,7 +1,7 @@
using System;
using Artemis.Storage.Entities.Profile.Abstract;
-namespace Artemis.Storage.Entities.Profile
+namespace Artemis.Storage.Entities.Profile.Conditions
{
public class DisplayConditionPredicateEntity : DisplayConditionPartEntity
{
diff --git a/src/Artemis.Storage/Entities/Profile/ProfileConditionEntity.cs b/src/Artemis.Storage/Entities/Profile/Conditions/ProfileConditionEntity.cs
similarity index 67%
rename from src/Artemis.Storage/Entities/Profile/ProfileConditionEntity.cs
rename to src/Artemis.Storage/Entities/Profile/Conditions/ProfileConditionEntity.cs
index 99ca1dbe3..686300b58 100644
--- a/src/Artemis.Storage/Entities/Profile/ProfileConditionEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/Conditions/ProfileConditionEntity.cs
@@ -1,6 +1,6 @@
using System;
-namespace Artemis.Storage.Entities.Profile
+namespace Artemis.Storage.Entities.Profile.Conditions
{
public class ProfileConditionEntity
{
diff --git a/src/Artemis.Storage/Entities/Profile/DataBindings/DataBindingEntity.cs b/src/Artemis.Storage/Entities/Profile/DataBindings/DataBindingEntity.cs
new file mode 100644
index 000000000..da9d85c7c
--- /dev/null
+++ b/src/Artemis.Storage/Entities/Profile/DataBindings/DataBindingEntity.cs
@@ -0,0 +1,21 @@
+using System;
+using System.Collections.Generic;
+
+namespace Artemis.Storage.Entities.Profile.DataBindings
+{
+ public class DataBindingEntity
+ {
+ public DataBindingEntity()
+ {
+ Modifiers = new List();
+ }
+
+ 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 Modifiers { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Storage/Entities/Profile/DataBindings/DataBindingModifierEntity.cs b/src/Artemis.Storage/Entities/Profile/DataBindings/DataBindingModifierEntity.cs
new file mode 100644
index 000000000..cef799f14
--- /dev/null
+++ b/src/Artemis.Storage/Entities/Profile/DataBindings/DataBindingModifierEntity.cs
@@ -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; }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Storage/Entities/Profile/KeyframeEntity.cs b/src/Artemis.Storage/Entities/Profile/KeyframeEntity.cs
new file mode 100644
index 000000000..32762d4c5
--- /dev/null
+++ b/src/Artemis.Storage/Entities/Profile/KeyframeEntity.cs
@@ -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; }
+ }
+}
\ No newline at end of file
diff --git a/src/Artemis.Storage/Entities/Profile/PropertyEntity.cs b/src/Artemis.Storage/Entities/Profile/PropertyEntity.cs
index 610bf0910..d2c82aef7 100644
--- a/src/Artemis.Storage/Entities/Profile/PropertyEntity.cs
+++ b/src/Artemis.Storage/Entities/Profile/PropertyEntity.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using Artemis.Storage.Entities.Profile.DataBindings;
namespace Artemis.Storage.Entities.Profile
{
@@ -17,13 +18,6 @@ namespace Artemis.Storage.Entities.Profile
public bool KeyframesEnabled { get; set; }
public List KeyframeEntities { 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; }
+ public DataBindingEntity DataBindingEntity { get; set; }
}
}
\ No newline at end of file
diff --git a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionGroupViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionGroupViewModel.cs
index 279149129..92835d7bd 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionGroupViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionGroupViewModel.cs
@@ -2,6 +2,7 @@
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
+using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Profile.Conditions;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
@@ -74,16 +75,16 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
if (type == "Static")
{
if (!IsListGroup)
- DisplayConditionGroup.AddChild(new DisplayConditionPredicate(DisplayConditionGroup, PredicateType.Static));
+ DisplayConditionGroup.AddChild(new DisplayConditionPredicate(DisplayConditionGroup, ProfileRightSideType.Static));
else
- DisplayConditionGroup.AddChild(new DisplayConditionListPredicate(DisplayConditionGroup, PredicateType.Static));
+ DisplayConditionGroup.AddChild(new DisplayConditionListPredicate(DisplayConditionGroup, ProfileRightSideType.Static));
}
else if (type == "Dynamic")
{
if (!IsListGroup)
- DisplayConditionGroup.AddChild(new DisplayConditionPredicate(DisplayConditionGroup, PredicateType.Dynamic));
+ DisplayConditionGroup.AddChild(new DisplayConditionPredicate(DisplayConditionGroup, ProfileRightSideType.Dynamic));
else
- DisplayConditionGroup.AddChild(new DisplayConditionListPredicate(DisplayConditionGroup, PredicateType.Dynamic));
+ DisplayConditionGroup.AddChild(new DisplayConditionListPredicate(DisplayConditionGroup, ProfileRightSideType.Dynamic));
}
else if (type == "List" && !IsListGroup)
DisplayConditionGroup.AddChild(new DisplayConditionList(DisplayConditionGroup));
diff --git a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListPredicateViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListPredicateViewModel.cs
index c8eee0de2..f4cc5c236 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListPredicateViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListPredicateViewModel.cs
@@ -5,6 +5,7 @@ using System.Threading.Tasks;
using System.Timers;
using System.Windows;
using System.Windows.Input;
+using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Profile.Conditions;
using Artemis.Core.Plugins.Settings;
using Artemis.Core.Services;
@@ -14,7 +15,6 @@ using Artemis.UI.Exceptions;
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
using Artemis.UI.Shared.DataModelVisualization;
using Artemis.UI.Shared.DataModelVisualization.Shared;
-using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Interfaces;
using Artemis.UI.Utilities;
using Stylet;
@@ -69,7 +69,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
}
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 PluginSetting ShowDataModelValues { get; }
@@ -201,7 +201,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
var listDataModelGuid = DisplayConditionListPredicate.ListDataModel.PluginInfo.Guid;
// 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());
// Determine the left side property first
@@ -216,7 +216,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
SelectedOperator = DisplayConditionListPredicate.Operator;
// Determine the right side
- if (DisplayConditionListPredicate.PredicateType == PredicateType.Dynamic)
+ if (DisplayConditionListPredicate.PredicateType == ProfileRightSideType.Dynamic)
{
SelectedRightSideProperty = RightSideDataModel.GetChildByPath(listDataModelGuid, DisplayConditionListPredicate.RightPropertyPath);
RightSideDataModel.ApplyTypeFilter(true, leftSideType);
@@ -310,7 +310,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
private void LeftDataModelUpdateRequested(object sender, EventArgs e)
{
- if (DisplayConditionListPredicate.PredicateType == PredicateType.Static)
+ if (DisplayConditionListPredicate.PredicateType == ProfileRightSideType.Static)
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
}
diff --git a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListViewModel.cs
index 7127bf517..1ce2ca740 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionListViewModel.cs
@@ -3,13 +3,13 @@ using System.Collections;
using System.Linq;
using System.Threading.Tasks;
using System.Timers;
+using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Profile.Conditions;
using Artemis.Core.Plugins.Settings;
using Artemis.Core.Services;
using Artemis.UI.Ninject.Factories;
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
using Artemis.UI.Shared.DataModelVisualization.Shared;
-using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Interfaces;
using Artemis.UI.Utilities;
using Humanizer;
@@ -18,13 +18,13 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
{
public class DisplayConditionListViewModel : DisplayConditionViewModel
{
- private readonly IProfileEditorService _profileEditorService;
private readonly IDataModelUIService _dataModelUIService;
private readonly IDisplayConditionsVmFactory _displayConditionsVmFactory;
+ private readonly IProfileEditorService _profileEditorService;
+ private readonly Timer _updateTimer;
private bool _isInitialized;
private DataModelListViewModel _selectedListProperty;
private DataModelPropertiesViewModel _targetDataModel;
- private readonly Timer _updateTimer;
public DisplayConditionListViewModel(
DisplayConditionList displayConditionList,
@@ -73,7 +73,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
}
public string SelectedListOperator => DisplayConditionList.ListOperator.Humanize();
-
+
public void SelectListOperator(string type)
{
var enumValue = Enum.Parse(type);
@@ -86,9 +86,9 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
public void AddCondition(string type)
{
if (type == "Static")
- DisplayConditionList.AddChild(new DisplayConditionPredicate(DisplayConditionList, PredicateType.Static));
+ DisplayConditionList.AddChild(new DisplayConditionPredicate(DisplayConditionList, ProfileRightSideType.Static));
else if (type == "Dynamic")
- DisplayConditionList.AddChild(new DisplayConditionPredicate(DisplayConditionList, PredicateType.Dynamic));
+ DisplayConditionList.AddChild(new DisplayConditionPredicate(DisplayConditionList, ProfileRightSideType.Dynamic));
Update();
_profileEditorService.UpdateSelectedProfileElement();
@@ -125,26 +125,6 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
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()
{
DisplayConditionList.UpdateList(SelectedListProperty.DataModel, SelectedListProperty.PropertyPath);
@@ -198,6 +178,26 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
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)
{
diff --git a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionPredicateViewModel.cs b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionPredicateViewModel.cs
index f132c696a..5e0779be3 100644
--- a/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionPredicateViewModel.cs
+++ b/src/Artemis.UI/Screens/ProfileEditor/DisplayConditions/DisplayConditionPredicateViewModel.cs
@@ -5,6 +5,7 @@ using System.Threading.Tasks;
using System.Timers;
using System.Windows;
using System.Windows.Input;
+using Artemis.Core.Models.Profile;
using Artemis.Core.Models.Profile.Conditions;
using Artemis.Core.Plugins.Settings;
using Artemis.Core.Services;
@@ -13,7 +14,6 @@ using Artemis.UI.Events;
using Artemis.UI.Screens.ProfileEditor.DisplayConditions.Abstract;
using Artemis.UI.Shared.DataModelVisualization;
using Artemis.UI.Shared.DataModelVisualization.Shared;
-using Artemis.UI.Shared.Services;
using Artemis.UI.Shared.Services.Interfaces;
using Artemis.UI.Utilities;
using Stylet;
@@ -26,6 +26,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
private readonly IDataModelUIService _dataModelUIService;
private readonly IEventAggregator _eventAggregator;
private readonly IProfileEditorService _profileEditorService;
+ private readonly Timer _updateTimer;
private bool _isInitialized;
private DataModelPropertiesViewModel _leftSideDataModel;
private BindableCollection _operators;
@@ -38,7 +39,6 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
private DataModelVisualizationViewModel _selectedRightSideProperty;
private List _supportedInputTypes;
- private readonly Timer _updateTimer;
public DisplayConditionPredicateViewModel(
DisplayConditionPredicate displayConditionPredicate,
@@ -68,7 +68,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
}
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 PluginSetting ShowDataModelValues { get; }
@@ -199,11 +199,11 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
public override void Update()
{
- if (LeftSideDataModel == null || DisplayConditionPredicate.PredicateType == PredicateType.Dynamic && RightSideDataModel == null)
+ if (LeftSideDataModel == null || DisplayConditionPredicate.PredicateType == ProfileRightSideType.Dynamic && RightSideDataModel == null)
return;
// 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());
// Determine the left side property first
@@ -218,7 +218,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
SelectedOperator = DisplayConditionPredicate.Operator;
// Determine the right side
- if (DisplayConditionPredicate.PredicateType == PredicateType.Dynamic)
+ if (DisplayConditionPredicate.PredicateType == ProfileRightSideType.Dynamic)
{
SelectedRightSideProperty = LeftSideDataModel.GetChildForCondition(DisplayConditionPredicate, DisplayConditionSide.Right);
RightSideDataModel.ApplyTypeFilter(true, leftSideType);
@@ -300,7 +300,7 @@ namespace Artemis.UI.Screens.ProfileEditor.DisplayConditions
private void RightDataModelUpdateRequested(object sender, EventArgs e)
{
var leftSideType = SelectedLeftSideProperty?.PropertyInfo?.PropertyType;
- if (DisplayConditionPredicate.PredicateType == PredicateType.Dynamic)
+ if (DisplayConditionPredicate.PredicateType == ProfileRightSideType.Dynamic)
SelectedRightSideProperty = LeftSideDataModel.GetChildForCondition(DisplayConditionPredicate, DisplayConditionSide.Right);
// 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)
{
- if (DisplayConditionPredicate.PredicateType == PredicateType.Static)
+ if (DisplayConditionPredicate.PredicateType == ProfileRightSideType.Static)
LeftSideDataModel.ApplyTypeFilter(false, _supportedInputTypes.ToArray());
}