mirror of
https://github.com/Artemis-RGB/Artemis
synced 2025-12-13 05:48:35 +00:00
Merge pull request #488 from Artemis-RGB/generic-conditions
Condition operators - Redesigned API to leverage generics
This commit is contained in:
commit
2bb663db15
@ -1,12 +1,7 @@
|
|||||||
using System;
|
namespace Artemis.Core.DefaultTypes
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
|
||||||
{
|
{
|
||||||
internal class EqualsConditionOperator : ConditionOperator
|
internal class EqualsConditionOperator : ConditionOperator<object, object>
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(object)};
|
|
||||||
|
|
||||||
public override string Description => "Equals";
|
public override string Description => "Equals";
|
||||||
public override string Icon => "Equal";
|
public override string Icon => "Equal";
|
||||||
|
|
||||||
|
|||||||
@ -1,18 +1,13 @@
|
|||||||
using System;
|
namespace Artemis.Core.DefaultTypes
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
|
||||||
{
|
{
|
||||||
internal class GreaterThanConditionOperator : ConditionOperator
|
internal class GreaterThanConditionOperator : ConditionOperator<double, double>
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
|
||||||
|
|
||||||
public override string Description => "Is greater than";
|
public override string Description => "Is greater than";
|
||||||
public override string Icon => "GreaterThan";
|
public override string Icon => "GreaterThan";
|
||||||
|
|
||||||
public override bool Evaluate(object a, object b)
|
public override bool Evaluate(double a, double b)
|
||||||
{
|
{
|
||||||
return Convert.ToSingle(a) > Convert.ToSingle(b);
|
return a > b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,18 +1,13 @@
|
|||||||
using System;
|
namespace Artemis.Core.DefaultTypes
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
|
||||||
{
|
{
|
||||||
internal class GreaterThanOrEqualConditionOperator : ConditionOperator
|
internal class GreaterThanOrEqualConditionOperator : ConditionOperator<double, double>
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
|
||||||
|
|
||||||
public override string Description => "Is greater than or equal to";
|
public override string Description => "Is greater than or equal to";
|
||||||
public override string Icon => "GreaterThanOrEqual";
|
public override string Icon => "GreaterThanOrEqual";
|
||||||
|
|
||||||
public override bool Evaluate(object a, object b)
|
public override bool Evaluate(double a, double b)
|
||||||
{
|
{
|
||||||
return Convert.ToSingle(a) >= Convert.ToSingle(b);
|
return a >= b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,18 +1,13 @@
|
|||||||
using System;
|
namespace Artemis.Core.DefaultTypes
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
|
||||||
{
|
{
|
||||||
internal class LessThanConditionOperator : ConditionOperator
|
internal class LessThanConditionOperator : ConditionOperator<double, double>
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
|
||||||
|
|
||||||
public override string Description => "Is less than";
|
public override string Description => "Is less than";
|
||||||
public override string Icon => "LessThan";
|
public override string Icon => "LessThan";
|
||||||
|
|
||||||
public override bool Evaluate(object a, object b)
|
public override bool Evaluate(double a, double b)
|
||||||
{
|
{
|
||||||
return Convert.ToSingle(a) < Convert.ToSingle(b);
|
return a < b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,18 +1,13 @@
|
|||||||
using System;
|
namespace Artemis.Core.DefaultTypes
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
|
||||||
{
|
{
|
||||||
internal class LessThanOrEqualConditionOperator : ConditionOperator
|
internal class LessThanOrEqualConditionOperator : ConditionOperator<double, double>
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => Constants.NumberTypes;
|
|
||||||
|
|
||||||
public override string Description => "Is less than or equal to";
|
public override string Description => "Is less than or equal to";
|
||||||
public override string Icon => "LessThanOrEqual";
|
public override string Icon => "LessThanOrEqual";
|
||||||
|
|
||||||
public override bool Evaluate(object a, object b)
|
public override bool Evaluate(double a, double b)
|
||||||
{
|
{
|
||||||
return Convert.ToSingle(a) <= Convert.ToSingle(b);
|
return a <= b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,12 +1,7 @@
|
|||||||
using System;
|
namespace Artemis.Core.DefaultTypes
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
|
||||||
{
|
{
|
||||||
internal class NotEqualConditionOperator : ConditionOperator
|
internal class NotEqualConditionOperator : ConditionOperator<object, object>
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(object)};
|
|
||||||
|
|
||||||
public override string Description => "Does not equal";
|
public override string Description => "Does not equal";
|
||||||
public override string Icon => "NotEqualVariant";
|
public override string Icon => "NotEqualVariant";
|
||||||
|
|
||||||
|
|||||||
@ -1,21 +1,11 @@
|
|||||||
using System;
|
namespace Artemis.Core.DefaultTypes
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
|
||||||
{
|
{
|
||||||
internal class NotNullConditionOperator : ConditionOperator
|
internal class NotNullConditionOperator : ConditionOperator<object>
|
||||||
{
|
{
|
||||||
public NotNullConditionOperator()
|
|
||||||
{
|
|
||||||
SupportsRightSide = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(object)};
|
|
||||||
|
|
||||||
public override string Description => "Is not null";
|
public override string Description => "Is not null";
|
||||||
public override string Icon => "CheckboxMarkedCircleOutline";
|
public override string Icon => "CheckboxMarkedCircleOutline";
|
||||||
|
|
||||||
public override bool Evaluate(object a, object b)
|
public override bool Evaluate(object a)
|
||||||
{
|
{
|
||||||
return a != null;
|
return a != null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,21 +1,11 @@
|
|||||||
using System;
|
namespace Artemis.Core.DefaultTypes
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
|
||||||
{
|
{
|
||||||
internal class NullConditionOperator : ConditionOperator
|
internal class NullConditionOperator : ConditionOperator<object>
|
||||||
{
|
{
|
||||||
public NullConditionOperator()
|
|
||||||
{
|
|
||||||
SupportsRightSide = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(object)};
|
|
||||||
|
|
||||||
public override string Description => "Is null";
|
public override string Description => "Is null";
|
||||||
public override string Icon => "Null";
|
public override string Icon => "Null";
|
||||||
|
|
||||||
public override bool Evaluate(object a, object b)
|
public override bool Evaluate(object a)
|
||||||
{
|
{
|
||||||
return a == null;
|
return a == null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Artemis.Core.DefaultTypes
|
||||||
|
{
|
||||||
|
internal class NumberEqualsConditionOperator : ConditionOperator<double, double>
|
||||||
|
{
|
||||||
|
public override string Description => "Equals";
|
||||||
|
public override string Icon => "Equal";
|
||||||
|
|
||||||
|
public override bool Evaluate(double a, double b)
|
||||||
|
{
|
||||||
|
// Numbers can be tricky, an epsilon like this is close enough
|
||||||
|
return Math.Abs(a - b) < 0.000001;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Artemis.Core.DefaultTypes
|
||||||
|
{
|
||||||
|
internal class NumberNotEqualConditionOperator : ConditionOperator<double, double>
|
||||||
|
{
|
||||||
|
public override string Description => "Does not equal";
|
||||||
|
public override string Icon => "NotEqualVariant";
|
||||||
|
|
||||||
|
public override bool Evaluate(double a, double b)
|
||||||
|
{
|
||||||
|
// Numbers can be tricky, an epsilon like this is close enough
|
||||||
|
return Math.Abs(a - b) > 0.000001;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,21 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
namespace Artemis.Core.DefaultTypes
|
||||||
{
|
{
|
||||||
internal class StringContainsConditionOperator : ConditionOperator
|
internal class StringContainsConditionOperator : ConditionOperator<string, string>
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(string)};
|
|
||||||
|
|
||||||
public override string Description => "Contains";
|
public override string Description => "Contains";
|
||||||
public override string Icon => "Contain";
|
public override string Icon => "Contain";
|
||||||
|
|
||||||
public override bool Evaluate(object a, object b)
|
public override bool Evaluate(string a, string b)
|
||||||
{
|
{
|
||||||
string aString = (string) a;
|
return a != null && b != null && a.Contains(b, StringComparison.InvariantCultureIgnoreCase);
|
||||||
string bString = (string) b;
|
|
||||||
|
|
||||||
return bString != null && aString != null && aString.Contains(bString, StringComparison.InvariantCultureIgnoreCase);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,21 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
namespace Artemis.Core.DefaultTypes
|
||||||
{
|
{
|
||||||
internal class StringEndsWithConditionOperator : ConditionOperator
|
internal class StringEndsWithConditionOperator : ConditionOperator<string, string>
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(string)};
|
|
||||||
|
|
||||||
public override string Description => "Ends with";
|
public override string Description => "Ends with";
|
||||||
public override string Icon => "ContainEnd";
|
public override string Icon => "ContainEnd";
|
||||||
|
|
||||||
public override bool Evaluate(object a, object b)
|
public override bool Evaluate(string a, string b)
|
||||||
{
|
{
|
||||||
string aString = (string) a;
|
return a != null && b != null && a.EndsWith(b, StringComparison.InvariantCultureIgnoreCase);
|
||||||
string bString = (string) b;
|
|
||||||
|
|
||||||
return bString != null && aString != null && aString.EndsWith(bString, StringComparison.InvariantCultureIgnoreCase);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,21 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
namespace Artemis.Core.DefaultTypes
|
||||||
{
|
{
|
||||||
internal class StringEqualsConditionOperator : ConditionOperator
|
internal class StringEqualsConditionOperator : ConditionOperator<string, string>
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(string)};
|
|
||||||
|
|
||||||
public override string Description => "Equals";
|
public override string Description => "Equals";
|
||||||
public override string Icon => "Equal";
|
public override string Icon => "Equal";
|
||||||
|
|
||||||
public override bool Evaluate(object a, object b)
|
public override bool Evaluate(string a, string b)
|
||||||
{
|
{
|
||||||
string aString = (string) a;
|
return string.Equals(a, b, StringComparison.InvariantCultureIgnoreCase);
|
||||||
string bString = (string) b;
|
|
||||||
|
|
||||||
return string.Equals(aString, bString, StringComparison.InvariantCultureIgnoreCase);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,21 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
namespace Artemis.Core.DefaultTypes
|
||||||
{
|
{
|
||||||
internal class StringNotContainsConditionOperator : ConditionOperator
|
internal class StringNotContainsConditionOperator : ConditionOperator<string, string>
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(string)};
|
|
||||||
|
|
||||||
public override string Description => "Does not contain";
|
public override string Description => "Does not contain";
|
||||||
public override string Icon => "FormatStrikethrough";
|
public override string Icon => "FormatStrikethrough";
|
||||||
|
|
||||||
public override bool Evaluate(object a, object b)
|
public override bool Evaluate(string a, string b)
|
||||||
{
|
{
|
||||||
string aString = (string) a;
|
return a != null && b != null && !a.Contains(b, StringComparison.InvariantCultureIgnoreCase);
|
||||||
string bString = (string) b;
|
|
||||||
|
|
||||||
return bString != null && aString != null && !aString.Contains(bString, StringComparison.InvariantCultureIgnoreCase);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,21 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
namespace Artemis.Core.DefaultTypes
|
||||||
{
|
{
|
||||||
internal class StringNotEqualConditionOperator : ConditionOperator
|
internal class StringNotEqualConditionOperator : ConditionOperator<string, string>
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(string)};
|
|
||||||
|
|
||||||
public override string Description => "Does not equal";
|
public override string Description => "Does not equal";
|
||||||
public override string Icon => "NotEqualVariant";
|
public override string Icon => "NotEqualVariant";
|
||||||
|
|
||||||
public override bool Evaluate(object a, object b)
|
public override bool Evaluate(string a, string b)
|
||||||
{
|
{
|
||||||
string aString = (string) a;
|
return !string.Equals(a, b, StringComparison.InvariantCultureIgnoreCase);
|
||||||
string bString = (string) b;
|
|
||||||
|
|
||||||
return !string.Equals(aString, bString, StringComparison.InvariantCultureIgnoreCase);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1,21 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
|
||||||
|
|
||||||
namespace Artemis.Core.DefaultTypes
|
namespace Artemis.Core.DefaultTypes
|
||||||
{
|
{
|
||||||
internal class StringStartsWithConditionOperator : ConditionOperator
|
internal class StringStartsWithConditionOperator : ConditionOperator<string, string>
|
||||||
{
|
{
|
||||||
public override IReadOnlyCollection<Type> CompatibleTypes => new List<Type> {typeof(string)};
|
|
||||||
|
|
||||||
public override string Description => "Starts with";
|
public override string Description => "Starts with";
|
||||||
public override string Icon => "ContainStart";
|
public override string Icon => "ContainStart";
|
||||||
|
|
||||||
public override bool Evaluate(object a, object b)
|
public override bool Evaluate(string a, string b)
|
||||||
{
|
{
|
||||||
string aString = (string) a;
|
return a != null && b != null && a.StartsWith(b, StringComparison.InvariantCultureIgnoreCase);
|
||||||
string bString = (string) b;
|
|
||||||
|
|
||||||
return bString != null && aString != null && aString.StartsWith(bString, StringComparison.InvariantCultureIgnoreCase);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -21,6 +21,25 @@ namespace Artemis.Core
|
|||||||
{typeof(short), new List<Type> {typeof(byte)}}
|
{typeof(short), new List<Type> {typeof(byte)}}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static readonly Dictionary<Type, string> TypeKeywords = new Dictionary<Type, string>
|
||||||
|
{
|
||||||
|
{typeof(bool), "bool"},
|
||||||
|
{typeof(byte), "byte"},
|
||||||
|
{typeof(sbyte), "sbyte"},
|
||||||
|
{typeof(char), "char"},
|
||||||
|
{typeof(decimal), "decimal"},
|
||||||
|
{typeof(double), "double"},
|
||||||
|
{typeof(float), "float"},
|
||||||
|
{typeof(int), "int"},
|
||||||
|
{typeof(uint), "uint"},
|
||||||
|
{typeof(long), "long"},
|
||||||
|
{typeof(ulong), "ulong"},
|
||||||
|
{typeof(short), "short"},
|
||||||
|
{typeof(ushort), "ushort"},
|
||||||
|
{typeof(object), "object"},
|
||||||
|
{typeof(string), "string"}
|
||||||
|
};
|
||||||
|
|
||||||
public static bool IsGenericType(this Type type, Type genericType)
|
public static bool IsGenericType(this Type type, Type genericType)
|
||||||
{
|
{
|
||||||
if (type == null)
|
if (type == null)
|
||||||
@ -64,31 +83,10 @@ namespace Artemis.Core
|
|||||||
|| value is decimal;
|
|| value is decimal;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static readonly Dictionary<Type, string> TypeKeywords = new Dictionary<Type, string>()
|
|
||||||
{
|
|
||||||
{typeof(bool), "bool"},
|
|
||||||
{typeof(byte), "byte"},
|
|
||||||
{typeof(sbyte), "sbyte"},
|
|
||||||
{typeof(char), "char"},
|
|
||||||
{typeof(decimal), "decimal"},
|
|
||||||
{typeof(double), "double"},
|
|
||||||
{typeof(float), "float"},
|
|
||||||
{typeof(int), "int"},
|
|
||||||
{typeof(uint), "uint"},
|
|
||||||
{typeof(long), "long"},
|
|
||||||
{typeof(ulong), "ulong"},
|
|
||||||
{typeof(short), "short"},
|
|
||||||
{typeof(ushort), "ushort"},
|
|
||||||
{typeof(object), "object"},
|
|
||||||
{typeof(string), "string"},
|
|
||||||
};
|
|
||||||
|
|
||||||
// From https://stackoverflow.com/a/2224421/5015269 but inverted and renamed to match similar framework methods
|
// From https://stackoverflow.com/a/2224421/5015269 but inverted and renamed to match similar framework methods
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines whether an instance of a specified type can be casted to a variable of the current type
|
/// Determines whether an instance of a specified type can be casted to a variable of the current type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="to"></param>
|
|
||||||
/// <param name="from"></param>
|
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
public static bool IsCastableFrom(this Type to, Type from)
|
public static bool IsCastableFrom(this Type to, Type from)
|
||||||
{
|
{
|
||||||
@ -99,14 +97,32 @@ namespace Artemis.Core
|
|||||||
if (PrimitiveTypeConversions.ContainsKey(to) && PrimitiveTypeConversions[to].Contains(from))
|
if (PrimitiveTypeConversions.ContainsKey(to) && PrimitiveTypeConversions[to].Contains(from))
|
||||||
return true;
|
return true;
|
||||||
bool castable = from.GetMethods(BindingFlags.Public | BindingFlags.Static)
|
bool castable = from.GetMethods(BindingFlags.Public | BindingFlags.Static)
|
||||||
.Any(
|
.Any(m => m.ReturnType == to && (m.Name == "op_Implicit" || m.Name == "op_Explicit"));
|
||||||
m => m.ReturnType == to &&
|
|
||||||
(m.Name == "op_Implicit" ||
|
|
||||||
m.Name == "op_Explicit")
|
|
||||||
);
|
|
||||||
return castable;
|
return castable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Scores how well the two types can be casted from one to another, 5 being a perfect match and 0 being not castable
|
||||||
|
/// at all
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public static int ScoreCastability(this Type to, Type from)
|
||||||
|
{
|
||||||
|
if (to == from)
|
||||||
|
return 5;
|
||||||
|
if (to.TypeIsNumber() && from.TypeIsNumber())
|
||||||
|
return 4;
|
||||||
|
if (PrimitiveTypeConversions.ContainsKey(to) && PrimitiveTypeConversions[to].Contains(from))
|
||||||
|
return 3;
|
||||||
|
bool castable = from.GetMethods(BindingFlags.Public | BindingFlags.Static)
|
||||||
|
.Any(m => m.ReturnType == to && (m.Name == "op_Implicit" || m.Name == "op_Explicit"));
|
||||||
|
if (castable)
|
||||||
|
return 2;
|
||||||
|
if (to.IsAssignableFrom(from))
|
||||||
|
return 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the default value of the given type
|
/// Returns the default value of the given type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -149,7 +165,7 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Determines a display name for the given type
|
/// Determines a display name for the given type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="type">The type to determine the name for</param>
|
/// <param name="type">The type to determine the name for</param>
|
||||||
/// <param name="humanize">Whether or not to humanize the result, defaults to false</param>
|
/// <param name="humanize">Whether or not to humanize the result, defaults to false</param>
|
||||||
|
|||||||
@ -0,0 +1,65 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a condition operator that performs a boolean operation
|
||||||
|
/// <para>
|
||||||
|
/// To implement your own condition operator, inherit <see cref="ConditionOperator{TLeftSide, TRightSide}" /> or
|
||||||
|
/// <see cref="ConditionOperator{TLeftSide}" />
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
public abstract class BaseConditionOperator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the description of this logical operator
|
||||||
|
/// </summary>
|
||||||
|
public abstract string Description { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets or sets the icon of this logical operator
|
||||||
|
/// </summary>
|
||||||
|
public abstract string Icon { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the plugin info this condition operator belongs to
|
||||||
|
/// <para>Note: Not set until after registering</para>
|
||||||
|
/// </summary>
|
||||||
|
public PluginInfo PluginInfo { get; internal set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the left side type of this condition operator
|
||||||
|
/// </summary>
|
||||||
|
public abstract Type LeftSideType { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the right side type of this condition operator. May be null if the operator does not support a left side type
|
||||||
|
/// </summary>
|
||||||
|
public abstract Type? RightSideType { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether the given type is supported by the operator
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">The type to check for, must be either the same or be castable to the target type</param>
|
||||||
|
/// <param name="side">Which side of the operator to check, left or right</param>
|
||||||
|
public abstract bool SupportsType(Type type, ConditionParameterSide side);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Evaluates the condition with the input types being provided as objects
|
||||||
|
/// <para>
|
||||||
|
/// This leaves the caller responsible for the types matching <see cref="LeftSideType" /> and
|
||||||
|
/// <see cref="RightSideType" />
|
||||||
|
/// </para>
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="leftSideValue">The left side value, type should match <see cref="LeftSideType" /></param>
|
||||||
|
/// <param name="rightSideValue">The right side value, type should match <see cref="RightSideType" /></param>
|
||||||
|
/// <returns>The result of the boolean condition's evaluation</returns>
|
||||||
|
internal abstract bool InternalEvaluate(object? leftSideValue, object? rightSideValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ConditionParameterSide
|
||||||
|
{
|
||||||
|
Left,
|
||||||
|
Right
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,95 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a condition operator that performs a boolean operation using a left- and right-side
|
||||||
|
/// </summary>
|
||||||
|
public abstract class ConditionOperator<TLeftSide, TRightSide> : BaseConditionOperator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Evaluates the operator on a and b
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="a">The parameter on the left side of the expression</param>
|
||||||
|
/// <param name="b">The parameter on the right side of the expression</param>
|
||||||
|
public abstract bool Evaluate(TLeftSide a, TRightSide b);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool SupportsType(Type type, ConditionParameterSide side)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
return true;
|
||||||
|
if (side == ConditionParameterSide.Left)
|
||||||
|
return LeftSideType.IsCastableFrom(type);
|
||||||
|
return RightSideType.IsCastableFrom(type);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
internal override bool InternalEvaluate(object? leftSideValue, object? rightSideValue)
|
||||||
|
{
|
||||||
|
// TODO: Can we avoid boxing/unboxing?
|
||||||
|
TLeftSide leftSide;
|
||||||
|
if (leftSideValue != null)
|
||||||
|
leftSide = (TLeftSide) Convert.ChangeType(leftSideValue, typeof(TLeftSide));
|
||||||
|
else
|
||||||
|
leftSide = default;
|
||||||
|
|
||||||
|
TRightSide rightSide;
|
||||||
|
if (rightSideValue != null)
|
||||||
|
rightSide = (TRightSide) Convert.ChangeType(rightSideValue, typeof(TRightSide));
|
||||||
|
else
|
||||||
|
rightSide = default;
|
||||||
|
|
||||||
|
return Evaluate(leftSide, rightSide);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Type LeftSideType => typeof(TLeftSide);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Type RightSideType => typeof(TRightSide);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Represents a condition operator that performs a boolean operation using only a left side
|
||||||
|
/// </summary>
|
||||||
|
public abstract class ConditionOperator<TLeftSide> : BaseConditionOperator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Evaluates the operator on a and b
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="a">The parameter on the left side of the expression</param>
|
||||||
|
public abstract bool Evaluate(TLeftSide a);
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override bool SupportsType(Type type, ConditionParameterSide side)
|
||||||
|
{
|
||||||
|
if (type == null)
|
||||||
|
return true;
|
||||||
|
if (side == ConditionParameterSide.Left)
|
||||||
|
return LeftSideType.IsCastableFrom(type);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
internal override bool InternalEvaluate(object? leftSideValue, object? rightSideValue)
|
||||||
|
{
|
||||||
|
// TODO: Can we avoid boxing/unboxing?
|
||||||
|
TLeftSide leftSide;
|
||||||
|
if (leftSideValue != null)
|
||||||
|
leftSide = (TLeftSide) Convert.ChangeType(leftSideValue, typeof(TLeftSide));
|
||||||
|
else
|
||||||
|
leftSide = default;
|
||||||
|
|
||||||
|
return Evaluate(leftSide);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override Type LeftSideType => typeof(TLeftSide);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Always <c>null</c>, not applicable to this type of condition operator
|
||||||
|
/// </summary>
|
||||||
|
public override Type? RightSideType => null;
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -1,55 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Artemis.Core
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// A condition operator is used by the conditions system to perform a specific boolean check
|
|
||||||
/// </summary>
|
|
||||||
public abstract class ConditionOperator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the plugin info this condition operator belongs to
|
|
||||||
/// <para>Note: Not set until after registering</para>
|
|
||||||
/// </summary>
|
|
||||||
public PluginInfo PluginInfo { get; internal set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the types this operator supports
|
|
||||||
/// </summary>
|
|
||||||
public abstract IReadOnlyCollection<Type> CompatibleTypes { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the description of this logical operator
|
|
||||||
/// </summary>
|
|
||||||
public abstract string Description { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets the icon of this logical operator
|
|
||||||
/// </summary>
|
|
||||||
public abstract string Icon { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets or sets whether this condition operator supports a right side, defaults to true
|
|
||||||
/// </summary>
|
|
||||||
public bool SupportsRightSide { get; protected set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns whether the given type is supported by the operator
|
|
||||||
/// </summary>
|
|
||||||
public bool SupportsType(Type type)
|
|
||||||
{
|
|
||||||
if (type == null)
|
|
||||||
return true;
|
|
||||||
return CompatibleTypes.Any(t => t.IsCastableFrom(type));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Evaluates the operator on a and b
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="a">The parameter on the left side of the expression</param>
|
|
||||||
/// <param name="b">The parameter on the right side of the expression</param>
|
|
||||||
public abstract bool Evaluate(object? a, object? b);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@ -47,7 +47,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the operator
|
/// Gets the operator
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ConditionOperator? Operator { get; private set; }
|
public BaseConditionOperator? Operator { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the data model condition list this predicate belongs to
|
/// Gets the data model condition list this predicate belongs to
|
||||||
@ -99,6 +99,8 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
if (path != null && !path.IsValid)
|
if (path != null && !path.IsValid)
|
||||||
throw new ArtemisCoreException("Cannot update right side of predicate to an invalid path");
|
throw new ArtemisCoreException("Cannot update right side of predicate to an invalid path");
|
||||||
|
if (Operator != null && path != null && !Operator.SupportsType(path.GetPropertyType()!, ConditionParameterSide.Right))
|
||||||
|
throw new ArtemisCoreException($"Selected operator does not support right side of type {path.GetPropertyType()!.Name}");
|
||||||
|
|
||||||
RightPath?.Dispose();
|
RightPath?.Dispose();
|
||||||
RightPath = path != null ? new DataModelPath(path) : null;
|
RightPath = path != null ? new DataModelPath(path) : null;
|
||||||
@ -123,7 +125,7 @@ namespace Artemis.Core
|
|||||||
/// Updates the operator of the predicate and re-compiles the expression
|
/// Updates the operator of the predicate and re-compiles the expression
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="conditionOperator"></param>
|
/// <param name="conditionOperator"></param>
|
||||||
public void UpdateOperator(ConditionOperator? conditionOperator)
|
public void UpdateOperator(BaseConditionOperator? conditionOperator)
|
||||||
{
|
{
|
||||||
if (conditionOperator == null)
|
if (conditionOperator == null)
|
||||||
{
|
{
|
||||||
@ -139,12 +141,13 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
Type leftType = LeftPath.GetPropertyType()!;
|
Type leftType = LeftPath.GetPropertyType()!;
|
||||||
if (!conditionOperator.SupportsType(leftType))
|
// Left side can't go empty so enforce a match
|
||||||
|
if (!conditionOperator.SupportsType(leftType, ConditionParameterSide.Left))
|
||||||
throw new ArtemisCoreException($"Cannot apply operator {conditionOperator.GetType().Name} to this predicate because " +
|
throw new ArtemisCoreException($"Cannot apply operator {conditionOperator.GetType().Name} to this predicate because " +
|
||||||
$"it does not support left side type {leftType.Name}");
|
$"it does not support left side type {leftType.Name}");
|
||||||
|
|
||||||
if (conditionOperator.SupportsType(leftType))
|
Operator = conditionOperator;
|
||||||
Operator = conditionOperator;
|
ValidateRightSide();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -206,7 +209,7 @@ namespace Artemis.Core
|
|||||||
if (leftSideValue != null && leftSideValue.GetType().IsValueType && RightStaticValue == null)
|
if (leftSideValue != null && leftSideValue.GetType().IsValueType && RightStaticValue == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return Operator.Evaluate(leftSideValue, RightStaticValue);
|
return Operator.InternalEvaluate(leftSideValue, RightStaticValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RightPath == null || !RightPath.IsValid)
|
if (RightPath == null || !RightPath.IsValid)
|
||||||
@ -217,8 +220,8 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
// If the path targets a property inside the list, evaluate on the list path value instead of the right path value
|
// If the path targets a property inside the list, evaluate on the list path value instead of the right path value
|
||||||
if (RightPath.Target is ListPredicateWrapperDataModel)
|
if (RightPath.Target is ListPredicateWrapperDataModel)
|
||||||
return Operator.Evaluate(GetListPathValue(LeftPath, target), GetListPathValue(RightPath, target));
|
return Operator.InternalEvaluate(GetListPathValue(LeftPath, target), GetListPathValue(RightPath, target));
|
||||||
return Operator.Evaluate(GetListPathValue(LeftPath, target), RightPath.GetValue());
|
return Operator.InternalEvaluate(GetListPathValue(LeftPath, target), RightPath.GetValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -300,7 +303,7 @@ namespace Artemis.Core
|
|||||||
// Operator
|
// Operator
|
||||||
if (Entity.OperatorPluginGuid != null)
|
if (Entity.OperatorPluginGuid != null)
|
||||||
{
|
{
|
||||||
ConditionOperator? conditionOperator = ConditionOperatorStore.Get(Entity.OperatorPluginGuid.Value, Entity.OperatorType)?.ConditionOperator;
|
BaseConditionOperator? conditionOperator = ConditionOperatorStore.Get(Entity.OperatorPluginGuid.Value, Entity.OperatorType)?.ConditionOperator;
|
||||||
if (conditionOperator != null)
|
if (conditionOperator != null)
|
||||||
UpdateOperator(conditionOperator);
|
UpdateOperator(conditionOperator);
|
||||||
}
|
}
|
||||||
@ -361,28 +364,31 @@ namespace Artemis.Core
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Type leftType = LeftPath.GetPropertyType()!;
|
Type leftType = LeftPath.GetPropertyType()!;
|
||||||
if (!Operator.SupportsType(leftType))
|
if (!Operator.SupportsType(leftType, ConditionParameterSide.Left))
|
||||||
Operator = null;
|
Operator = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ValidateRightSide()
|
private void ValidateRightSide()
|
||||||
{
|
{
|
||||||
Type? leftType = LeftPath?.GetPropertyType();
|
if (Operator == null)
|
||||||
|
return;
|
||||||
|
|
||||||
if (PredicateType == ProfileRightSideType.Dynamic)
|
if (PredicateType == ProfileRightSideType.Dynamic)
|
||||||
{
|
{
|
||||||
if (RightPath == null || !RightPath.IsValid)
|
if (RightPath == null || !RightPath.IsValid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Type rightSideType = RightPath.GetPropertyType()!;
|
Type rightSideType = RightPath.GetPropertyType()!;
|
||||||
if (leftType != null && !leftType.IsCastableFrom(rightSideType))
|
if (!Operator.SupportsType(rightSideType, ConditionParameterSide.Right))
|
||||||
UpdateRightSideDynamic(null);
|
UpdateRightSideDynamic(null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (RightStaticValue != null && (leftType == null || leftType.IsCastableFrom(RightStaticValue.GetType())))
|
if (RightStaticValue == null)
|
||||||
UpdateRightSideStatic(RightStaticValue);
|
return;
|
||||||
else
|
|
||||||
UpdateRightSideStatic(null);
|
if (!Operator.SupportsType(RightStaticValue.GetType(), ConditionParameterSide.Right))
|
||||||
|
UpdateRightSideDynamic(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -391,24 +397,27 @@ namespace Artemis.Core
|
|||||||
RightPath?.Dispose();
|
RightPath?.Dispose();
|
||||||
RightPath = null;
|
RightPath = null;
|
||||||
|
|
||||||
// If the left side is empty simply apply the value, any validation will wait
|
// If the operator is null simply apply the value, any validation will wait
|
||||||
if (LeftPath == null || !LeftPath.IsValid)
|
if (Operator == null)
|
||||||
{
|
{
|
||||||
RightStaticValue = staticValue;
|
RightStaticValue = staticValue;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// If the operator does not support a right side, always set it to null
|
||||||
// If the left path is valid we can expect a type
|
if (Operator.RightSideType == null)
|
||||||
Type leftSideType = LeftPath.GetPropertyType()!;
|
{
|
||||||
|
RightStaticValue = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// If not null ensure the types match and if not, convert it
|
// If not null ensure the types match and if not, convert it
|
||||||
if (staticValue != null && staticValue.GetType() == leftSideType)
|
if (staticValue != null && staticValue.GetType() == Operator.RightSideType)
|
||||||
RightStaticValue = staticValue;
|
RightStaticValue = staticValue;
|
||||||
else if (staticValue != null)
|
else if (staticValue != null)
|
||||||
RightStaticValue = Convert.ChangeType(staticValue, leftSideType);
|
RightStaticValue = Convert.ChangeType(staticValue, Operator.RightSideType);
|
||||||
// If null create a default instance for value types or simply make it null for reference types
|
// If null create a default instance for value types or simply make it null for reference types
|
||||||
else if (leftSideType.IsValueType)
|
else if (Operator.RightSideType.IsValueType)
|
||||||
RightStaticValue = Activator.CreateInstance(leftSideType);
|
RightStaticValue = Activator.CreateInstance(Operator.RightSideType);
|
||||||
else
|
else
|
||||||
RightStaticValue = null;
|
RightStaticValue = null;
|
||||||
}
|
}
|
||||||
@ -426,7 +435,7 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
private void ConditionOperatorStoreOnConditionOperatorAdded(object sender, ConditionOperatorStoreEvent e)
|
private void ConditionOperatorStoreOnConditionOperatorAdded(object sender, ConditionOperatorStoreEvent e)
|
||||||
{
|
{
|
||||||
ConditionOperator conditionOperator = e.Registration.ConditionOperator;
|
BaseConditionOperator conditionOperator = e.Registration.ConditionOperator;
|
||||||
if (Entity.OperatorPluginGuid == conditionOperator.PluginInfo.Guid && Entity.OperatorType == conditionOperator.GetType().Name)
|
if (Entity.OperatorPluginGuid == conditionOperator.PluginInfo.Guid && Entity.OperatorType == conditionOperator.GetType().Name)
|
||||||
UpdateOperator(conditionOperator);
|
UpdateOperator(conditionOperator);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.IO;
|
||||||
using Artemis.Core.DataModelExpansions;
|
using Artemis.Core.DataModelExpansions;
|
||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
using Artemis.Storage.Entities.Profile.Conditions;
|
using Artemis.Storage.Entities.Profile.Conditions;
|
||||||
@ -43,7 +44,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the operator
|
/// Gets the operator
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ConditionOperator? Operator { get; private set; }
|
public BaseConditionOperator? Operator { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the path of the left property
|
/// Gets the path of the left property
|
||||||
@ -87,6 +88,8 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
if (path != null && !path.IsValid)
|
if (path != null && !path.IsValid)
|
||||||
throw new ArtemisCoreException("Cannot update right side of predicate to an invalid path");
|
throw new ArtemisCoreException("Cannot update right side of predicate to an invalid path");
|
||||||
|
if (Operator != null && path != null && !Operator.SupportsType(path.GetPropertyType()!, ConditionParameterSide.Right))
|
||||||
|
throw new ArtemisCoreException($"Selected operator does not support right side of type {path.GetPropertyType()!.Name}");
|
||||||
|
|
||||||
RightPath?.Dispose();
|
RightPath?.Dispose();
|
||||||
RightPath = path != null ? new DataModelPath(path) : null;
|
RightPath = path != null ? new DataModelPath(path) : null;
|
||||||
@ -104,24 +107,27 @@ namespace Artemis.Core
|
|||||||
RightPath?.Dispose();
|
RightPath?.Dispose();
|
||||||
RightPath = null;
|
RightPath = null;
|
||||||
|
|
||||||
// If the left side is empty simply apply the value, any validation will wait
|
// If the operator is null simply apply the value, any validation will wait
|
||||||
if (LeftPath == null || !LeftPath.IsValid)
|
if (Operator == null)
|
||||||
{
|
{
|
||||||
RightStaticValue = staticValue;
|
RightStaticValue = staticValue;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
// If the operator does not support a right side, always set it to null
|
||||||
// If the left path is valid we can expect a type
|
if (Operator.RightSideType == null)
|
||||||
Type leftSideType = LeftPath.GetPropertyType()!;
|
{
|
||||||
|
RightStaticValue = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// If not null ensure the types match and if not, convert it
|
// If not null ensure the types match and if not, convert it
|
||||||
if (staticValue != null && staticValue.GetType() == leftSideType)
|
if (staticValue != null && staticValue.GetType() == Operator.RightSideType)
|
||||||
RightStaticValue = staticValue;
|
RightStaticValue = staticValue;
|
||||||
else if (staticValue != null)
|
else if (staticValue != null)
|
||||||
RightStaticValue = Convert.ChangeType(staticValue, leftSideType);
|
RightStaticValue = Convert.ChangeType(staticValue, Operator.RightSideType);
|
||||||
// If null create a default instance for value types or simply make it null for reference types
|
// If null create a default instance for value types or simply make it null for reference types
|
||||||
else if (leftSideType.IsValueType)
|
else if (Operator.RightSideType.IsValueType)
|
||||||
RightStaticValue = Activator.CreateInstance(leftSideType);
|
RightStaticValue = Activator.CreateInstance(Operator.RightSideType);
|
||||||
else
|
else
|
||||||
RightStaticValue = null;
|
RightStaticValue = null;
|
||||||
}
|
}
|
||||||
@ -130,9 +136,8 @@ namespace Artemis.Core
|
|||||||
/// Updates the operator of the predicate and re-compiles the expression
|
/// Updates the operator of the predicate and re-compiles the expression
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="conditionOperator"></param>
|
/// <param name="conditionOperator"></param>
|
||||||
public void UpdateOperator(ConditionOperator? conditionOperator)
|
public void UpdateOperator(BaseConditionOperator? conditionOperator)
|
||||||
{
|
{
|
||||||
// Calling CreateExpression will clear compiled expressions
|
|
||||||
if (conditionOperator == null)
|
if (conditionOperator == null)
|
||||||
{
|
{
|
||||||
Operator = null;
|
Operator = null;
|
||||||
@ -147,11 +152,13 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
Type leftType = LeftPath.GetPropertyType()!;
|
Type leftType = LeftPath.GetPropertyType()!;
|
||||||
if (!conditionOperator.SupportsType(leftType))
|
// Left side can't go empty so enforce a match
|
||||||
|
if (!conditionOperator.SupportsType(leftType, ConditionParameterSide.Left))
|
||||||
throw new ArtemisCoreException($"Cannot apply operator {conditionOperator.GetType().Name} to this predicate because " +
|
throw new ArtemisCoreException($"Cannot apply operator {conditionOperator.GetType().Name} to this predicate because " +
|
||||||
$"it does not support left side type {leftType.Name}");
|
$"it does not support left side type {leftType.Name}");
|
||||||
|
|
||||||
Operator = conditionOperator;
|
Operator = conditionOperator;
|
||||||
|
ValidateRightSide();
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -167,14 +174,14 @@ namespace Artemis.Core
|
|||||||
if (leftSideValue != null && leftSideValue.GetType().IsValueType && RightStaticValue == null)
|
if (leftSideValue != null && leftSideValue.GetType().IsValueType && RightStaticValue == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return Operator.Evaluate(leftSideValue, RightStaticValue);
|
return Operator.InternalEvaluate(leftSideValue, RightStaticValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (RightPath == null || !RightPath.IsValid)
|
if (RightPath == null || !RightPath.IsValid)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Compare with dynamic values
|
// Compare with dynamic values
|
||||||
return Operator.Evaluate(LeftPath.GetValue(), RightPath.GetValue());
|
return Operator.InternalEvaluate(LeftPath.GetValue(), RightPath.GetValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -237,7 +244,7 @@ namespace Artemis.Core
|
|||||||
// Operator
|
// Operator
|
||||||
if (Entity.OperatorPluginGuid != null)
|
if (Entity.OperatorPluginGuid != null)
|
||||||
{
|
{
|
||||||
ConditionOperator? conditionOperator = ConditionOperatorStore.Get(Entity.OperatorPluginGuid.Value, Entity.OperatorType)?.ConditionOperator;
|
BaseConditionOperator? conditionOperator = ConditionOperatorStore.Get(Entity.OperatorPluginGuid.Value, Entity.OperatorType)?.ConditionOperator;
|
||||||
if (conditionOperator != null)
|
if (conditionOperator != null)
|
||||||
UpdateOperator(conditionOperator);
|
UpdateOperator(conditionOperator);
|
||||||
}
|
}
|
||||||
@ -292,28 +299,31 @@ namespace Artemis.Core
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
Type leftType = LeftPath.GetPropertyType()!;
|
Type leftType = LeftPath.GetPropertyType()!;
|
||||||
if (!Operator.SupportsType(leftType))
|
if (!Operator.SupportsType(leftType, ConditionParameterSide.Left))
|
||||||
Operator = null;
|
Operator = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ValidateRightSide()
|
private void ValidateRightSide()
|
||||||
{
|
{
|
||||||
Type? leftType = LeftPath?.GetPropertyType();
|
if (Operator == null)
|
||||||
|
return;
|
||||||
|
|
||||||
if (PredicateType == ProfileRightSideType.Dynamic)
|
if (PredicateType == ProfileRightSideType.Dynamic)
|
||||||
{
|
{
|
||||||
if (RightPath == null || !RightPath.IsValid)
|
if (RightPath == null || !RightPath.IsValid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Type rightSideType = RightPath.GetPropertyType()!;
|
Type rightSideType = RightPath.GetPropertyType()!;
|
||||||
if (leftType != null && !leftType.IsCastableFrom(rightSideType))
|
if (!Operator.SupportsType(rightSideType, ConditionParameterSide.Right))
|
||||||
UpdateRightSideDynamic(null);
|
UpdateRightSideDynamic(null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (RightStaticValue != null && (leftType == null || leftType.IsCastableFrom(RightStaticValue.GetType())))
|
if (RightStaticValue == null)
|
||||||
UpdateRightSideStatic(RightStaticValue);
|
return;
|
||||||
else
|
|
||||||
UpdateRightSideStatic(null);
|
if (!Operator.SupportsType(RightStaticValue.GetType(), ConditionParameterSide.Right))
|
||||||
|
UpdateRightSideDynamic(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -321,7 +331,7 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
private void ConditionOperatorStoreOnConditionOperatorAdded(object? sender, ConditionOperatorStoreEvent e)
|
private void ConditionOperatorStoreOnConditionOperatorAdded(object? sender, ConditionOperatorStoreEvent e)
|
||||||
{
|
{
|
||||||
ConditionOperator conditionOperator = e.Registration.ConditionOperator;
|
BaseConditionOperator conditionOperator = e.Registration.ConditionOperator;
|
||||||
if (Entity.OperatorPluginGuid == conditionOperator.PluginInfo.Guid && Entity.OperatorType == conditionOperator.GetType().Name)
|
if (Entity.OperatorPluginGuid == conditionOperator.PluginInfo.Guid && Entity.OperatorType == conditionOperator.GetType().Name)
|
||||||
UpdateOperator(conditionOperator);
|
UpdateOperator(conditionOperator);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,7 +12,7 @@ namespace Artemis.Core.Services
|
|||||||
RegisterBuiltInConditionOperators();
|
RegisterBuiltInConditionOperators();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConditionOperatorRegistration RegisterConditionOperator(PluginInfo pluginInfo, ConditionOperator conditionOperator)
|
public ConditionOperatorRegistration RegisterConditionOperator(PluginInfo pluginInfo, BaseConditionOperator conditionOperator)
|
||||||
{
|
{
|
||||||
if (pluginInfo == null)
|
if (pluginInfo == null)
|
||||||
throw new ArgumentNullException(nameof(pluginInfo));
|
throw new ArgumentNullException(nameof(pluginInfo));
|
||||||
@ -30,12 +30,12 @@ namespace Artemis.Core.Services
|
|||||||
ConditionOperatorStore.Remove(registration);
|
ConditionOperatorStore.Remove(registration);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<ConditionOperator> GetConditionOperatorsForType(Type type)
|
public List<BaseConditionOperator> GetConditionOperatorsForType(Type type, ConditionParameterSide side)
|
||||||
{
|
{
|
||||||
return ConditionOperatorStore.GetForType(type).Select(r => r.ConditionOperator).ToList();
|
return ConditionOperatorStore.GetForType(type, side).Select(r => r.ConditionOperator).ToList();
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConditionOperator GetConditionOperator(Guid operatorPluginGuid, string operatorType)
|
public BaseConditionOperator GetConditionOperator(Guid operatorPluginGuid, string operatorType)
|
||||||
{
|
{
|
||||||
return ConditionOperatorStore.Get(operatorPluginGuid, operatorType)?.ConditionOperator;
|
return ConditionOperatorStore.Get(operatorPluginGuid, operatorType)?.ConditionOperator;
|
||||||
}
|
}
|
||||||
@ -47,6 +47,8 @@ namespace Artemis.Core.Services
|
|||||||
RegisterConditionOperator(Constants.CorePluginInfo, new NotEqualConditionOperator());
|
RegisterConditionOperator(Constants.CorePluginInfo, new NotEqualConditionOperator());
|
||||||
|
|
||||||
// Numeric operators
|
// Numeric operators
|
||||||
|
RegisterConditionOperator(Constants.CorePluginInfo, new NumberEqualsConditionOperator());
|
||||||
|
RegisterConditionOperator(Constants.CorePluginInfo, new NumberNotEqualConditionOperator());
|
||||||
RegisterConditionOperator(Constants.CorePluginInfo, new LessThanConditionOperator());
|
RegisterConditionOperator(Constants.CorePluginInfo, new LessThanConditionOperator());
|
||||||
RegisterConditionOperator(Constants.CorePluginInfo, new GreaterThanConditionOperator());
|
RegisterConditionOperator(Constants.CorePluginInfo, new GreaterThanConditionOperator());
|
||||||
RegisterConditionOperator(Constants.CorePluginInfo, new LessThanOrEqualConditionOperator());
|
RegisterConditionOperator(Constants.CorePluginInfo, new LessThanOrEqualConditionOperator());
|
||||||
|
|||||||
@ -14,7 +14,7 @@ namespace Artemis.Core.Services
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="pluginInfo">The PluginInfo of the plugin this condition operator belongs to</param>
|
/// <param name="pluginInfo">The PluginInfo of the plugin this condition operator belongs to</param>
|
||||||
/// <param name="conditionOperator">The condition operator to register</param>
|
/// <param name="conditionOperator">The condition operator to register</param>
|
||||||
ConditionOperatorRegistration RegisterConditionOperator([NotNull] PluginInfo pluginInfo, [NotNull] ConditionOperator conditionOperator);
|
ConditionOperatorRegistration RegisterConditionOperator([NotNull] PluginInfo pluginInfo, [NotNull] BaseConditionOperator conditionOperator);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Removes a condition operator so it is no longer available for use in layer conditions
|
/// Removes a condition operator so it is no longer available for use in layer conditions
|
||||||
@ -25,13 +25,13 @@ namespace Artemis.Core.Services
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns all the condition operators compatible with the provided type
|
/// Returns all the condition operators compatible with the provided type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
List<ConditionOperator> GetConditionOperatorsForType(Type type);
|
List<BaseConditionOperator> GetConditionOperatorsForType(Type type, ConditionParameterSide side);
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a condition operator by its plugin GUID and type name
|
/// Gets a condition operator by its plugin GUID and type name
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="operatorPluginGuid">The operator's plugin GUID</param>
|
/// <param name="operatorPluginGuid">The operator's plugin GUID</param>
|
||||||
/// <param name="operatorType">The type name of the operator</param>
|
/// <param name="operatorType">The type name of the operator</param>
|
||||||
ConditionOperator GetConditionOperator(Guid operatorPluginGuid, string operatorType);
|
BaseConditionOperator GetConditionOperator(Guid operatorPluginGuid, string operatorType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -8,7 +8,7 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
private static readonly List<ConditionOperatorRegistration> Registrations = new List<ConditionOperatorRegistration>();
|
private static readonly List<ConditionOperatorRegistration> Registrations = new List<ConditionOperatorRegistration>();
|
||||||
|
|
||||||
public static ConditionOperatorRegistration Add(ConditionOperator conditionOperator)
|
public static ConditionOperatorRegistration Add(BaseConditionOperator conditionOperator)
|
||||||
{
|
{
|
||||||
ConditionOperatorRegistration registration;
|
ConditionOperatorRegistration registration;
|
||||||
lock (Registrations)
|
lock (Registrations)
|
||||||
@ -46,19 +46,21 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static List<ConditionOperatorRegistration> GetForType(Type type)
|
public static List<ConditionOperatorRegistration> GetForType(Type type, ConditionParameterSide side)
|
||||||
{
|
{
|
||||||
lock (Registrations)
|
lock (Registrations)
|
||||||
{
|
{
|
||||||
if (type == null)
|
if (type == null)
|
||||||
return new List<ConditionOperatorRegistration>(Registrations);
|
return new List<ConditionOperatorRegistration>(Registrations);
|
||||||
|
|
||||||
List<ConditionOperatorRegistration> candidates = Registrations.Where(r => r.ConditionOperator.CompatibleTypes.Any(t => t.IsCastableFrom(type))).ToList();
|
List<ConditionOperatorRegistration> candidates = Registrations.Where(r => r.ConditionOperator.SupportsType(type, side)).ToList();
|
||||||
|
|
||||||
// If there are multiple operators with the same description, use the closest match
|
// If there are multiple operators with the same description, use the closest match
|
||||||
foreach (IGrouping<string, ConditionOperatorRegistration> candidate in candidates.GroupBy(r => r.ConditionOperator.Description).Where(g => g.Count() > 1).ToList())
|
foreach (IGrouping<string, ConditionOperatorRegistration> candidate in candidates.GroupBy(r => r.ConditionOperator.Description).Where(g => g.Count() > 1).ToList())
|
||||||
{
|
{
|
||||||
ConditionOperatorRegistration closest = candidate.OrderByDescending(r => r.ConditionOperator.CompatibleTypes.Contains(type)).FirstOrDefault();
|
ConditionOperatorRegistration closest = side == ConditionParameterSide.Left
|
||||||
|
? candidate.OrderByDescending(r => r.ConditionOperator.LeftSideType.ScoreCastability(type)).First()
|
||||||
|
: candidate.OrderByDescending(r => r.ConditionOperator.RightSideType!.ScoreCastability(type)).First();
|
||||||
foreach (ConditionOperatorRegistration conditionOperator in candidate)
|
foreach (ConditionOperatorRegistration conditionOperator in candidate)
|
||||||
{
|
{
|
||||||
if (conditionOperator != closest)
|
if (conditionOperator != closest)
|
||||||
|
|||||||
@ -7,7 +7,7 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class ConditionOperatorRegistration
|
public class ConditionOperatorRegistration
|
||||||
{
|
{
|
||||||
internal ConditionOperatorRegistration(ConditionOperator conditionOperator, Plugin plugin)
|
internal ConditionOperatorRegistration(BaseConditionOperator conditionOperator, Plugin plugin)
|
||||||
{
|
{
|
||||||
ConditionOperator = conditionOperator;
|
ConditionOperator = conditionOperator;
|
||||||
Plugin = plugin;
|
Plugin = plugin;
|
||||||
@ -18,7 +18,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the condition operator that has been registered
|
/// Gets the condition operator that has been registered
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ConditionOperator ConditionOperator { get; }
|
public BaseConditionOperator ConditionOperator { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the plugin the condition operator is associated with
|
/// Gets the plugin the condition operator is associated with
|
||||||
|
|||||||
@ -21,10 +21,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
private readonly IProfileEditorService _profileEditorService;
|
private readonly IProfileEditorService _profileEditorService;
|
||||||
private bool _isPrimitiveList;
|
private bool _isPrimitiveList;
|
||||||
private DataModelDynamicViewModel _leftSideSelectionViewModel;
|
private DataModelDynamicViewModel _leftSideSelectionViewModel;
|
||||||
private BindableCollection<ConditionOperator> _operators;
|
private BindableCollection<BaseConditionOperator> _operators;
|
||||||
private DataModelStaticViewModel _rightSideInputViewModel;
|
private DataModelStaticViewModel _rightSideInputViewModel;
|
||||||
private DataModelDynamicViewModel _rightSideSelectionViewModel;
|
private DataModelDynamicViewModel _rightSideSelectionViewModel;
|
||||||
private ConditionOperator _selectedOperator;
|
private BaseConditionOperator _selectedOperator;
|
||||||
|
|
||||||
private List<Type> _supportedInputTypes;
|
private List<Type> _supportedInputTypes;
|
||||||
|
|
||||||
@ -40,14 +40,14 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
_supportedInputTypes = new List<Type>();
|
_supportedInputTypes = new List<Type>();
|
||||||
|
|
||||||
SelectOperatorCommand = new DelegateCommand(ExecuteSelectOperatorCommand);
|
SelectOperatorCommand = new DelegateCommand(ExecuteSelectOperatorCommand);
|
||||||
Operators = new BindableCollection<ConditionOperator>();
|
Operators = new BindableCollection<BaseConditionOperator>();
|
||||||
|
|
||||||
Initialize();
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataModelConditionListPredicate DataModelConditionListPredicate => (DataModelConditionListPredicate) Model;
|
public DataModelConditionListPredicate DataModelConditionListPredicate => (DataModelConditionListPredicate) Model;
|
||||||
|
|
||||||
public BindableCollection<ConditionOperator> Operators
|
public BindableCollection<BaseConditionOperator> Operators
|
||||||
{
|
{
|
||||||
get => _operators;
|
get => _operators;
|
||||||
set => SetAndNotify(ref _operators, value);
|
set => SetAndNotify(ref _operators, value);
|
||||||
@ -71,7 +71,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
set => SetAndNotify(ref _rightSideInputViewModel, value);
|
set => SetAndNotify(ref _rightSideInputViewModel, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConditionOperator SelectedOperator
|
public BaseConditionOperator SelectedOperator
|
||||||
{
|
{
|
||||||
get => _selectedOperator;
|
get => _selectedOperator;
|
||||||
set => SetAndNotify(ref _selectedOperator, value);
|
set => SetAndNotify(ref _selectedOperator, value);
|
||||||
@ -129,35 +129,41 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
|
|
||||||
// Get the supported operators
|
// Get the supported operators
|
||||||
Operators.Clear();
|
Operators.Clear();
|
||||||
Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType ?? typeof(object)));
|
Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType ?? typeof(object), ConditionParameterSide.Left));
|
||||||
if (DataModelConditionListPredicate.Operator == null)
|
if (DataModelConditionListPredicate.Operator == null)
|
||||||
DataModelConditionListPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType ?? typeof(object))));
|
DataModelConditionListPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType ?? typeof(object), ConditionParameterSide.Left)));
|
||||||
SelectedOperator = DataModelConditionListPredicate.Operator;
|
SelectedOperator = DataModelConditionListPredicate.Operator;
|
||||||
if (SelectedOperator == null || !SelectedOperator.SupportsRightSide)
|
|
||||||
|
// Without a selected operator or one that supports a right side, leave the right side input empty
|
||||||
|
if (SelectedOperator == null || SelectedOperator.RightSideType == null)
|
||||||
{
|
{
|
||||||
DisposeRightSideStaticViewModel();
|
DisposeRightSideStaticViewModel();
|
||||||
DisposeRightSideDynamicViewModel();
|
DisposeRightSideDynamicViewModel();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the right side has the proper VM
|
// Ensure the right side has the proper VM
|
||||||
if (DataModelConditionListPredicate.PredicateType == ProfileRightSideType.Dynamic && SelectedOperator.SupportsRightSide)
|
if (DataModelConditionListPredicate.PredicateType == ProfileRightSideType.Dynamic)
|
||||||
{
|
{
|
||||||
DisposeRightSideStaticViewModel();
|
DisposeRightSideStaticViewModel();
|
||||||
if (RightSideSelectionViewModel == null)
|
if (RightSideSelectionViewModel == null)
|
||||||
CreateRightSideSelectionViewModel();
|
CreateRightSideSelectionViewModel();
|
||||||
|
|
||||||
RightSideSelectionViewModel.FilterTypes = new[] {leftSideType};
|
|
||||||
RightSideSelectionViewModel.ChangeDataModelPath(DataModelConditionListPredicate.RightPath);
|
RightSideSelectionViewModel.ChangeDataModelPath(DataModelConditionListPredicate.RightPath);
|
||||||
|
RightSideSelectionViewModel.FilterTypes = new[] {SelectedOperator.RightSideType};
|
||||||
}
|
}
|
||||||
else if (SelectedOperator.SupportsRightSide)
|
else
|
||||||
{
|
{
|
||||||
DisposeRightSideDynamicViewModel();
|
DisposeRightSideDynamicViewModel();
|
||||||
if (RightSideInputViewModel == null)
|
if (RightSideInputViewModel == null)
|
||||||
CreateRightSideInputViewModel(leftSideType);
|
CreateRightSideInputViewModel(SelectedOperator.RightSideType);
|
||||||
|
|
||||||
RightSideInputViewModel.Value = DataModelConditionListPredicate.RightStaticValue;
|
if (SelectedOperator.RightSideType.IsValueType && DataModelConditionListPredicate.RightStaticValue == null)
|
||||||
if (RightSideInputViewModel.TargetType != leftSideType)
|
RightSideInputViewModel.Value = SelectedOperator.RightSideType.GetDefault();
|
||||||
RightSideInputViewModel.UpdateTargetType(leftSideType);
|
else
|
||||||
|
RightSideInputViewModel.Value = DataModelConditionListPredicate.RightStaticValue;
|
||||||
|
if (RightSideInputViewModel.TargetType != SelectedOperator.RightSideType)
|
||||||
|
RightSideInputViewModel.UpdateTargetType(SelectedOperator.RightSideType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -210,7 +216,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
|
|
||||||
private void ExecuteSelectOperatorCommand(object context)
|
private void ExecuteSelectOperatorCommand(object context)
|
||||||
{
|
{
|
||||||
if (!(context is ConditionOperator dataModelConditionOperator))
|
if (!(context is BaseConditionOperator dataModelConditionOperator))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SelectedOperator = dataModelConditionOperator;
|
SelectedOperator = dataModelConditionOperator;
|
||||||
@ -276,7 +282,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
|
|
||||||
// Add an extra data model to the selection VM to allow self-referencing the current item
|
// Add an extra data model to the selection VM to allow self-referencing the current item
|
||||||
// The safe cast prevents adding this extra VM on primitive lists where they serve no purpose
|
// The safe cast prevents adding this extra VM on primitive lists where they serve no purpose
|
||||||
if (GetListDataModel()?.Children?.FirstOrDefault() is DataModelPropertiesViewModel listValue)
|
if (GetListDataModel()?.Children?.FirstOrDefault() is DataModelPropertiesViewModel listValue)
|
||||||
RightSideSelectionViewModel.ExtraDataModelViewModels.Add(listValue);
|
RightSideSelectionViewModel.ExtraDataModelViewModels.Add(listValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -19,10 +19,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
private readonly IDataModelUIService _dataModelUIService;
|
private readonly IDataModelUIService _dataModelUIService;
|
||||||
private readonly IProfileEditorService _profileEditorService;
|
private readonly IProfileEditorService _profileEditorService;
|
||||||
private DataModelDynamicViewModel _leftSideSelectionViewModel;
|
private DataModelDynamicViewModel _leftSideSelectionViewModel;
|
||||||
private BindableCollection<ConditionOperator> _operators;
|
private BindableCollection<BaseConditionOperator> _operators;
|
||||||
private DataModelStaticViewModel _rightSideInputViewModel;
|
private DataModelStaticViewModel _rightSideInputViewModel;
|
||||||
private DataModelDynamicViewModel _rightSideSelectionViewModel;
|
private DataModelDynamicViewModel _rightSideSelectionViewModel;
|
||||||
private ConditionOperator _selectedOperator;
|
private BaseConditionOperator _selectedOperator;
|
||||||
|
|
||||||
private List<Type> _supportedInputTypes;
|
private List<Type> _supportedInputTypes;
|
||||||
|
|
||||||
@ -39,7 +39,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
_supportedInputTypes = new List<Type>();
|
_supportedInputTypes = new List<Type>();
|
||||||
|
|
||||||
SelectOperatorCommand = new DelegateCommand(ExecuteSelectOperatorCommand);
|
SelectOperatorCommand = new DelegateCommand(ExecuteSelectOperatorCommand);
|
||||||
Operators = new BindableCollection<ConditionOperator>();
|
Operators = new BindableCollection<BaseConditionOperator>();
|
||||||
|
|
||||||
ShowDataModelValues = settingsService.GetSetting<bool>("ProfileEditor.ShowDataModelValues");
|
ShowDataModelValues = settingsService.GetSetting<bool>("ProfileEditor.ShowDataModelValues");
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
public PluginSetting<bool> ShowDataModelValues { get; }
|
public PluginSetting<bool> ShowDataModelValues { get; }
|
||||||
|
|
||||||
|
|
||||||
public BindableCollection<ConditionOperator> Operators
|
public BindableCollection<BaseConditionOperator> Operators
|
||||||
{
|
{
|
||||||
get => _operators;
|
get => _operators;
|
||||||
set => SetAndNotify(ref _operators, value);
|
set => SetAndNotify(ref _operators, value);
|
||||||
@ -62,7 +62,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
set => SetAndNotify(ref _leftSideSelectionViewModel, value);
|
set => SetAndNotify(ref _leftSideSelectionViewModel, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ConditionOperator SelectedOperator
|
public BaseConditionOperator SelectedOperator
|
||||||
{
|
{
|
||||||
get => _selectedOperator;
|
get => _selectedOperator;
|
||||||
set => SetAndNotify(ref _selectedOperator, value);
|
set => SetAndNotify(ref _selectedOperator, value);
|
||||||
@ -109,12 +109,13 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
|
|
||||||
// Get the supported operators
|
// Get the supported operators
|
||||||
Operators.Clear();
|
Operators.Clear();
|
||||||
Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType ?? typeof(object)));
|
Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType ?? typeof(object), ConditionParameterSide.Left));
|
||||||
if (DataModelConditionPredicate.Operator == null)
|
if (DataModelConditionPredicate.Operator == null)
|
||||||
DataModelConditionPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType ?? typeof(object))));
|
DataModelConditionPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType ?? typeof(object), ConditionParameterSide.Left)));
|
||||||
SelectedOperator = DataModelConditionPredicate.Operator;
|
SelectedOperator = DataModelConditionPredicate.Operator;
|
||||||
|
|
||||||
if (SelectedOperator == null || !SelectedOperator.SupportsRightSide)
|
// Without a selected operator or one that supports a right side, leave the right side input empty
|
||||||
|
if (SelectedOperator == null || SelectedOperator.RightSideType == null)
|
||||||
{
|
{
|
||||||
DisposeRightSideStaticViewModel();
|
DisposeRightSideStaticViewModel();
|
||||||
DisposeRightSideDynamicViewModel();
|
DisposeRightSideDynamicViewModel();
|
||||||
@ -129,20 +130,20 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
CreateRightSideSelectionViewModel();
|
CreateRightSideSelectionViewModel();
|
||||||
|
|
||||||
RightSideSelectionViewModel.ChangeDataModelPath(DataModelConditionPredicate.RightPath);
|
RightSideSelectionViewModel.ChangeDataModelPath(DataModelConditionPredicate.RightPath);
|
||||||
RightSideSelectionViewModel.FilterTypes = new[] {leftSideType};
|
RightSideSelectionViewModel.FilterTypes = new[] {SelectedOperator.RightSideType};
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DisposeRightSideDynamicViewModel();
|
DisposeRightSideDynamicViewModel();
|
||||||
if (RightSideInputViewModel == null)
|
if (RightSideInputViewModel == null)
|
||||||
CreateRightSideInputViewModel(leftSideType);
|
CreateRightSideInputViewModel(SelectedOperator.RightSideType);
|
||||||
|
|
||||||
if (leftSideType != null && leftSideType.IsValueType && DataModelConditionPredicate.RightStaticValue == null)
|
if (SelectedOperator.RightSideType.IsValueType && DataModelConditionPredicate.RightStaticValue == null)
|
||||||
RightSideInputViewModel.Value = leftSideType.GetDefault();
|
RightSideInputViewModel.Value = SelectedOperator.RightSideType.GetDefault();
|
||||||
else
|
else
|
||||||
RightSideInputViewModel.Value = DataModelConditionPredicate.RightStaticValue;
|
RightSideInputViewModel.Value = DataModelConditionPredicate.RightStaticValue;
|
||||||
if (RightSideInputViewModel.TargetType != leftSideType)
|
if (RightSideInputViewModel.TargetType != SelectedOperator.RightSideType)
|
||||||
RightSideInputViewModel.UpdateTargetType(leftSideType);
|
RightSideInputViewModel.UpdateTargetType(SelectedOperator.RightSideType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -188,7 +189,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
|
|
||||||
private void ExecuteSelectOperatorCommand(object context)
|
private void ExecuteSelectOperatorCommand(object context)
|
||||||
{
|
{
|
||||||
if (!(context is ConditionOperator DataModelConditionOperator))
|
if (!(context is BaseConditionOperator DataModelConditionOperator))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SelectedOperator = DataModelConditionOperator;
|
SelectedOperator = DataModelConditionOperator;
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user