mirror of
https://github.com/Artemis-RGB/Artemis
synced 2026-01-01 02:03:32 +00:00
Merge pull request #476 from Artemis-RGB/dynamic-datamodels
Dynamic datamodel - Conditions
This commit is contained in:
commit
551e32528e
@ -15,7 +15,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the parent of this part
|
/// Gets the parent of this part
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DataModelConditionPart Parent { get; internal set; }
|
public DataModelConditionPart? Parent { get; internal set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the children of this part
|
/// Gets the children of this part
|
||||||
|
|||||||
@ -50,6 +50,6 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="a">The parameter on the left side of the expression</param>
|
/// <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>
|
/// <param name="b">The parameter on the right side of the expression</param>
|
||||||
public abstract bool Evaluate(object a, object b);
|
public abstract bool Evaluate(object? a, object? b);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2,8 +2,6 @@
|
|||||||
using System.Collections;
|
using System.Collections;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
using System.Linq.Expressions;
|
|
||||||
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;
|
||||||
|
|
||||||
@ -37,37 +35,27 @@ namespace Artemis.Core
|
|||||||
Initialize();
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal DataModelConditionListEntity Entity { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the list operator
|
/// Gets or sets the list operator
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ListOperator ListOperator { get; set; }
|
public ListOperator ListOperator { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the path of the list property
|
||||||
|
/// </summary>
|
||||||
|
public DataModelPath? ListPath { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the type of the content of the list this predicate is evaluated on
|
/// Gets the type of the content of the list this predicate is evaluated on
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Type ListType { get; set; }
|
public Type? ListType { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets whether the list contains primitives
|
/// Gets whether the list contains primitives
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool IsPrimitiveList { get; set; }
|
public bool IsPrimitiveList { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
internal DataModelConditionListEntity Entity { get; set; }
|
||||||
/// Gets the currently used instance of the list data model
|
|
||||||
/// </summary>
|
|
||||||
public DataModel ListDataModel { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the path of the list property in the <see cref="ListDataModel" />
|
|
||||||
/// </summary>
|
|
||||||
public string ListPropertyPath { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the compiled function that accesses the list this condition evaluates on
|
|
||||||
/// </summary>
|
|
||||||
public Func<object, IList> CompiledListAccessor { get; private set; }
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool Evaluate()
|
public override bool Evaluate()
|
||||||
@ -75,50 +63,44 @@ namespace Artemis.Core
|
|||||||
if (_disposed)
|
if (_disposed)
|
||||||
throw new ObjectDisposedException("DataModelConditionList");
|
throw new ObjectDisposedException("DataModelConditionList");
|
||||||
|
|
||||||
if (CompiledListAccessor == null)
|
if (ListPath == null || !ListPath.IsValid)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return EvaluateObject(CompiledListAccessor(ListDataModel));
|
return EvaluateObject(ListPath.GetValue());
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the list the predicate is evaluated on
|
/// Updates the list the predicate is evaluated on
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dataModel">The data model of the list</param>
|
|
||||||
/// <param name="path">The path pointing to the list inside the list</param>
|
/// <param name="path">The path pointing to the list inside the list</param>
|
||||||
public void UpdateList(DataModel dataModel, string path)
|
public void UpdateList(DataModelPath? path)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
throw new ObjectDisposedException("DataModelConditionList");
|
throw new ObjectDisposedException("DataModelConditionList");
|
||||||
|
|
||||||
if (dataModel != null && path == null)
|
if (path != null && !path.IsValid)
|
||||||
throw new ArtemisCoreException("If a data model is provided, a path is also required");
|
throw new ArtemisCoreException("Cannot update list to an invalid path");
|
||||||
if (dataModel == null && path != null)
|
|
||||||
throw new ArtemisCoreException("If path is provided, a data model is also required");
|
|
||||||
|
|
||||||
if (dataModel != null)
|
ListPath?.Dispose();
|
||||||
{
|
ListPath = path != null ? new DataModelPath(path) : null;
|
||||||
Type listType = dataModel.GetListTypeAtPath(path);
|
|
||||||
if (listType == null)
|
|
||||||
throw new ArtemisCoreException($"Data model of type {dataModel.GetType().Name} does not contain a list at path '{path}'");
|
|
||||||
|
|
||||||
ListType = listType;
|
|
||||||
IsPrimitiveList = listType.IsPrimitive || listType.IsEnum || listType == typeof(string);
|
|
||||||
|
|
||||||
ListDataModel = dataModel;
|
|
||||||
ListPropertyPath = path;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the old root group that was tied to the old data model
|
// Remove the old root group that was tied to the old data model
|
||||||
while (Children.Any())
|
while (Children.Any())
|
||||||
RemoveChild(Children[0]);
|
RemoveChild(Children[0]);
|
||||||
|
|
||||||
if (dataModel == null)
|
if (ListPath != null)
|
||||||
return;
|
{
|
||||||
|
Type listType = ListPath.GetPropertyType()!;
|
||||||
|
ListType = listType.GetGenericArguments()[0];
|
||||||
|
IsPrimitiveList = ListType.IsPrimitive || ListType.IsEnum || ListType == typeof(string);
|
||||||
|
|
||||||
// Create a new root group
|
// Create a new root group
|
||||||
AddChild(new DataModelConditionGroup(this));
|
AddChild(new DataModelConditionGroup(this));
|
||||||
CreateExpression();
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ListType = null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#region IDisposable
|
#region IDisposable
|
||||||
@ -128,8 +110,7 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
_disposed = true;
|
_disposed = true;
|
||||||
|
|
||||||
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
|
ListPath?.Dispose();
|
||||||
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
|
|
||||||
|
|
||||||
foreach (DataModelConditionPart child in Children)
|
foreach (DataModelConditionPart child in Children)
|
||||||
child.Dispose();
|
child.Dispose();
|
||||||
@ -139,7 +120,7 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
internal override bool EvaluateObject(object target)
|
internal override bool EvaluateObject(object? target)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
throw new ObjectDisposedException("DataModelConditionList");
|
throw new ObjectDisposedException("DataModelConditionList");
|
||||||
@ -163,11 +144,8 @@ namespace Artemis.Core
|
|||||||
internal override void Save()
|
internal override void Save()
|
||||||
{
|
{
|
||||||
// Target list
|
// Target list
|
||||||
if (ListDataModel != null)
|
ListPath?.Save();
|
||||||
{
|
Entity.ListPath = ListPath?.Entity;
|
||||||
Entity.ListDataModelGuid = ListDataModel.PluginInfo.Guid;
|
|
||||||
Entity.ListPropertyPath = ListPropertyPath;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Operator
|
// Operator
|
||||||
Entity.ListOperator = (int) ListOperator;
|
Entity.ListOperator = (int) ListOperator;
|
||||||
@ -186,80 +164,39 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
internal void Initialize()
|
internal void Initialize()
|
||||||
{
|
{
|
||||||
DataModelStore.DataModelAdded += DataModelStoreOnDataModelAdded;
|
if (Entity.ListPath == null)
|
||||||
DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved;
|
|
||||||
if (Entity.ListDataModelGuid == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Get the data model ...
|
// Ensure the list path is valid and points to a list
|
||||||
DataModel dataModel = DataModelStore.Get(Entity.ListDataModelGuid.Value)?.DataModel;
|
DataModelPath listPath = new DataModelPath(null, Entity.ListPath);
|
||||||
if (dataModel == null)
|
Type listType = listPath.GetPropertyType()!;
|
||||||
return;
|
// Can't check this on an invalid list, if it becomes valid later lets hope for the best
|
||||||
// ... and ensure the path is valid
|
if (listPath.IsValid && !typeof(IList).IsAssignableFrom(listType))
|
||||||
Type listType = dataModel.GetListTypeAtPath(Entity.ListPropertyPath);
|
|
||||||
if (listType == null)
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ListType = listType;
|
ListPath = listPath;
|
||||||
IsPrimitiveList = listType.IsPrimitive || listType.IsEnum || listType == typeof(string);
|
if (ListPath.IsValid)
|
||||||
ListDataModel = dataModel;
|
{
|
||||||
ListPropertyPath = Entity.ListPropertyPath;
|
ListType = listType.GetGenericArguments()[0];
|
||||||
|
IsPrimitiveList = ListType.IsPrimitive || ListType.IsEnum || ListType == typeof(string);
|
||||||
CreateExpression();
|
}
|
||||||
|
else
|
||||||
if (ListDataModel == null)
|
{
|
||||||
return;
|
ListType = null;
|
||||||
|
IsPrimitiveList = false;
|
||||||
|
}
|
||||||
|
|
||||||
// There should only be one child and it should be a group
|
// There should only be one child and it should be a group
|
||||||
if (Entity.Children.SingleOrDefault() is DataModelConditionGroupEntity rootGroup)
|
if (Entity.Children.SingleOrDefault() is DataModelConditionGroupEntity rootGroup)
|
||||||
|
{
|
||||||
AddChild(new DataModelConditionGroup(this, rootGroup));
|
AddChild(new DataModelConditionGroup(this, rootGroup));
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
Entity.Children.Clear();
|
Entity.Children.Clear();
|
||||||
AddChild(new DataModelConditionGroup(this));
|
AddChild(new DataModelConditionGroup(this));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateExpression()
|
|
||||||
{
|
|
||||||
if (_disposed)
|
|
||||||
throw new ObjectDisposedException("DataModelConditionList");
|
|
||||||
|
|
||||||
ParameterExpression parameter = Expression.Parameter(typeof(object), "listDataModel");
|
|
||||||
Expression accessor = ListPropertyPath.Split('.').Aggregate<string, Expression>(
|
|
||||||
Expression.Convert(parameter, ListDataModel.GetType()),
|
|
||||||
Expression.Property
|
|
||||||
);
|
|
||||||
accessor = Expression.Convert(accessor, typeof(IList));
|
|
||||||
|
|
||||||
Expression<Func<object, IList>> lambda = Expression.Lambda<Func<object, IList>>(accessor, parameter);
|
|
||||||
CompiledListAccessor = lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#region Event handlers
|
|
||||||
|
|
||||||
private void DataModelStoreOnDataModelAdded(object sender, DataModelStoreEvent e)
|
|
||||||
{
|
|
||||||
DataModel dataModel = e.Registration.DataModel;
|
|
||||||
if (dataModel.PluginInfo.Guid == Entity.ListDataModelGuid && dataModel.ContainsPath(Entity.ListPropertyPath))
|
|
||||||
{
|
|
||||||
ListDataModel = dataModel;
|
|
||||||
ListPropertyPath = Entity.ListPropertyPath;
|
|
||||||
CreateExpression();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DataModelStoreOnDataModelRemoved(object sender, DataModelStoreEvent e)
|
|
||||||
{
|
|
||||||
if (ListDataModel != e.Registration.DataModel)
|
|
||||||
return;
|
|
||||||
|
|
||||||
ListDataModel = null;
|
|
||||||
CompiledListAccessor = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
using Artemis.Core.DataModelExpansions;
|
using Artemis.Core.DataModelExpansions;
|
||||||
using Artemis.Storage.Entities.Profile.Abstract;
|
using Artemis.Storage.Entities.Profile.Abstract;
|
||||||
@ -25,6 +23,7 @@ namespace Artemis.Core
|
|||||||
PredicateType = predicateType;
|
PredicateType = predicateType;
|
||||||
Entity = new DataModelConditionListPredicateEntity();
|
Entity = new DataModelConditionListPredicateEntity();
|
||||||
|
|
||||||
|
DataModelConditionList = null!;
|
||||||
ApplyParentList();
|
ApplyParentList();
|
||||||
Initialize();
|
Initialize();
|
||||||
}
|
}
|
||||||
@ -35,12 +34,11 @@ namespace Artemis.Core
|
|||||||
Entity = entity;
|
Entity = entity;
|
||||||
PredicateType = (ListRightSideType) entity.PredicateType;
|
PredicateType = (ListRightSideType) entity.PredicateType;
|
||||||
|
|
||||||
|
DataModelConditionList = null!;
|
||||||
ApplyParentList();
|
ApplyParentList();
|
||||||
Initialize();
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal DataModelConditionListPredicateEntity Entity { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets or sets the predicate type
|
/// Gets or sets the predicate type
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -49,7 +47,7 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the operator
|
/// Gets the operator
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ConditionOperator Operator { get; private set; }
|
public ConditionOperator? 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
|
||||||
@ -57,46 +55,40 @@ namespace Artemis.Core
|
|||||||
public DataModelConditionList DataModelConditionList { get; private set; }
|
public DataModelConditionList DataModelConditionList { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the path of the left property in the <see cref="ListType" />
|
/// Gets the path of the left property
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string LeftPropertyPath { get; private set; }
|
public DataModelPath? LeftPath { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the currently used instance of the right side data model
|
/// Gets the path of the right property
|
||||||
/// <para>Note: This is null when using a path inside the list</para>
|
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DataModel RightDataModel { get; set; }
|
public DataModelPath? RightPath { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the path of the right property in the <see cref="ListType" />
|
|
||||||
/// </summary>
|
|
||||||
public string RightPropertyPath { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the right static value, only used it <see cref="PredicateType" /> is
|
/// Gets the right static value, only used it <see cref="PredicateType" /> is
|
||||||
/// <see cref="ListRightSideType.Static" />
|
/// <see cref="ListRightSideType.Static" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object RightStaticValue { get; private set; }
|
public object? RightStaticValue { get; private set; }
|
||||||
|
|
||||||
public Func<object, object> LeftSideAccessor { get; set; }
|
internal DataModelConditionListPredicateEntity Entity { get; set; }
|
||||||
public Func<object, object> RightSideAccessor { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the left side of the predicate
|
/// Updates the left side of the predicate
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The path pointing to the left side value inside the list</param>
|
/// <param name="path">The path pointing to the left side value inside the list</param>
|
||||||
public void UpdateLeftSide(string path)
|
public void UpdateLeftSide(DataModelPath? path)
|
||||||
{
|
{
|
||||||
if (DataModelConditionList.IsPrimitiveList)
|
if (DataModelConditionList.IsPrimitiveList)
|
||||||
throw new ArtemisCoreException("Cannot apply a left side to a predicate inside a primitive list");
|
throw new ArtemisCoreException("Cannot apply a left side to a predicate inside a primitive list");
|
||||||
if (!ListContainsInnerPath(path))
|
|
||||||
throw new ArtemisCoreException($"List type {DataModelConditionList.ListType.Name} does not contain path {path}");
|
|
||||||
|
|
||||||
LeftPropertyPath = path;
|
if (path != null && !path.IsValid)
|
||||||
|
throw new ArtemisCoreException("Cannot update left side of predicate to an invalid path");
|
||||||
|
|
||||||
|
LeftPath?.Dispose();
|
||||||
|
LeftPath = path != null ? new DataModelPath(path) : null;
|
||||||
|
|
||||||
ValidateOperator();
|
ValidateOperator();
|
||||||
ValidateRightSide();
|
ValidateRightSide();
|
||||||
CreateExpression();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -104,61 +96,48 @@ namespace Artemis.Core
|
|||||||
/// and re-compiles the expression
|
/// and re-compiles the expression
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="path">The path pointing to the right side value inside the list</param>
|
/// <param name="path">The path pointing to the right side value inside the list</param>
|
||||||
public void UpdateRightSideDynamic(string path)
|
public void UpdateRightSideDynamicList(DataModelPath? path)
|
||||||
{
|
{
|
||||||
if (!ListContainsInnerPath(path))
|
if (path != null && !path.IsValid)
|
||||||
throw new ArtemisCoreException($"List type {DataModelConditionList.ListType.Name} does not contain path {path}");
|
throw new ArtemisCoreException("Cannot update right side of predicate to an invalid path");
|
||||||
|
|
||||||
|
RightPath?.Dispose();
|
||||||
|
RightPath = path != null ? new DataModelPath(path) : null;
|
||||||
|
|
||||||
PredicateType = ListRightSideType.DynamicList;
|
PredicateType = ListRightSideType.DynamicList;
|
||||||
RightPropertyPath = path;
|
|
||||||
|
|
||||||
CreateExpression();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the right side of the predicate using path to a value in a data model, makes the predicate dynamic and
|
/// Updates the right side of the predicate using path to a value in a data model, makes the predicate dynamic and
|
||||||
/// re-compiles the expression
|
/// re-compiles the expression
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dataModel"></param>
|
|
||||||
/// <param name="path">The path pointing to the right side value inside the list</param>
|
/// <param name="path">The path pointing to the right side value inside the list</param>
|
||||||
public void UpdateRightSideDynamic(DataModel dataModel, string path)
|
public void UpdateRightSideDynamic(DataModelPath? path)
|
||||||
{
|
{
|
||||||
if (dataModel != null && path == null)
|
RightPath?.Dispose();
|
||||||
throw new ArtemisCoreException("If a data model is provided, a path is also required");
|
RightPath = path;
|
||||||
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}'");
|
|
||||||
}
|
|
||||||
|
|
||||||
PredicateType = ListRightSideType.Dynamic;
|
PredicateType = ListRightSideType.Dynamic;
|
||||||
RightDataModel = dataModel;
|
|
||||||
RightPropertyPath = path;
|
|
||||||
|
|
||||||
CreateExpression();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the right side of the predicate, makes the predicate static and re-compiles the expression
|
/// Updates the right side of the predicate, makes the predicate static and re-compiles the expression
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="staticValue">The right side value to use</param>
|
/// <param name="staticValue">The right side value to use</param>
|
||||||
public void UpdateRightSideStatic(object staticValue)
|
public void UpdateRightSideStatic(object? staticValue)
|
||||||
{
|
{
|
||||||
PredicateType = ListRightSideType.Static;
|
PredicateType = ListRightSideType.Static;
|
||||||
RightPropertyPath = null;
|
RightPath?.Dispose();
|
||||||
|
RightPath = null;
|
||||||
|
|
||||||
SetStaticValue(staticValue);
|
SetStaticValue(staticValue);
|
||||||
CreateExpression();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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(ConditionOperator? conditionOperator)
|
||||||
{
|
{
|
||||||
if (conditionOperator == null)
|
if (conditionOperator == null)
|
||||||
{
|
{
|
||||||
@ -166,17 +145,20 @@ namespace Artemis.Core
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (LeftPropertyPath == null)
|
// No need to clear compiled expressions, without a left data model they are already null
|
||||||
|
if (LeftPath == null || !LeftPath.IsValid)
|
||||||
{
|
{
|
||||||
Operator = conditionOperator;
|
Operator = conditionOperator;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Type leftType = GetTypeAtInnerPath(LeftPropertyPath);
|
Type leftType = LeftPath.GetPropertyType()!;
|
||||||
|
if (!conditionOperator.SupportsType(leftType))
|
||||||
|
throw new ArtemisCoreException($"Cannot apply operator {conditionOperator.GetType().Name} to this predicate because " +
|
||||||
|
$"it does not support left side type {leftType.Name}");
|
||||||
|
|
||||||
if (conditionOperator.SupportsType(leftType))
|
if (conditionOperator.SupportsType(leftType))
|
||||||
Operator = conditionOperator;
|
Operator = conditionOperator;
|
||||||
|
|
||||||
CreateExpression();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -209,26 +191,46 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region IDisposable
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
ConditionOperatorStore.ConditionOperatorAdded -= ConditionOperatorStoreOnConditionOperatorAdded;
|
||||||
|
ConditionOperatorStore.ConditionOperatorRemoved -= ConditionOperatorStoreOnConditionOperatorRemoved;
|
||||||
|
|
||||||
|
LeftPath?.Dispose();
|
||||||
|
RightPath?.Dispose();
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
internal override bool EvaluateObject(object target)
|
internal override bool EvaluateObject(object target)
|
||||||
{
|
{
|
||||||
if (Operator == null || LeftSideAccessor == null || PredicateType != ListRightSideType.Static && RightSideAccessor == null)
|
if (Operator == null || LeftPath == null || !LeftPath.IsValid)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Compare with a static value
|
// Compare with a static value
|
||||||
if (PredicateType == ListRightSideType.Static)
|
if (PredicateType == ListRightSideType.Static)
|
||||||
{
|
{
|
||||||
if (!DataModelConditionList.ListType.IsValueType && RightStaticValue == null)
|
object? leftSideValue = GetListPathValue(LeftPath, target);
|
||||||
|
if (leftSideValue != null && leftSideValue.GetType().IsValueType && RightStaticValue == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return Operator.Evaluate(LeftSideAccessor(target), RightStaticValue);
|
return Operator.Evaluate(leftSideValue, RightStaticValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (RightPath == null || !RightPath.IsValid)
|
||||||
|
return false;
|
||||||
|
|
||||||
// Compare with dynamic values
|
// Compare with dynamic values
|
||||||
if (PredicateType == ListRightSideType.Dynamic)
|
if (PredicateType == ListRightSideType.Dynamic)
|
||||||
return Operator.Evaluate(LeftSideAccessor(target), RightSideAccessor(RightDataModel));
|
return Operator.Evaluate(GetListPathValue(LeftPath, target), RightPath.GetValue());
|
||||||
if (PredicateType == ListRightSideType.DynamicList)
|
if (PredicateType == ListRightSideType.DynamicList)
|
||||||
return Operator.Evaluate(LeftSideAccessor(target), RightSideAccessor(target));
|
return Operator.Evaluate(GetListPathValue(LeftPath, target), GetListPathValue(RightPath, target));
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@ -255,10 +257,12 @@ namespace Artemis.Core
|
|||||||
internal override void Save()
|
internal override void Save()
|
||||||
{
|
{
|
||||||
Entity.PredicateType = (int) PredicateType;
|
Entity.PredicateType = (int) PredicateType;
|
||||||
Entity.LeftPropertyPath = LeftPropertyPath;
|
|
||||||
|
|
||||||
Entity.RightDataModelGuid = RightDataModel?.PluginInfo?.Guid;
|
LeftPath?.Save();
|
||||||
Entity.RightPropertyPath = RightPropertyPath;
|
Entity.LeftPath = LeftPath?.Entity;
|
||||||
|
RightPath?.Save();
|
||||||
|
Entity.RightPath = RightPath?.Entity;
|
||||||
|
|
||||||
Entity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue);
|
Entity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue);
|
||||||
|
|
||||||
if (Operator != null)
|
if (Operator != null)
|
||||||
@ -275,19 +279,12 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
private void ApplyParentList()
|
private void ApplyParentList()
|
||||||
{
|
{
|
||||||
DataModelConditionPart current = Parent;
|
DataModelConditionPart? current = Parent;
|
||||||
|
|
||||||
while (current != null)
|
while (current != null)
|
||||||
{
|
{
|
||||||
if (current is DataModelConditionList parentList)
|
if (current is DataModelConditionList parentList)
|
||||||
{
|
{
|
||||||
DataModelConditionList = parentList;
|
DataModelConditionList = parentList;
|
||||||
|
|
||||||
if (LeftPropertyPath != null && !ListContainsInnerPath(LeftPropertyPath))
|
|
||||||
LeftPropertyPath = null;
|
|
||||||
if (RightPropertyPath != null && !ListContainsInnerPath(RightPropertyPath))
|
|
||||||
RightPropertyPath = null;
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -300,46 +297,44 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
private void Initialize()
|
private void Initialize()
|
||||||
{
|
{
|
||||||
DataModelStore.DataModelAdded += DataModelStoreOnDataModelAdded;
|
|
||||||
DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved;
|
|
||||||
ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded;
|
ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded;
|
||||||
ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved;
|
ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved;
|
||||||
|
|
||||||
// Left side
|
// Left side
|
||||||
if (Entity.LeftPropertyPath != null && ListContainsInnerPath(Entity.LeftPropertyPath))
|
if (Entity.LeftPath != null)
|
||||||
UpdateLeftSide(Entity.LeftPropertyPath);
|
{
|
||||||
|
LeftPath = DataModelConditionList.ListType != null
|
||||||
|
? new DataModelPath(ListPredicateWrapperDataModel.Create(DataModelConditionList.ListType), Entity.LeftPath)
|
||||||
|
: null;
|
||||||
|
}
|
||||||
|
|
||||||
// Operator
|
// Operator
|
||||||
if (Entity.OperatorPluginGuid != null)
|
if (Entity.OperatorPluginGuid != null)
|
||||||
{
|
{
|
||||||
ConditionOperator conditionOperator = ConditionOperatorStore.Get(Entity.OperatorPluginGuid.Value, Entity.OperatorType)?.ConditionOperator;
|
ConditionOperator? conditionOperator = ConditionOperatorStore.Get(Entity.OperatorPluginGuid.Value, Entity.OperatorType)?.ConditionOperator;
|
||||||
if (conditionOperator != null)
|
if (conditionOperator != null)
|
||||||
UpdateOperator(conditionOperator);
|
UpdateOperator(conditionOperator);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right side dynamic
|
// Right side dynamic
|
||||||
if (PredicateType == ListRightSideType.Dynamic && Entity.RightDataModelGuid != null && Entity.RightPropertyPath != null)
|
if (PredicateType == ListRightSideType.Dynamic && Entity.RightPath != null)
|
||||||
{
|
RightPath = new DataModelPath(null, Entity.RightPath);
|
||||||
DataModel dataModel = DataModelStore.Get(Entity.RightDataModelGuid.Value)?.DataModel;
|
|
||||||
if (dataModel != null && dataModel.ContainsPath(Entity.RightPropertyPath))
|
|
||||||
UpdateRightSideDynamic(dataModel, Entity.RightPropertyPath);
|
|
||||||
}
|
|
||||||
// Right side dynamic inside the list
|
// Right side dynamic inside the list
|
||||||
else if (PredicateType == ListRightSideType.DynamicList && Entity.RightPropertyPath != null)
|
else if (PredicateType == ListRightSideType.DynamicList && Entity.RightPath != null)
|
||||||
{
|
{
|
||||||
if (ListContainsInnerPath(Entity.RightPropertyPath))
|
RightPath = DataModelConditionList.ListType != null
|
||||||
UpdateRightSideDynamic(Entity.RightPropertyPath);
|
? new DataModelPath(ListPredicateWrapperDataModel.Create(DataModelConditionList.ListType), Entity.RightPath)
|
||||||
|
: null;
|
||||||
}
|
}
|
||||||
// Right side static
|
// Right side static
|
||||||
else if (PredicateType == ListRightSideType.Static && Entity.RightStaticValue != null)
|
else if (PredicateType == ListRightSideType.Static && Entity.RightStaticValue != null)
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (LeftPropertyPath != null)
|
if (LeftPath != null && LeftPath.IsValid)
|
||||||
{
|
{
|
||||||
// Use the left side type so JSON.NET has a better idea what to do
|
// Use the left side type so JSON.NET has a better idea what to do
|
||||||
Type leftSideType = GetTypeAtInnerPath(LeftPropertyPath);
|
Type leftSideType = LeftPath.GetPropertyType()!;
|
||||||
object rightSideValue;
|
object? rightSideValue;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -364,73 +359,62 @@ namespace Artemis.Core
|
|||||||
{
|
{
|
||||||
DeserializationLogger.LogListPredicateDeserializationFailure(this, e);
|
DeserializationLogger.LogListPredicateDeserializationFailure(this, e);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateExpression()
|
|
||||||
{
|
|
||||||
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 == ListRightSideType.DynamicList && Operator.SupportsRightSide)
|
|
||||||
CreateDynamicListAccessors();
|
|
||||||
else if (PredicateType == ListRightSideType.Dynamic && Operator.SupportsRightSide)
|
|
||||||
CreateDynamicAccessors();
|
|
||||||
else
|
|
||||||
CreateStaticAccessors();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ValidateOperator()
|
private void ValidateOperator()
|
||||||
{
|
{
|
||||||
if (LeftPropertyPath == null || Operator == null)
|
if (LeftPath == null || !LeftPath.IsValid || Operator == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Type leftSideType = GetTypeAtInnerPath(LeftPropertyPath);
|
Type leftType = LeftPath.GetPropertyType()!;
|
||||||
if (!Operator.SupportsType(leftSideType))
|
if (!Operator.SupportsType(leftType))
|
||||||
Operator = null;
|
Operator = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ValidateRightSide()
|
private void ValidateRightSide()
|
||||||
{
|
{
|
||||||
Type leftSideType = GetTypeAtInnerPath(LeftPropertyPath);
|
Type? leftType = LeftPath?.GetPropertyType();
|
||||||
if (PredicateType == ListRightSideType.Dynamic)
|
if (PredicateType == ListRightSideType.Dynamic)
|
||||||
{
|
{
|
||||||
if (RightDataModel == null)
|
if (RightPath == null || !RightPath.IsValid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Type rightSideType = RightDataModel.GetTypeAtPath(RightPropertyPath);
|
Type rightSideType = RightPath.GetPropertyType()!;
|
||||||
if (!leftSideType.IsCastableFrom(rightSideType))
|
if (leftType != null && !leftType.IsCastableFrom(rightSideType))
|
||||||
UpdateRightSideDynamic(null, null);
|
UpdateRightSideDynamic(null);
|
||||||
}
|
}
|
||||||
else if (PredicateType == ListRightSideType.DynamicList)
|
else if (PredicateType == ListRightSideType.DynamicList)
|
||||||
{
|
{
|
||||||
if (RightPropertyPath == null)
|
if (RightPath == null || !RightPath.IsValid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Type rightSideType = GetTypeAtInnerPath(RightPropertyPath);
|
Type rightSideType = RightPath.GetPropertyType()!;
|
||||||
if (!leftSideType.IsCastableFrom(rightSideType))
|
if (leftType != null && !leftType.IsCastableFrom(rightSideType))
|
||||||
UpdateRightSideDynamic(null);
|
UpdateRightSideDynamicList(null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (RightStaticValue != null && leftSideType.IsCastableFrom(RightStaticValue.GetType()))
|
if (RightStaticValue != null && (leftType == null || leftType.IsCastableFrom(RightStaticValue.GetType())))
|
||||||
UpdateRightSideStatic(RightStaticValue);
|
UpdateRightSideStatic(RightStaticValue);
|
||||||
else
|
else
|
||||||
UpdateRightSideStatic(null);
|
UpdateRightSideStatic(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetStaticValue(object staticValue)
|
private void SetStaticValue(object? staticValue)
|
||||||
{
|
{
|
||||||
|
RightPath?.Dispose();
|
||||||
|
RightPath = null;
|
||||||
|
|
||||||
// If the left side is empty simply apply the value, any validation will wait
|
// If the left side is empty simply apply the value, any validation will wait
|
||||||
if (LeftPropertyPath == null)
|
if (LeftPath == null || !LeftPath.IsValid)
|
||||||
{
|
{
|
||||||
RightStaticValue = staticValue;
|
RightStaticValue = staticValue;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Type leftSideType = GetTypeAtInnerPath(LeftPropertyPath);
|
// If the left path is valid we can expect a type
|
||||||
|
Type leftSideType = LeftPath.GetPropertyType()!;
|
||||||
|
|
||||||
// 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() == leftSideType)
|
||||||
@ -444,100 +428,17 @@ namespace Artemis.Core
|
|||||||
RightStaticValue = null;
|
RightStaticValue = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateDynamicListAccessors()
|
private object? GetListPathValue(DataModelPath path, object target)
|
||||||
{
|
{
|
||||||
if (LeftPropertyPath == null || RightPropertyPath == null || Operator == null)
|
if (!(path.Target is ListPredicateWrapperDataModel wrapper))
|
||||||
return;
|
throw new ArtemisCoreException("Data model condition list predicate has a path with an invalid target");
|
||||||
|
|
||||||
// List accessors share the same parameter because a list always contains one item per entry
|
wrapper.UntypedValue = target;
|
||||||
ParameterExpression leftSideParameter = Expression.Parameter(typeof(object), "listItem");
|
return path.GetValue();
|
||||||
Expression leftSideAccessor = CreateListAccessor(LeftPropertyPath, leftSideParameter);
|
|
||||||
Expression rightSideAccessor = CreateListAccessor(RightPropertyPath, leftSideParameter);
|
|
||||||
|
|
||||||
// A conversion may be required if the types differ
|
|
||||||
// This can cause issues if the DataModelConditionOperator 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);
|
|
||||||
|
|
||||||
LeftSideAccessor = Expression.Lambda<Func<object, object>>(leftSideAccessor, leftSideParameter).Compile();
|
|
||||||
RightSideAccessor = Expression.Lambda<Func<object, object>>(rightSideAccessor, leftSideParameter).Compile();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateDynamicAccessors()
|
|
||||||
{
|
|
||||||
if (LeftPropertyPath == null || RightPropertyPath == null || RightDataModel == null || Operator == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// List accessors share the same parameter because a list always contains one item per entry
|
|
||||||
ParameterExpression leftSideParameter = Expression.Parameter(typeof(object), "listItem");
|
|
||||||
Expression leftSideAccessor = CreateListAccessor(LeftPropertyPath, leftSideParameter);
|
|
||||||
Expression rightSideAccessor = ExpressionUtilities.CreateDataModelAccessor(RightDataModel, RightPropertyPath, "right", out ParameterExpression rightSideParameter);
|
|
||||||
|
|
||||||
// A conversion may be required if the types differ
|
|
||||||
// This can cause issues if the DataModelConditionOperator 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);
|
|
||||||
|
|
||||||
LeftSideAccessor = Expression.Lambda<Func<object, object>>(leftSideAccessor, leftSideParameter).Compile();
|
|
||||||
RightSideAccessor = Expression.Lambda<Func<object, object>>(rightSideAccessor, rightSideParameter).Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateStaticAccessors()
|
|
||||||
{
|
|
||||||
if (!DataModelConditionList.IsPrimitiveList && LeftPropertyPath == null || Operator == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
// List accessors share the same parameter because a list always contains one item per entry
|
|
||||||
ParameterExpression leftSideParameter = Expression.Parameter(typeof(object), "listItem");
|
|
||||||
Expression leftSideAccessor = DataModelConditionList.IsPrimitiveList
|
|
||||||
? Expression.Convert(leftSideParameter, DataModelConditionList.ListType)
|
|
||||||
: CreateListAccessor(LeftPropertyPath, leftSideParameter);
|
|
||||||
|
|
||||||
LeftSideAccessor = Expression.Lambda<Func<object, object>>(leftSideAccessor, leftSideParameter).Compile();
|
|
||||||
RightSideAccessor = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Expression CreateListAccessor(string path, ParameterExpression listParameter)
|
|
||||||
{
|
|
||||||
// Create an expression that checks every part of the path for null
|
|
||||||
// In the same iteration, create the accessor
|
|
||||||
Expression source = Expression.Convert(listParameter, DataModelConditionList.ListType);
|
|
||||||
return ExpressionUtilities.CreateNullCheckedAccessor(source, path);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region IDisposable
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
|
|
||||||
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
|
|
||||||
ConditionOperatorStore.ConditionOperatorAdded -= ConditionOperatorStoreOnConditionOperatorAdded;
|
|
||||||
ConditionOperatorStore.ConditionOperatorRemoved -= ConditionOperatorStoreOnConditionOperatorRemoved;
|
|
||||||
|
|
||||||
base.Dispose(disposing);
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region Event handlers
|
#region Event handlers
|
||||||
|
|
||||||
private void DataModelStoreOnDataModelAdded(object sender, DataModelStoreEvent e)
|
|
||||||
{
|
|
||||||
DataModel dataModel = e.Registration.DataModel;
|
|
||||||
if (dataModel.PluginInfo.Guid == Entity.RightDataModelGuid && dataModel.ContainsPath(Entity.RightPropertyPath))
|
|
||||||
UpdateRightSideDynamic(dataModel, Entity.RightPropertyPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DataModelStoreOnDataModelRemoved(object sender, DataModelStoreEvent e)
|
|
||||||
{
|
|
||||||
if (RightDataModel == e.Registration.DataModel)
|
|
||||||
{
|
|
||||||
RightSideAccessor = null;
|
|
||||||
RightDataModel = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConditionOperatorStoreOnConditionOperatorAdded(object sender, ConditionOperatorStoreEvent e)
|
private void ConditionOperatorStoreOnConditionOperatorAdded(object sender, ConditionOperatorStoreEvent e)
|
||||||
{
|
{
|
||||||
ConditionOperator conditionOperator = e.Registration.ConditionOperator;
|
ConditionOperator conditionOperator = e.Registration.ConditionOperator;
|
||||||
|
|||||||
@ -1,6 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Linq;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
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;
|
||||||
@ -45,109 +43,76 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the operator
|
/// Gets the operator
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public ConditionOperator Operator { get; private set; }
|
public ConditionOperator? Operator { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the currently used instance of the left data model
|
/// Gets the path of the left property
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public DataModel LeftDataModel { get; private set; }
|
public DataModelPath? LeftPath { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the path of the left property in the <see cref="LeftDataModel" />
|
/// Gets the path of the right property
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string LeftPropertyPath { get; private set; }
|
public DataModelPath? RightPath { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the currently used instance of the right data model
|
|
||||||
/// </summary>
|
|
||||||
public DataModel RightDataModel { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the path of the right property in the <see cref="RightDataModel" />
|
|
||||||
/// </summary>
|
|
||||||
public string RightPropertyPath { get; private set; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the right static value, only used it <see cref="PredicateType" /> is
|
/// Gets the right static value, only used it <see cref="PredicateType" /> is
|
||||||
/// <see cref="ProfileRightSideType.Static" />
|
/// <see cref="ProfileRightSideType.Static" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object RightStaticValue { get; private set; }
|
public object? RightStaticValue { get; private set; }
|
||||||
|
|
||||||
public Func<object, object> LeftSideAccessor { get; set; }
|
|
||||||
public Func<object, object> RightSideAccessor { get; set; }
|
|
||||||
|
|
||||||
internal DataModelConditionPredicateEntity Entity { get; set; }
|
internal DataModelConditionPredicateEntity Entity { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the left side of the predicate
|
/// Updates the left side of the predicate
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dataModel">The data model of the left side value</param>
|
|
||||||
/// <param name="path">The path pointing to the left side value inside the data model</param>
|
/// <param name="path">The path pointing to the left side value inside the data model</param>
|
||||||
public void UpdateLeftSide(DataModel dataModel, string path)
|
public void UpdateLeftSide(DataModelPath? path)
|
||||||
{
|
{
|
||||||
if (dataModel != null && path == null)
|
if (path != null && !path.IsValid)
|
||||||
throw new ArtemisCoreException("If a data model is provided, a path is also required");
|
throw new ArtemisCoreException("Cannot update left side of predicate to an invalid path");
|
||||||
if (dataModel == null && path != null)
|
|
||||||
throw new ArtemisCoreException("If path is provided, a data model is also required");
|
|
||||||
|
|
||||||
if (dataModel != null)
|
LeftPath?.Dispose();
|
||||||
{
|
LeftPath = path != null ? new DataModelPath(path) : null;
|
||||||
if (!dataModel.ContainsPath(path))
|
|
||||||
throw new ArtemisCoreException($"Data model of type {dataModel.GetType().Name} does not contain a property at path '{path}'");
|
|
||||||
}
|
|
||||||
|
|
||||||
LeftDataModel = dataModel;
|
|
||||||
LeftPropertyPath = path;
|
|
||||||
|
|
||||||
ValidateOperator();
|
ValidateOperator();
|
||||||
ValidateRightSide();
|
ValidateRightSide();
|
||||||
|
|
||||||
CreateAccessors();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the right side of the predicate, makes the predicate dynamic and re-compiles the expression
|
/// Updates the right side of the predicate, makes the predicate dynamic and re-compiles the expression
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dataModel">The data model of the right side value</param>
|
|
||||||
/// <param name="path">The path pointing to the right side value inside the data model</param>
|
/// <param name="path">The path pointing to the right side value inside the data model</param>
|
||||||
public void UpdateRightSide(DataModel dataModel, string path)
|
public void UpdateRightSideDynamic(DataModelPath? path)
|
||||||
{
|
{
|
||||||
if (dataModel != null && path == null)
|
if (path != null && !path.IsValid)
|
||||||
throw new ArtemisCoreException("If a data model is provided, a path is also required");
|
throw new ArtemisCoreException("Cannot update right side of predicate to an invalid path");
|
||||||
if (dataModel == null && path != null)
|
|
||||||
throw new ArtemisCoreException("If path is provided, a data model is also required");
|
|
||||||
|
|
||||||
if (dataModel != null)
|
RightPath?.Dispose();
|
||||||
{
|
RightPath = path != null ? new DataModelPath(path) : null;
|
||||||
if (!dataModel.ContainsPath(path))
|
|
||||||
throw new ArtemisCoreException($"Data model of type {dataModel.GetType().Name} does not contain a property at path '{path}'");
|
|
||||||
}
|
|
||||||
|
|
||||||
PredicateType = ProfileRightSideType.Dynamic;
|
PredicateType = ProfileRightSideType.Dynamic;
|
||||||
RightDataModel = dataModel;
|
|
||||||
RightPropertyPath = path;
|
|
||||||
|
|
||||||
CreateAccessors();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the right side of the predicate, makes the predicate static and re-compiles the expression
|
/// Updates the right side of the predicate, makes the predicate static and re-compiles the expression
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="staticValue">The right side value to use</param>
|
/// <param name="staticValue">The right side value to use</param>
|
||||||
public void UpdateRightSide(object staticValue)
|
public void UpdateRightSideStatic(object? staticValue)
|
||||||
{
|
{
|
||||||
PredicateType = ProfileRightSideType.Static;
|
PredicateType = ProfileRightSideType.Static;
|
||||||
RightDataModel = null;
|
RightPath?.Dispose();
|
||||||
RightPropertyPath = null;
|
RightPath = null;
|
||||||
|
|
||||||
// If the left side is empty simply apply the value, any validation will wait
|
// If the left side is empty simply apply the value, any validation will wait
|
||||||
if (LeftDataModel == null)
|
if (LeftPath == null || !LeftPath.IsValid)
|
||||||
{
|
{
|
||||||
RightStaticValue = staticValue;
|
RightStaticValue = staticValue;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Type leftSideType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
|
// If the left path is valid we can expect a type
|
||||||
|
Type leftSideType = LeftPath.GetPropertyType()!;
|
||||||
|
|
||||||
// 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() == leftSideType)
|
||||||
@ -159,63 +124,77 @@ namespace Artemis.Core
|
|||||||
RightStaticValue = Activator.CreateInstance(leftSideType);
|
RightStaticValue = Activator.CreateInstance(leftSideType);
|
||||||
else
|
else
|
||||||
RightStaticValue = null;
|
RightStaticValue = null;
|
||||||
|
|
||||||
CreateAccessors();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 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(ConditionOperator? conditionOperator)
|
||||||
{
|
{
|
||||||
// Calling CreateExpression will clear compiled expressions
|
// Calling CreateExpression will clear compiled expressions
|
||||||
if (conditionOperator == null)
|
if (conditionOperator == null)
|
||||||
{
|
{
|
||||||
Operator = null;
|
Operator = null;
|
||||||
CreateAccessors();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// No need to clear compiled expressions, without a left data model they are already null
|
// No need to clear compiled expressions, without a left data model they are already null
|
||||||
if (LeftDataModel == null)
|
if (LeftPath == null || !LeftPath.IsValid)
|
||||||
{
|
{
|
||||||
Operator = conditionOperator;
|
Operator = conditionOperator;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Type leftType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
|
Type leftType = LeftPath.GetPropertyType()!;
|
||||||
if (!conditionOperator.SupportsType(leftType))
|
if (!conditionOperator.SupportsType(leftType))
|
||||||
{
|
|
||||||
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;
|
||||||
CreateAccessors();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public override bool Evaluate()
|
public override bool Evaluate()
|
||||||
{
|
{
|
||||||
if (Operator == null || LeftSideAccessor == null || PredicateType != ProfileRightSideType.Static && RightSideAccessor == null)
|
if (Operator == null || LeftPath == null || !LeftPath.IsValid)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
// Compare with a static value
|
// Compare with a static value
|
||||||
if (PredicateType == ProfileRightSideType.Static)
|
if (PredicateType == ProfileRightSideType.Static)
|
||||||
{
|
{
|
||||||
object leftSideValue = LeftSideAccessor(LeftDataModel);
|
object? leftSideValue = LeftPath.GetValue();
|
||||||
if (leftSideValue.GetType().IsValueType && RightStaticValue == null)
|
if (leftSideValue != null && leftSideValue.GetType().IsValueType && RightStaticValue == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
return Operator.Evaluate(leftSideValue, RightStaticValue);
|
return Operator.Evaluate(leftSideValue, RightStaticValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Compare with dynamic values
|
if (RightPath == null || !RightPath.IsValid)
|
||||||
if (PredicateType == ProfileRightSideType.Dynamic)
|
return false;
|
||||||
return Operator.Evaluate(LeftSideAccessor(LeftDataModel), RightSideAccessor(RightDataModel));
|
|
||||||
|
|
||||||
return false;
|
// Compare with dynamic values
|
||||||
|
return Operator.Evaluate(LeftPath.GetValue(), RightPath.GetValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public override string ToString()
|
||||||
|
{
|
||||||
|
if (PredicateType == ProfileRightSideType.Dynamic)
|
||||||
|
return $"[Dynamic] {LeftPath} {Operator.Description} {RightPath}";
|
||||||
|
return $"[Static] {LeftPath} {Operator.Description} {RightStaticValue}";
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
ConditionOperatorStore.ConditionOperatorAdded -= ConditionOperatorStoreOnConditionOperatorAdded;
|
||||||
|
ConditionOperatorStore.ConditionOperatorRemoved -= ConditionOperatorStoreOnConditionOperatorRemoved;
|
||||||
|
|
||||||
|
LeftPath?.Dispose();
|
||||||
|
RightPath?.Dispose();
|
||||||
|
|
||||||
|
base.Dispose(disposing);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
@ -224,68 +203,53 @@ namespace Artemis.Core
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public override string ToString()
|
|
||||||
{
|
|
||||||
if (PredicateType == ProfileRightSideType.Dynamic)
|
|
||||||
return $"[Dynamic] {LeftPropertyPath} {Operator.Description} {RightPropertyPath}";
|
|
||||||
return $"[Static] {LeftPropertyPath} {Operator.Description} {RightStaticValue}";
|
|
||||||
}
|
|
||||||
|
|
||||||
internal override void Save()
|
internal override void Save()
|
||||||
{
|
{
|
||||||
Entity.PredicateType = (int) PredicateType;
|
Entity.PredicateType = (int) PredicateType;
|
||||||
Entity.LeftDataModelGuid = LeftDataModel?.PluginInfo?.Guid;
|
|
||||||
Entity.LeftPropertyPath = LeftPropertyPath;
|
|
||||||
|
|
||||||
Entity.RightDataModelGuid = RightDataModel?.PluginInfo?.Guid;
|
LeftPath?.Save();
|
||||||
Entity.RightPropertyPath = RightPropertyPath;
|
Entity.LeftPath = LeftPath?.Entity;
|
||||||
|
RightPath?.Save();
|
||||||
|
Entity.RightPath = RightPath?.Entity;
|
||||||
|
|
||||||
Entity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue);
|
Entity.RightStaticValue = JsonConvert.SerializeObject(RightStaticValue);
|
||||||
|
|
||||||
Entity.OperatorPluginGuid = Operator?.PluginInfo?.Guid;
|
if (Operator != null)
|
||||||
Entity.OperatorType = Operator?.GetType().Name;
|
{
|
||||||
|
Entity.OperatorPluginGuid = Operator.PluginInfo.Guid;
|
||||||
|
Entity.OperatorType = Operator.GetType().Name;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void Initialize()
|
internal void Initialize()
|
||||||
{
|
{
|
||||||
DataModelStore.DataModelAdded += DataModelStoreOnDataModelAdded;
|
|
||||||
DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved;
|
|
||||||
ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded;
|
ConditionOperatorStore.ConditionOperatorAdded += ConditionOperatorStoreOnConditionOperatorAdded;
|
||||||
ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved;
|
ConditionOperatorStore.ConditionOperatorRemoved += ConditionOperatorStoreOnConditionOperatorRemoved;
|
||||||
|
|
||||||
// Left side
|
// Left side
|
||||||
if (Entity.LeftDataModelGuid != null)
|
if (Entity.LeftPath != null)
|
||||||
{
|
LeftPath = new DataModelPath(null, Entity.LeftPath);
|
||||||
DataModel dataModel = DataModelStore.Get(Entity.LeftDataModelGuid.Value)?.DataModel;
|
|
||||||
if (dataModel != null && dataModel.ContainsPath(Entity.LeftPropertyPath))
|
|
||||||
UpdateLeftSide(dataModel, Entity.LeftPropertyPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Operator
|
// Operator
|
||||||
if (Entity.OperatorPluginGuid != null)
|
if (Entity.OperatorPluginGuid != null)
|
||||||
{
|
{
|
||||||
ConditionOperator conditionOperator = ConditionOperatorStore.Get(Entity.OperatorPluginGuid.Value, Entity.OperatorType)?.ConditionOperator;
|
ConditionOperator? conditionOperator = ConditionOperatorStore.Get(Entity.OperatorPluginGuid.Value, Entity.OperatorType)?.ConditionOperator;
|
||||||
if (conditionOperator != null)
|
if (conditionOperator != null)
|
||||||
UpdateOperator(conditionOperator);
|
UpdateOperator(conditionOperator);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Right side dynamic
|
// Right side dynamic
|
||||||
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightDataModelGuid != null)
|
if (PredicateType == ProfileRightSideType.Dynamic && Entity.RightPath != null)
|
||||||
{
|
RightPath = new DataModelPath(null, Entity.RightPath);
|
||||||
DataModel dataModel = DataModelStore.Get(Entity.RightDataModelGuid.Value)?.DataModel;
|
|
||||||
if (dataModel != null && dataModel.ContainsPath(Entity.RightPropertyPath))
|
|
||||||
UpdateRightSide(dataModel, Entity.RightPropertyPath);
|
|
||||||
}
|
|
||||||
// Right side static
|
// Right side static
|
||||||
else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null)
|
else if (PredicateType == ProfileRightSideType.Static && Entity.RightStaticValue != null)
|
||||||
{
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (LeftDataModel != null)
|
if (LeftPath != null && LeftPath.IsValid)
|
||||||
{
|
{
|
||||||
// Use the left side type so JSON.NET has a better idea what to do
|
// Use the left side type so JSON.NET has a better idea what to do
|
||||||
Type leftSideType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
|
Type leftSideType = LeftPath.GetPropertyType()!;
|
||||||
object rightSideValue;
|
object? rightSideValue;
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
@ -298,12 +262,12 @@ namespace Artemis.Core
|
|||||||
rightSideValue = Activator.CreateInstance(leftSideType);
|
rightSideValue = Activator.CreateInstance(leftSideType);
|
||||||
}
|
}
|
||||||
|
|
||||||
UpdateRightSide(rightSideValue);
|
UpdateRightSideStatic(rightSideValue);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
// Hope for the best...
|
// Hope for the best...
|
||||||
UpdateRightSide(JsonConvert.DeserializeObject(Entity.RightStaticValue));
|
UpdateRightSideStatic(JsonConvert.DeserializeObject(Entity.RightStaticValue));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (JsonReaderException)
|
catch (JsonReaderException)
|
||||||
@ -311,7 +275,6 @@ namespace Artemis.Core
|
|||||||
// ignored
|
// ignored
|
||||||
// TODO: Some logging would be nice
|
// TODO: Some logging would be nice
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal override DataModelConditionPartEntity GetEntity()
|
internal override DataModelConditionPartEntity GetEntity()
|
||||||
@ -319,119 +282,47 @@ namespace Artemis.Core
|
|||||||
return Entity;
|
return Entity;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateAccessors()
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
CreateDynamicAccessors();
|
|
||||||
else
|
|
||||||
CreateStaticExpression();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ValidateOperator()
|
private void ValidateOperator()
|
||||||
{
|
{
|
||||||
if (LeftDataModel == null || Operator == null)
|
if (LeftPath == null || !LeftPath.IsValid || Operator == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Type leftType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
|
Type leftType = LeftPath.GetPropertyType()!;
|
||||||
if (!Operator.SupportsType(leftType))
|
if (!Operator.SupportsType(leftType))
|
||||||
Operator = null;
|
Operator = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ValidateRightSide()
|
private void ValidateRightSide()
|
||||||
{
|
{
|
||||||
Type leftSideType = LeftDataModel.GetTypeAtPath(LeftPropertyPath);
|
Type? leftType = LeftPath?.GetPropertyType();
|
||||||
if (PredicateType == ProfileRightSideType.Dynamic)
|
if (PredicateType == ProfileRightSideType.Dynamic)
|
||||||
{
|
{
|
||||||
if (RightDataModel == null)
|
if (RightPath == null || !RightPath.IsValid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Type rightSideType = RightDataModel.GetTypeAtPath(RightPropertyPath);
|
Type rightSideType = RightPath.GetPropertyType()!;
|
||||||
if (!leftSideType.IsCastableFrom(rightSideType))
|
if (leftType != null && !leftType.IsCastableFrom(rightSideType))
|
||||||
UpdateRightSide(null, null);
|
UpdateRightSideDynamic(null);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (RightStaticValue != null && leftSideType.IsCastableFrom(RightStaticValue.GetType()))
|
if (RightStaticValue != null && (leftType == null || leftType.IsCastableFrom(RightStaticValue.GetType())))
|
||||||
UpdateRightSide(RightStaticValue);
|
UpdateRightSideStatic(RightStaticValue);
|
||||||
else
|
else
|
||||||
UpdateRightSide(null);
|
UpdateRightSideStatic(null);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void CreateDynamicAccessors()
|
|
||||||
{
|
|
||||||
if (LeftDataModel == null || RightDataModel == null || Operator == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
Expression leftSideAccessor = ExpressionUtilities.CreateDataModelAccessor(LeftDataModel, LeftPropertyPath, "left", out ParameterExpression leftSideParameter);
|
|
||||||
Expression rightSideAccessor = ExpressionUtilities.CreateDataModelAccessor(RightDataModel, RightPropertyPath, "right", out ParameterExpression rightSideParameter);
|
|
||||||
|
|
||||||
// A conversion may be required if the types differ
|
|
||||||
// This can cause issues if the DataModelConditionOperator 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);
|
|
||||||
|
|
||||||
LeftSideAccessor = Expression.Lambda<Func<object, object>>(leftSideAccessor, leftSideParameter).Compile();
|
|
||||||
RightSideAccessor = Expression.Lambda<Func<object, object>>(rightSideAccessor, rightSideParameter).Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CreateStaticExpression()
|
|
||||||
{
|
|
||||||
if (LeftDataModel == null || Operator == null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
UnaryExpression leftSideAccessor = Expression.Convert(
|
|
||||||
ExpressionUtilities.CreateDataModelAccessor(LeftDataModel, LeftPropertyPath, "left", out ParameterExpression leftSideParameter),
|
|
||||||
typeof(object)
|
|
||||||
);
|
|
||||||
|
|
||||||
// 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)
|
|
||||||
return;
|
|
||||||
|
|
||||||
LeftSideAccessor = Expression.Lambda<Func<object, object>>(leftSideAccessor, leftSideParameter).Compile();
|
|
||||||
RightSideAccessor = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#region Event handlers
|
#region Event handlers
|
||||||
|
|
||||||
private void DataModelStoreOnDataModelAdded(object sender, DataModelStoreEvent e)
|
private void ConditionOperatorStoreOnConditionOperatorAdded(object? sender, ConditionOperatorStoreEvent e)
|
||||||
{
|
|
||||||
DataModel dataModel = e.Registration.DataModel;
|
|
||||||
if (dataModel.PluginInfo.Guid == Entity.LeftDataModelGuid && dataModel.ContainsPath(Entity.LeftPropertyPath))
|
|
||||||
UpdateLeftSide(dataModel, Entity.LeftPropertyPath);
|
|
||||||
if (dataModel.PluginInfo.Guid == Entity.RightDataModelGuid && dataModel.ContainsPath(Entity.RightPropertyPath))
|
|
||||||
UpdateRightSide(dataModel, Entity.RightPropertyPath);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DataModelStoreOnDataModelRemoved(object sender, DataModelStoreEvent e)
|
|
||||||
{
|
|
||||||
if (LeftDataModel == e.Registration.DataModel)
|
|
||||||
{
|
|
||||||
LeftSideAccessor = null;
|
|
||||||
LeftDataModel = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (RightDataModel == e.Registration.DataModel)
|
|
||||||
{
|
|
||||||
RightSideAccessor = null;
|
|
||||||
RightDataModel = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ConditionOperatorStoreOnConditionOperatorAdded(object sender, ConditionOperatorStoreEvent e)
|
|
||||||
{
|
{
|
||||||
ConditionOperator conditionOperator = e.Registration.ConditionOperator;
|
ConditionOperator 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);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConditionOperatorStoreOnConditionOperatorRemoved(object sender, ConditionOperatorStoreEvent e)
|
private void ConditionOperatorStoreOnConditionOperatorRemoved(object? sender, ConditionOperatorStoreEvent e)
|
||||||
{
|
{
|
||||||
if (e.Registration.ConditionOperator != Operator)
|
if (e.Registration.ConditionOperator != Operator)
|
||||||
return;
|
return;
|
||||||
@ -440,16 +331,5 @@ namespace Artemis.Core
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
protected override void Dispose(bool disposing)
|
|
||||||
{
|
|
||||||
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
|
|
||||||
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
|
|
||||||
ConditionOperatorStore.ConditionOperatorAdded -= ConditionOperatorStoreOnConditionOperatorAdded;
|
|
||||||
ConditionOperatorStore.ConditionOperatorRemoved -= ConditionOperatorStoreOnConditionOperatorRemoved;
|
|
||||||
|
|
||||||
base.Dispose(disposing);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -0,0 +1,30 @@
|
|||||||
|
using System;
|
||||||
|
using Artemis.Core.DataModelExpansions;
|
||||||
|
|
||||||
|
namespace Artemis.Core
|
||||||
|
{
|
||||||
|
internal class ListPredicateWrapperDataModel<T> : ListPredicateWrapperDataModel
|
||||||
|
{
|
||||||
|
public T Value => (UntypedValue is T typedValue ? typedValue : default)!;
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract class ListPredicateWrapperDataModel : DataModel
|
||||||
|
{
|
||||||
|
internal ListPredicateWrapperDataModel()
|
||||||
|
{
|
||||||
|
PluginInfo = Constants.CorePluginInfo;
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataModelIgnore]
|
||||||
|
public object? UntypedValue { get; set; }
|
||||||
|
|
||||||
|
public static ListPredicateWrapperDataModel Create(Type type)
|
||||||
|
{
|
||||||
|
object? instance = Activator.CreateInstance(typeof(ListPredicateWrapperDataModel<>).MakeGenericType(type));
|
||||||
|
if (instance == null)
|
||||||
|
throw new ArtemisCoreException($"Failed to create an instance of ListPredicateWrapperDataModel<T> for type {type.Name}");
|
||||||
|
|
||||||
|
return (ListPredicateWrapperDataModel) instance;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -190,7 +190,7 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="dataModel">The data model of the parameter</param>
|
/// <param name="dataModel">The data model of the parameter</param>
|
||||||
/// <param name="path">The path pointing to the parameter inside the data model</param>
|
/// <param name="path">The path pointing to the parameter inside the data model</param>
|
||||||
public void UpdateParameter(DataModel dataModel, string path)
|
public void UpdateParameter(DataModel? dataModel, string? path)
|
||||||
{
|
{
|
||||||
if (_disposed)
|
if (_disposed)
|
||||||
throw new ObjectDisposedException("DataBindingModifier");
|
throw new ObjectDisposedException("DataBindingModifier");
|
||||||
|
|||||||
@ -14,6 +14,7 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class DataModelPath : IStorageModel, IDisposable
|
public class DataModelPath : IStorageModel, IDisposable
|
||||||
{
|
{
|
||||||
|
private bool _disposed;
|
||||||
private readonly LinkedList<DataModelPathSegment> _segments;
|
private readonly LinkedList<DataModelPathSegment> _segments;
|
||||||
private Expression<Func<object, object>>? _accessorLambda;
|
private Expression<Func<object, object>>? _accessorLambda;
|
||||||
|
|
||||||
@ -21,13 +22,11 @@ namespace Artemis.Core
|
|||||||
/// Creates a new instance of the <see cref="DataModelPath" /> class pointing directly to the target
|
/// Creates a new instance of the <see cref="DataModelPath" /> class pointing directly to the target
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="target">The target at which this path starts</param>
|
/// <param name="target">The target at which this path starts</param>
|
||||||
public DataModelPath(object target)
|
public DataModelPath(DataModel target)
|
||||||
{
|
{
|
||||||
Target = target ?? throw new ArgumentNullException(nameof(target));
|
Target = target ?? throw new ArgumentNullException(nameof(target));
|
||||||
Path = "";
|
Path = "";
|
||||||
Entity = new DataModelPathEntity();
|
Entity = new DataModelPathEntity();
|
||||||
if (Target is DataModel dataModel)
|
|
||||||
DataModelGuid = dataModel.PluginInfo.Guid;
|
|
||||||
|
|
||||||
_segments = new LinkedList<DataModelPathSegment>();
|
_segments = new LinkedList<DataModelPathSegment>();
|
||||||
|
|
||||||
@ -41,13 +40,11 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="target">The target at which this path starts</param>
|
/// <param name="target">The target at which this path starts</param>
|
||||||
/// <param name="path">A point-separated path</param>
|
/// <param name="path">A point-separated path</param>
|
||||||
public DataModelPath(object target, string path)
|
public DataModelPath(DataModel target, string path)
|
||||||
{
|
{
|
||||||
Target = target ?? throw new ArgumentNullException(nameof(target));
|
Target = target ?? throw new ArgumentNullException(nameof(target));
|
||||||
Path = path ?? throw new ArgumentNullException(nameof(path));
|
Path = path ?? throw new ArgumentNullException(nameof(path));
|
||||||
Entity = new DataModelPathEntity();
|
Entity = new DataModelPathEntity();
|
||||||
if (Target is DataModel dataModel)
|
|
||||||
DataModelGuid = dataModel.PluginInfo.Guid;
|
|
||||||
|
|
||||||
_segments = new LinkedList<DataModelPathSegment>();
|
_segments = new LinkedList<DataModelPathSegment>();
|
||||||
|
|
||||||
@ -56,9 +53,29 @@ namespace Artemis.Core
|
|||||||
SubscribeToDataModelStore();
|
SubscribeToDataModelStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
internal DataModelPath(object target, DataModelPathEntity entity)
|
/// <summary>
|
||||||
|
/// Creates a new instance of the <see cref="DataModelPath" /> class based on an existing path
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="dataModelPath">The path to base the new instance on</param>
|
||||||
|
public DataModelPath(DataModelPath dataModelPath)
|
||||||
{
|
{
|
||||||
Target = target!;
|
if (dataModelPath == null)
|
||||||
|
throw new ArgumentNullException(nameof(dataModelPath));
|
||||||
|
|
||||||
|
Target = dataModelPath.Target;
|
||||||
|
Path = dataModelPath.Path;
|
||||||
|
Entity = new DataModelPathEntity();
|
||||||
|
|
||||||
|
_segments = new LinkedList<DataModelPathSegment>();
|
||||||
|
|
||||||
|
Save();
|
||||||
|
Initialize();
|
||||||
|
SubscribeToDataModelStore();
|
||||||
|
}
|
||||||
|
|
||||||
|
internal DataModelPath(DataModel? target, DataModelPathEntity entity)
|
||||||
|
{
|
||||||
|
Target = target;
|
||||||
Path = entity.Path;
|
Path = entity.Path;
|
||||||
Entity = entity;
|
Entity = entity;
|
||||||
|
|
||||||
@ -72,14 +89,12 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the data model at which this path starts
|
/// Gets the data model at which this path starts
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public object? Target { get; private set; }
|
public DataModel? Target { get; private set; }
|
||||||
|
|
||||||
internal DataModelPathEntity Entity { get; }
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the data model GUID of the <see cref="Target" /> if it is a <see cref="DataModel" />
|
/// Gets the data model GUID of the <see cref="Target" /> if it is a <see cref="DataModel" />
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public Guid? DataModelGuid { get; private set; }
|
public Guid? DataModelGuid => Target?.PluginInfo.Guid;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the point-separated path associated with this <see cref="DataModelPath" />
|
/// Gets the point-separated path associated with this <see cref="DataModelPath" />
|
||||||
@ -99,7 +114,10 @@ namespace Artemis.Core
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets a boolean indicating whether this data model path points to a list
|
/// Gets a boolean indicating whether this data model path points to a list
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool PointsToList => Segments.LastOrDefault()?.GetPropertyType() != null && typeof(IList).IsAssignableFrom(Segments.LastOrDefault()?.GetPropertyType());
|
public bool PointsToList => Segments.LastOrDefault()?.GetPropertyType() != null &&
|
||||||
|
typeof(IList).IsAssignableFrom(Segments.LastOrDefault()?.GetPropertyType());
|
||||||
|
|
||||||
|
internal DataModelPathEntity Entity { get; }
|
||||||
|
|
||||||
internal Func<object, object>? Accessor { get; private set; }
|
internal Func<object, object>? Accessor { get; private set; }
|
||||||
|
|
||||||
@ -108,6 +126,9 @@ namespace Artemis.Core
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public object? GetValue()
|
public object? GetValue()
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DataModelPath");
|
||||||
|
|
||||||
if (_accessorLambda == null || Target == null)
|
if (_accessorLambda == null || Target == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -123,6 +144,9 @@ namespace Artemis.Core
|
|||||||
/// <returns>If static, the property info. If dynamic, <c>null</c></returns>
|
/// <returns>If static, the property info. If dynamic, <c>null</c></returns>
|
||||||
public PropertyInfo? GetPropertyInfo()
|
public PropertyInfo? GetPropertyInfo()
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DataModelPath");
|
||||||
|
|
||||||
return Segments.LastOrDefault()?.GetPropertyInfo();
|
return Segments.LastOrDefault()?.GetPropertyInfo();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,6 +156,9 @@ namespace Artemis.Core
|
|||||||
/// <returns>If possible, the property type</returns>
|
/// <returns>If possible, the property type</returns>
|
||||||
public Type? GetPropertyType()
|
public Type? GetPropertyType()
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DataModelPath");
|
||||||
|
|
||||||
return Segments.LastOrDefault()?.GetPropertyType();
|
return Segments.LastOrDefault()?.GetPropertyType();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -141,6 +168,9 @@ namespace Artemis.Core
|
|||||||
/// <returns>If found, the data model property description</returns>
|
/// <returns>If found, the data model property description</returns>
|
||||||
public DataModelPropertyAttribute? GetPropertyDescription()
|
public DataModelPropertyAttribute? GetPropertyDescription()
|
||||||
{
|
{
|
||||||
|
if (_disposed)
|
||||||
|
throw new ObjectDisposedException("DataModelPath");
|
||||||
|
|
||||||
return Segments.LastOrDefault()?.GetPropertyDescription();
|
return Segments.LastOrDefault()?.GetPropertyDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -177,7 +207,9 @@ namespace Artemis.Core
|
|||||||
for (int index = 0; index < segments.Length; index++)
|
for (int index = 0; index < segments.Length; index++)
|
||||||
{
|
{
|
||||||
string identifier = segments[index];
|
string identifier = segments[index];
|
||||||
LinkedListNode<DataModelPathSegment> node = _segments.AddLast(new DataModelPathSegment(this, identifier, string.Join('.', segments.Take(index + 1))));
|
LinkedListNode<DataModelPathSegment> node = _segments.AddLast(
|
||||||
|
new DataModelPathSegment(this, identifier, string.Join('.', segments.Take(index + 1)))
|
||||||
|
);
|
||||||
node.Value.Node = node;
|
node.Value.Node = node;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -215,32 +247,13 @@ namespace Artemis.Core
|
|||||||
DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved;
|
DataModelStore.DataModelRemoved += DataModelStoreOnDataModelRemoved;
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Storage
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Load()
|
|
||||||
{
|
|
||||||
Path = Entity.Path;
|
|
||||||
DataModelGuid = Entity.DataModelGuid;
|
|
||||||
|
|
||||||
if (Target == null && Entity.DataModelGuid != null)
|
|
||||||
Target = DataModelStore.Get(Entity.DataModelGuid.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <inheritdoc />
|
|
||||||
public void Save()
|
|
||||||
{
|
|
||||||
Entity.Path = Path;
|
|
||||||
Entity.DataModelGuid = DataModelGuid;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#region IDisposable
|
#region IDisposable
|
||||||
|
|
||||||
/// <inheritdoc />
|
/// <inheritdoc />
|
||||||
public void Dispose()
|
public void Dispose()
|
||||||
{
|
{
|
||||||
|
_disposed = true;
|
||||||
|
|
||||||
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
|
DataModelStore.DataModelAdded -= DataModelStoreOnDataModelAdded;
|
||||||
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
|
DataModelStore.DataModelRemoved -= DataModelStoreOnDataModelRemoved;
|
||||||
|
|
||||||
@ -249,11 +262,35 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
#region Storage
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Load()
|
||||||
|
{
|
||||||
|
Path = Entity.Path;
|
||||||
|
|
||||||
|
if (Target == null && Entity.DataModelGuid != null)
|
||||||
|
Target = DataModelStore.Get(Entity.DataModelGuid.Value)?.DataModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <inheritdoc />
|
||||||
|
public void Save()
|
||||||
|
{
|
||||||
|
// Do not save an invalid state
|
||||||
|
if (!IsValid)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Entity.Path = Path;
|
||||||
|
Entity.DataModelGuid = DataModelGuid;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
#region Event handlers
|
#region Event handlers
|
||||||
|
|
||||||
private void DataModelStoreOnDataModelAdded(object? sender, DataModelStoreEvent e)
|
private void DataModelStoreOnDataModelAdded(object? sender, DataModelStoreEvent e)
|
||||||
{
|
{
|
||||||
if (e.Registration.DataModel.PluginInfo.Guid != DataModelGuid)
|
if (e.Registration.DataModel.PluginInfo.Guid != Entity.DataModelGuid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Target = e.Registration.DataModel;
|
Target = e.Registration.DataModel;
|
||||||
@ -262,7 +299,7 @@ namespace Artemis.Core
|
|||||||
|
|
||||||
private void DataModelStoreOnDataModelRemoved(object? sender, DataModelStoreEvent e)
|
private void DataModelStoreOnDataModelRemoved(object? sender, DataModelStoreEvent e)
|
||||||
{
|
{
|
||||||
if (e.Registration.DataModel.PluginInfo.Guid != DataModelGuid)
|
if (e.Registration.DataModel.PluginInfo.Guid != Entity.DataModelGuid)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Target = null;
|
Target = null;
|
||||||
|
|||||||
@ -137,10 +137,12 @@ namespace Artemis.Core.DataModelExpansions
|
|||||||
return value as T;
|
return value as T;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal bool ContainsPath(string path)
|
internal bool ContainsPath(string? path)
|
||||||
{
|
{
|
||||||
|
if (path == null)
|
||||||
|
return false;
|
||||||
string[] parts = path.Split('.');
|
string[] parts = path.Split('.');
|
||||||
Type current = GetType();
|
Type? current = GetType();
|
||||||
foreach (string part in parts)
|
foreach (string part in parts)
|
||||||
{
|
{
|
||||||
PropertyInfo? property = current?.GetProperty(part);
|
PropertyInfo? property = current?.GetProperty(part);
|
||||||
|
|||||||
@ -18,9 +18,9 @@ namespace Artemis.Core
|
|||||||
_logger.Warning(
|
_logger.Warning(
|
||||||
exception,
|
exception,
|
||||||
"Failed to deserialize display condition predicate {left} {operator} {right}",
|
"Failed to deserialize display condition predicate {left} {operator} {right}",
|
||||||
dataModelConditionPredicate.Entity.LeftPropertyPath,
|
dataModelConditionPredicate.Entity.LeftPath?.Path,
|
||||||
dataModelConditionPredicate.Entity.OperatorType,
|
dataModelConditionPredicate.Entity.OperatorType,
|
||||||
dataModelConditionPredicate.Entity.RightPropertyPath
|
dataModelConditionPredicate.Entity.RightPath?.Path
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -29,9 +29,9 @@ namespace Artemis.Core
|
|||||||
_logger.Warning(
|
_logger.Warning(
|
||||||
exception,
|
exception,
|
||||||
"Failed to deserialize display condition list predicate {list} => {left} {operator} {right}",
|
"Failed to deserialize display condition list predicate {list} => {left} {operator} {right}",
|
||||||
dataModelConditionPredicate.Entity.LeftPropertyPath,
|
dataModelConditionPredicate.Entity.LeftPath?.Path,
|
||||||
dataModelConditionPredicate.Entity.OperatorType,
|
dataModelConditionPredicate.Entity.OperatorType,
|
||||||
dataModelConditionPredicate.Entity.RightPropertyPath
|
dataModelConditionPredicate.Entity.RightPath?.Path
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -11,9 +11,7 @@ namespace Artemis.Storage.Entities.Profile.Conditions
|
|||||||
Children = new List<DataModelConditionPartEntity>();
|
Children = new List<DataModelConditionPartEntity>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public Guid? ListDataModelGuid { get; set; }
|
public DataModelPathEntity ListPath { get; set; }
|
||||||
public string ListPropertyPath { get; set; }
|
|
||||||
|
|
||||||
public int ListOperator { get; set; }
|
public int ListOperator { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6,16 +6,15 @@ namespace Artemis.Storage.Entities.Profile.Conditions
|
|||||||
public class DataModelConditionListPredicateEntity : DataModelConditionPartEntity
|
public class DataModelConditionListPredicateEntity : DataModelConditionPartEntity
|
||||||
{
|
{
|
||||||
public int PredicateType { get; set; }
|
public int PredicateType { get; set; }
|
||||||
|
|
||||||
public string LeftPropertyPath { get; set; }
|
|
||||||
|
|
||||||
public Guid? RightDataModelGuid { get; set; }
|
public DataModelPathEntity LeftPath { get; set; }
|
||||||
public string RightPropertyPath { get; set; }
|
public DataModelPathEntity RightPath { get; set; }
|
||||||
|
|
||||||
// Stored as a string to be able to control serialization and deserialization ourselves
|
// Stored as a string to be able to control serialization and deserialization ourselves
|
||||||
public string RightStaticValue { get; set; }
|
public string RightStaticValue { get; set; }
|
||||||
|
|
||||||
public string OperatorType { get; set; }
|
public string OperatorType { get; set; }
|
||||||
public Guid? OperatorPluginGuid { get; set; }
|
public Guid? OperatorPluginGuid { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6,16 +6,14 @@ namespace Artemis.Storage.Entities.Profile.Conditions
|
|||||||
public class DataModelConditionPredicateEntity : DataModelConditionPartEntity
|
public class DataModelConditionPredicateEntity : DataModelConditionPartEntity
|
||||||
{
|
{
|
||||||
public int PredicateType { get; set; }
|
public int PredicateType { get; set; }
|
||||||
public Guid? LeftDataModelGuid { get; set; }
|
public DataModelPathEntity LeftPath { get; set; }
|
||||||
public string LeftPropertyPath { get; set; }
|
public DataModelPathEntity RightPath { get; set; }
|
||||||
|
|
||||||
public Guid? RightDataModelGuid { get; set; }
|
|
||||||
public string RightPropertyPath { get; set; }
|
|
||||||
|
|
||||||
public string OperatorType { get; set; }
|
public string OperatorType { get; set; }
|
||||||
public Guid? OperatorPluginGuid { get; set; }
|
public Guid? OperatorPluginGuid { get; set; }
|
||||||
|
|
||||||
// Stored as a string to be able to control serialization and deserialization ourselves
|
// Stored as a string to be able to control serialization and deserialization ourselves
|
||||||
public string RightStaticValue { get; set; }
|
public string RightStaticValue { get; set; }
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -15,16 +15,12 @@ namespace Artemis.UI.Shared
|
|||||||
else
|
else
|
||||||
direction = (Parameters) Enum.Parse(typeof(Parameters), (string) parameter);
|
direction = (Parameters) Enum.Parse(typeof(Parameters), (string) parameter);
|
||||||
|
|
||||||
if (direction == Parameters.Normal)
|
if (value is string stringValue && string.IsNullOrWhiteSpace(stringValue))
|
||||||
{
|
value = null;
|
||||||
if (value == null)
|
|
||||||
return Visibility.Collapsed;
|
|
||||||
return Visibility.Visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (value == null)
|
if (direction == Parameters.Normal)
|
||||||
return Visibility.Visible;
|
return value == null ? Visibility.Collapsed : Visibility.Visible;
|
||||||
return Visibility.Collapsed;
|
return value == null ? Visibility.Visible : Visibility.Collapsed;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||||
xmlns:shared="clr-namespace:Artemis.UI.Shared"
|
xmlns:shared="clr-namespace:Artemis.UI.Shared"
|
||||||
xmlns:input="clr-namespace:Artemis.UI.Shared.Input"
|
xmlns:input="clr-namespace:Artemis.UI.Shared.Input"
|
||||||
|
xmlns:s="https://github.com/canton7/Stylet"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignHeight="450" d:DesignWidth="800"
|
d:DesignHeight="450" d:DesignWidth="800"
|
||||||
d:DataContext="{d:DesignInstance input:DataModelDynamicViewModel}">
|
d:DataContext="{d:DesignInstance input:DataModelDynamicViewModel}">
|
||||||
@ -30,7 +31,7 @@
|
|||||||
<Button Background="{Binding ButtonBrush}"
|
<Button Background="{Binding ButtonBrush}"
|
||||||
BorderBrush="{Binding ButtonBrush}"
|
BorderBrush="{Binding ButtonBrush}"
|
||||||
Style="{StaticResource DataModelConditionButton}"
|
Style="{StaticResource DataModelConditionButton}"
|
||||||
ToolTip="{Binding SelectedPropertyViewModel.DisplayPath}"
|
ToolTip="{Binding DisplayPath}"
|
||||||
IsEnabled="{Binding IsEnabled}"
|
IsEnabled="{Binding IsEnabled}"
|
||||||
HorizontalAlignment="Left"
|
HorizontalAlignment="Left"
|
||||||
Click="PropertyButton_OnClick">
|
Click="PropertyButton_OnClick">
|
||||||
@ -50,11 +51,17 @@
|
|||||||
</ContextMenu>
|
</ContextMenu>
|
||||||
</Button.ContextMenu>
|
</Button.ContextMenu>
|
||||||
<Grid>
|
<Grid>
|
||||||
<TextBlock Text="{Binding SelectedPropertyViewModel.PropertyDescription.Name}"
|
<Grid Visibility="{Binding IsValid,Converter={x:Static s:BoolToVisibilityConverter.Instance}, Mode=OneWay}">
|
||||||
Visibility="{Binding SelectedPropertyViewModel, Converter={StaticResource NullToVisibilityConverter}}" />
|
<TextBlock Text="{Binding DisplayValue}"
|
||||||
|
Visibility="{Binding DataModelPath, Converter={StaticResource NullToVisibilityConverter}}" />
|
||||||
|
<TextBlock FontStyle="Italic"
|
||||||
|
Visibility="{Binding DataModelPath, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}">
|
||||||
|
<Run Text="« " /><Run Text="{Binding Placeholder}" /><Run Text=" »" />
|
||||||
|
</TextBlock>
|
||||||
|
</Grid>
|
||||||
<TextBlock FontStyle="Italic"
|
<TextBlock FontStyle="Italic"
|
||||||
Visibility="{Binding SelectedPropertyViewModel, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}">
|
Visibility="{Binding IsValid, Converter={x:Static s:BoolToVisibilityConverter.InverseInstance}, Mode=OneWay}">
|
||||||
<Run Text="« " /><Run Text="{Binding Placeholder}" /><Run Text=" »" />
|
« Invalid »
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Button>
|
</Button>
|
||||||
|
|||||||
@ -1,8 +1,8 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Linq;
|
||||||
using System.Timers;
|
using System.Timers;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
using Artemis.Core.DataModelExpansions;
|
|
||||||
using Artemis.Core.Modules;
|
using Artemis.Core.Modules;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
@ -19,12 +19,12 @@ namespace Artemis.UI.Shared.Input
|
|||||||
private readonly Module _module;
|
private readonly Module _module;
|
||||||
private readonly Timer _updateTimer;
|
private readonly Timer _updateTimer;
|
||||||
private Brush _buttonBrush = new SolidColorBrush(Color.FromRgb(171, 71, 188));
|
private Brush _buttonBrush = new SolidColorBrush(Color.FromRgb(171, 71, 188));
|
||||||
|
private DataModelPath _dataModelPath;
|
||||||
private DataModelPropertiesViewModel _dataModelViewModel;
|
private DataModelPropertiesViewModel _dataModelViewModel;
|
||||||
|
private Type[] _filterTypes;
|
||||||
private bool _isDataModelViewModelOpen;
|
private bool _isDataModelViewModelOpen;
|
||||||
private bool _isEnabled = true;
|
private bool _isEnabled = true;
|
||||||
private string _placeholder = "Select a property";
|
private string _placeholder = "Select a property";
|
||||||
private DataModelVisualizationViewModel _selectedPropertyViewModel;
|
|
||||||
private Type[] _filterTypes;
|
|
||||||
|
|
||||||
internal DataModelDynamicViewModel(Module module, ISettingsService settingsService, IDataModelUIService dataModelUIService)
|
internal DataModelDynamicViewModel(Module module, ISettingsService settingsService, IDataModelUIService dataModelUIService)
|
||||||
{
|
{
|
||||||
@ -81,18 +81,31 @@ namespace Artemis.UI.Shared.Input
|
|||||||
set => SetAndNotify(ref _isDataModelViewModelOpen, value);
|
set => SetAndNotify(ref _isDataModelViewModelOpen, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataModelVisualizationViewModel SelectedPropertyViewModel
|
public DataModelPath DataModelPath
|
||||||
{
|
{
|
||||||
get => _selectedPropertyViewModel;
|
get => _dataModelPath;
|
||||||
set => SetAndNotify(ref _selectedPropertyViewModel, value);
|
private set
|
||||||
|
{
|
||||||
|
if (!SetAndNotify(ref _dataModelPath, value)) return;
|
||||||
|
NotifyOfPropertyChange(nameof(IsValid));
|
||||||
|
NotifyOfPropertyChange(nameof(DisplayValue));
|
||||||
|
NotifyOfPropertyChange(nameof(DisplayPath));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void PopulateSelectedPropertyViewModel(DataModel datamodel, string path)
|
public bool IsValid => DataModelPath?.IsValid ?? true;
|
||||||
|
public string DisplayValue => DataModelPath?.GetPropertyDescription()?.Name ?? DataModelPath?.Segments.LastOrDefault()?.Identifier;
|
||||||
|
|
||||||
|
public string DisplayPath
|
||||||
{
|
{
|
||||||
if (datamodel == null)
|
get
|
||||||
SelectedPropertyViewModel = null;
|
{
|
||||||
else
|
if (DataModelPath == null)
|
||||||
SelectedPropertyViewModel = DataModelViewModel.GetChildByPath(datamodel.PluginInfo.Guid, path);
|
return "Click to select a property";
|
||||||
|
if (!DataModelPath.IsValid)
|
||||||
|
return "Invalid path";
|
||||||
|
return string.Join(" › ", DataModelPath.Segments.Select(s => s.GetPropertyDescription()?.Name ?? s.Identifier));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ChangeDataModel(DataModelPropertiesViewModel dataModel)
|
public void ChangeDataModel(DataModelPropertiesViewModel dataModel)
|
||||||
@ -106,6 +119,12 @@ namespace Artemis.UI.Shared.Input
|
|||||||
DataModelViewModel.UpdateRequested += DataModelOnUpdateRequested;
|
DataModelViewModel.UpdateRequested += DataModelOnUpdateRequested;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void ChangeDataModelPath(DataModelPath dataModelPath)
|
||||||
|
{
|
||||||
|
DataModelPath?.Dispose();
|
||||||
|
DataModelPath = dataModelPath != null ? new DataModelPath(dataModelPath) : null;
|
||||||
|
}
|
||||||
|
|
||||||
private void Initialize()
|
private void Initialize()
|
||||||
{
|
{
|
||||||
// Get the data models
|
// Get the data models
|
||||||
@ -132,10 +151,18 @@ namespace Artemis.UI.Shared.Input
|
|||||||
if (!(context is DataModelVisualizationViewModel selected))
|
if (!(context is DataModelVisualizationViewModel selected))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
SelectedPropertyViewModel = selected;
|
ChangeDataModelPath(selected.DataModelPath);
|
||||||
OnPropertySelected(new DataModelInputDynamicEventArgs(selected));
|
OnPropertySelected(new DataModelInputDynamicEventArgs(DataModelPath));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_updateTimer.Stop();
|
||||||
|
_updateTimer.Dispose();
|
||||||
|
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
|
||||||
|
|
||||||
|
DataModelPath?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
#region Events
|
#region Events
|
||||||
|
|
||||||
@ -147,12 +174,5 @@ namespace Artemis.UI.Shared.Input
|
|||||||
}
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
_updateTimer.Stop();
|
|
||||||
_updateTimer.Dispose();
|
|
||||||
_updateTimer.Elapsed -= OnUpdateTimerOnElapsed;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -32,13 +32,7 @@
|
|||||||
Visibility="{Binding InputViewModel, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}">
|
Visibility="{Binding InputViewModel, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}">
|
||||||
<Grid>
|
<Grid>
|
||||||
<StackPanel Visibility="{Binding Value, Converter={StaticResource NullToVisibilityConverter}}" Orientation="Horizontal">
|
<StackPanel Visibility="{Binding Value, Converter={StaticResource NullToVisibilityConverter}}" Orientation="Horizontal">
|
||||||
<TextBlock FontWeight="Light"
|
|
||||||
Text="{Binding TargetDescription.Prefix}"
|
|
||||||
Visibility="{Binding TargetDescription.Prefix, Converter={StaticResource NullToVisibilityConverter}}" />
|
|
||||||
<ContentControl s:View.Model="{Binding DisplayViewModel}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
|
<ContentControl s:View.Model="{Binding DisplayViewModel}" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
|
||||||
<TextBlock FontWeight="Light"
|
|
||||||
Text="{Binding TargetDescription.Affix}"
|
|
||||||
Visibility="{Binding TargetDescription.Affix, Converter={StaticResource NullToVisibilityConverter}}" />
|
|
||||||
</StackPanel>
|
</StackPanel>
|
||||||
|
|
||||||
<TextBlock FontStyle="Italic" Visibility="{Binding Value, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}">
|
<TextBlock FontStyle="Italic" Visibility="{Binding Value, Converter={StaticResource NullToVisibilityConverter}, ConverterParameter=Inverted}">
|
||||||
|
|||||||
@ -23,14 +23,15 @@ namespace Artemis.UI.Shared.Input
|
|||||||
private object _value;
|
private object _value;
|
||||||
private bool _isEnabled;
|
private bool _isEnabled;
|
||||||
|
|
||||||
internal DataModelStaticViewModel(Type targetType, IDataModelUIService dataModelUIService)
|
internal DataModelStaticViewModel(Type targetType, DataModelPropertyAttribute targetDescription, IDataModelUIService dataModelUIService)
|
||||||
{
|
{
|
||||||
_dataModelUIService = dataModelUIService;
|
_dataModelUIService = dataModelUIService;
|
||||||
_rootView = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
|
_rootView = Application.Current.Windows.OfType<Window>().SingleOrDefault(x => x.IsActive);
|
||||||
|
|
||||||
TargetType = targetType;
|
TargetType = targetType;
|
||||||
|
TargetDescription = targetDescription;
|
||||||
IsEnabled = TargetType != null;
|
IsEnabled = TargetType != null;
|
||||||
DisplayViewModel = _dataModelUIService.GetDataModelDisplayViewModel(TargetType ?? typeof(object), true);
|
DisplayViewModel = _dataModelUIService.GetDataModelDisplayViewModel(TargetType ?? typeof(object), TargetDescription, true);
|
||||||
|
|
||||||
if (_rootView != null)
|
if (_rootView != null)
|
||||||
{
|
{
|
||||||
@ -117,7 +118,7 @@ namespace Artemis.UI.Shared.Input
|
|||||||
public void UpdateTargetType(Type target)
|
public void UpdateTargetType(Type target)
|
||||||
{
|
{
|
||||||
TargetType = target;
|
TargetType = target;
|
||||||
DisplayViewModel = _dataModelUIService.GetDataModelDisplayViewModel(TargetType ?? typeof(object), true);
|
DisplayViewModel = _dataModelUIService.GetDataModelDisplayViewModel(TargetType ?? typeof(object), TargetDescription, true);
|
||||||
IsEnabled = TargetType != null;
|
IsEnabled = TargetType != null;
|
||||||
|
|
||||||
// If null, clear the input
|
// If null, clear the input
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using Artemis.Core.DataModelExpansions;
|
using System.Linq;
|
||||||
|
using Artemis.Core;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
|
|
||||||
namespace Artemis.UI.Shared
|
namespace Artemis.UI.Shared
|
||||||
@ -10,11 +11,12 @@ namespace Artemis.UI.Shared
|
|||||||
private int _index;
|
private int _index;
|
||||||
private Type _listType;
|
private Type _listType;
|
||||||
|
|
||||||
public DataModelListPropertiesViewModel(DataModel dataModel, object listItem) : base(null, null, null)
|
public DataModelListPropertiesViewModel(Type listType) : base(null, null, null)
|
||||||
{
|
{
|
||||||
DataModel = dataModel;
|
DataModel = ListPredicateWrapperDataModel.Create(listType);
|
||||||
ListType = listItem.GetType();
|
ListType = listType;
|
||||||
DisplayValue = listItem;
|
|
||||||
|
IsRootViewModel = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Index
|
public int Index
|
||||||
@ -35,18 +37,21 @@ namespace Artemis.UI.Shared
|
|||||||
set => SetAndNotify(ref _displayValue, value);
|
set => SetAndNotify(ref _displayValue, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public DataModelVisualizationViewModel DisplayViewModel => Children.FirstOrDefault();
|
||||||
|
|
||||||
public override string DisplayPath => null;
|
public override string DisplayPath => null;
|
||||||
|
|
||||||
public override void Update(IDataModelUIService dataModelUIService)
|
public override void Update(IDataModelUIService dataModelUIService)
|
||||||
{
|
{
|
||||||
// Display value gets updated by parent, don't do anything if it is null
|
((ListPredicateWrapperDataModel) DataModel).UntypedValue = DisplayValue;
|
||||||
if (DisplayValue == null)
|
|
||||||
|
PopulateProperties(dataModelUIService);
|
||||||
|
if (DisplayViewModel == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
ListType = DisplayValue.GetType();
|
if (IsVisualizationExpanded && !DisplayViewModel.IsVisualizationExpanded)
|
||||||
PopulateProperties(dataModelUIService, DisplayValue);
|
DisplayViewModel.IsVisualizationExpanded = IsVisualizationExpanded;
|
||||||
foreach (DataModelVisualizationViewModel dataModelVisualizationViewModel in Children)
|
DisplayViewModel.Update(dataModelUIService);
|
||||||
dataModelVisualizationViewModel.Update(dataModelUIService);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override object GetCurrentValue()
|
public override object GetCurrentValue()
|
||||||
|
|||||||
@ -1,5 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
using Artemis.Core.DataModelExpansions;
|
using Artemis.Core;
|
||||||
using Artemis.UI.Shared.Services;
|
using Artemis.UI.Shared.Services;
|
||||||
|
|
||||||
namespace Artemis.UI.Shared
|
namespace Artemis.UI.Shared
|
||||||
@ -9,19 +9,17 @@ namespace Artemis.UI.Shared
|
|||||||
private int _index;
|
private int _index;
|
||||||
private Type _listType;
|
private Type _listType;
|
||||||
|
|
||||||
public DataModelListPropertyViewModel(DataModel dataModel, object listItem, DataModelDisplayViewModel displayViewModel) : base(null, null, null)
|
public DataModelListPropertyViewModel(Type listType, DataModelDisplayViewModel displayViewModel) : base(null, null, null)
|
||||||
{
|
{
|
||||||
DataModel = dataModel;
|
DataModel = ListPredicateWrapperDataModel.Create(listType);
|
||||||
ListType = listItem.GetType();
|
ListType = listType;
|
||||||
DisplayValue = listItem;
|
|
||||||
DisplayViewModel = displayViewModel;
|
DisplayViewModel = displayViewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataModelListPropertyViewModel(DataModel dataModel, object listItem) : base(null, null, null)
|
public DataModelListPropertyViewModel(Type listType) : base(null, null, null)
|
||||||
{
|
{
|
||||||
DataModel = dataModel;
|
DataModel = ListPredicateWrapperDataModel.Create(listType);
|
||||||
ListType = listItem.GetType();
|
ListType = listType;
|
||||||
DisplayValue = listItem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int Index
|
public int Index
|
||||||
@ -47,8 +45,10 @@ namespace Artemis.UI.Shared
|
|||||||
if (DisplayValue == null)
|
if (DisplayValue == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
((ListPredicateWrapperDataModel)DataModel).UntypedValue = DisplayValue;
|
||||||
|
|
||||||
if (DisplayViewModel == null)
|
if (DisplayViewModel == null)
|
||||||
DisplayViewModel = dataModelUIService.GetDataModelDisplayViewModel(DisplayValue.GetType(), true);
|
DisplayViewModel = dataModelUIService.GetDataModelDisplayViewModel(DisplayValue.GetType(), PropertyDescription, true);
|
||||||
|
|
||||||
ListType = DisplayValue.GetType();
|
ListType = DisplayValue.GetType();
|
||||||
UpdateDisplayParameters();
|
UpdateDisplayParameters();
|
||||||
|
|||||||
@ -11,8 +11,7 @@ namespace Artemis.UI.Shared
|
|||||||
{
|
{
|
||||||
private string _count;
|
private string _count;
|
||||||
private IList _list;
|
private IList _list;
|
||||||
private DataModelVisualizationViewModel _listTypePropertyViewModel;
|
|
||||||
|
|
||||||
internal DataModelListViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath) : base(dataModel, parent, dataModelPath)
|
internal DataModelListViewModel(DataModel dataModel, DataModelVisualizationViewModel parent, DataModelPath dataModelPath) : base(dataModel, parent, dataModelPath)
|
||||||
{
|
{
|
||||||
ListChildren = new BindableCollection<DataModelVisualizationViewModel>();
|
ListChildren = new BindableCollection<DataModelVisualizationViewModel>();
|
||||||
@ -34,21 +33,19 @@ namespace Artemis.UI.Shared
|
|||||||
|
|
||||||
public DataModelPropertiesViewModel GetListTypeViewModel(IDataModelUIService dataModelUIService)
|
public DataModelPropertiesViewModel GetListTypeViewModel(IDataModelUIService dataModelUIService)
|
||||||
{
|
{
|
||||||
Type type = DataModelPath.GetPropertyType();
|
Type listType = DataModelPath.GetPropertyType()?.GenericTypeArguments[0];
|
||||||
if (type == null)
|
if (listType == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
// Create a property VM describing the type of the list
|
// Create a property VM describing the type of the list
|
||||||
DataModelVisualizationViewModel viewModel = CreateListChild(dataModelUIService, type.GenericTypeArguments[0]);
|
DataModelVisualizationViewModel viewModel = CreateListChild(dataModelUIService, listType);
|
||||||
|
viewModel.Update(dataModelUIService);
|
||||||
|
|
||||||
// Put an empty value into the list type property view model
|
// Put an empty value into the list type property view model
|
||||||
if (viewModel is DataModelListPropertiesViewModel dataModelListClassViewModel)
|
if (viewModel is DataModelListPropertiesViewModel dataModelListClassViewModel)
|
||||||
{
|
{
|
||||||
dataModelListClassViewModel.DisplayValue = Activator.CreateInstance(dataModelListClassViewModel.ListType);
|
|
||||||
dataModelListClassViewModel.Update(dataModelUIService);
|
|
||||||
return dataModelListClassViewModel;
|
return dataModelListClassViewModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (viewModel is DataModelListPropertyViewModel dataModelListPropertyViewModel)
|
if (viewModel is DataModelListPropertyViewModel dataModelListPropertyViewModel)
|
||||||
{
|
{
|
||||||
dataModelListPropertyViewModel.DisplayValue = Activator.CreateInstance(dataModelListPropertyViewModel.ListType);
|
dataModelListPropertyViewModel.DisplayValue = Activator.CreateInstance(dataModelListPropertyViewModel.ListType);
|
||||||
@ -70,12 +67,15 @@ namespace Artemis.UI.Shared
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
int index = 0;
|
int index = 0;
|
||||||
foreach (object? item in List)
|
foreach (object item in List)
|
||||||
{
|
{
|
||||||
|
if (item == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
DataModelVisualizationViewModel child;
|
DataModelVisualizationViewModel child;
|
||||||
if (ListChildren.Count <= index)
|
if (ListChildren.Count <= index)
|
||||||
{
|
{
|
||||||
child = CreateListChild(dataModelUIService, item);
|
child = CreateListChild(dataModelUIService, item.GetType());
|
||||||
ListChildren.Add(child);
|
ListChildren.Add(child);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -102,20 +102,18 @@ namespace Artemis.UI.Shared
|
|||||||
Count = $"{ListChildren.Count} {(ListChildren.Count == 1 ? "item" : "items")}";
|
Count = $"{ListChildren.Count} {(ListChildren.Count == 1 ? "item" : "items")}";
|
||||||
}
|
}
|
||||||
|
|
||||||
protected DataModelVisualizationViewModel CreateListChild(IDataModelUIService dataModelUIService, object listItem)
|
protected DataModelVisualizationViewModel CreateListChild(IDataModelUIService dataModelUIService, Type listType)
|
||||||
{
|
{
|
||||||
Type listType = listItem.GetType();
|
|
||||||
|
|
||||||
// If a display VM was found, prefer to use that in any case
|
// If a display VM was found, prefer to use that in any case
|
||||||
DataModelDisplayViewModel typeViewModel = dataModelUIService.GetDataModelDisplayViewModel(listType);
|
DataModelDisplayViewModel typeViewModel = dataModelUIService.GetDataModelDisplayViewModel(listType, PropertyDescription);
|
||||||
if (typeViewModel != null)
|
if (typeViewModel != null)
|
||||||
return new DataModelListPropertyViewModel(DataModel, listItem, typeViewModel);
|
return new DataModelListPropertyViewModel(listType, typeViewModel);
|
||||||
// For primitives, create a property view model, it may be null that is fine
|
// For primitives, create a property view model, it may be null that is fine
|
||||||
if (listType.IsPrimitive || listType.IsEnum || listType == typeof(string))
|
if (listType.IsPrimitive || listType.IsEnum || listType == typeof(string))
|
||||||
return new DataModelListPropertyViewModel(DataModel, listItem);
|
return new DataModelListPropertyViewModel(listType);
|
||||||
// For other value types create a child view model
|
// For other value types create a child view model
|
||||||
if (listType.IsClass || listType.IsStruct())
|
if (listType.IsClass || listType.IsStruct())
|
||||||
return new DataModelListPropertiesViewModel(DataModel, listItem);
|
return new DataModelListPropertiesViewModel(listType);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -13,7 +13,7 @@ namespace Artemis.UI.Shared
|
|||||||
public override void Update(IDataModelUIService dataModelUIService)
|
public override void Update(IDataModelUIService dataModelUIService)
|
||||||
{
|
{
|
||||||
// Always populate properties
|
// Always populate properties
|
||||||
PopulateProperties(dataModelUIService, null);
|
PopulateProperties(dataModelUIService);
|
||||||
|
|
||||||
// Only update children if the parent is expanded
|
// Only update children if the parent is expanded
|
||||||
if (Parent != null && !Parent.IsVisualizationExpanded && !Parent.IsRootViewModel)
|
if (Parent != null && !Parent.IsVisualizationExpanded && !Parent.IsRootViewModel)
|
||||||
|
|||||||
@ -40,7 +40,7 @@ namespace Artemis.UI.Shared
|
|||||||
|
|
||||||
if (DisplayViewModel == null)
|
if (DisplayViewModel == null)
|
||||||
{
|
{
|
||||||
DisplayViewModel = dataModelUIService.GetDataModelDisplayViewModel(DataModelPath.GetPropertyType(), true);
|
DisplayViewModel = dataModelUIService.GetDataModelDisplayViewModel(DataModelPath.GetPropertyType(), PropertyDescription, true);
|
||||||
DisplayViewModel.PropertyDescription = DataModelPath.GetPropertyDescription();
|
DisplayViewModel.PropertyDescription = DataModelPath.GetPropertyDescription();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -35,7 +35,7 @@ namespace Artemis.UI.Shared
|
|||||||
PropertyDescription = DataModelPath?.GetPropertyDescription() ?? DataModel.DataModelDescription;
|
PropertyDescription = DataModelPath?.GetPropertyDescription() ?? DataModel.DataModelDescription;
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsRootViewModel { get; }
|
public bool IsRootViewModel { get; protected set; }
|
||||||
public DataModelPath DataModelPath { get; }
|
public DataModelPath DataModelPath { get; }
|
||||||
public string Path => DataModelPath?.Path;
|
public string Path => DataModelPath?.Path;
|
||||||
|
|
||||||
@ -81,7 +81,7 @@ namespace Artemis.UI.Shared
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public virtual string DisplayPath => Path?.Replace(".", " › ");
|
public virtual string DisplayPath => string.Join(" › ", DataModelPath.Segments.Select(s => s.GetPropertyDescription()?.Name ?? s.Identifier));
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Updates the datamodel and if in an parent, any children
|
/// Updates the datamodel and if in an parent, any children
|
||||||
@ -125,7 +125,7 @@ namespace Artemis.UI.Shared
|
|||||||
}
|
}
|
||||||
|
|
||||||
// If the type couldn't be retrieved either way, assume false
|
// If the type couldn't be retrieved either way, assume false
|
||||||
Type type = DataModelPath.GetPropertyType();
|
Type type = DataModelPath?.GetPropertyType();
|
||||||
if (type == null)
|
if (type == null)
|
||||||
{
|
{
|
||||||
IsMatchingFilteredTypes = false;
|
IsMatchingFilteredTypes = false;
|
||||||
@ -146,7 +146,7 @@ namespace Artemis.UI.Shared
|
|||||||
return null;
|
return null;
|
||||||
if (propertyPath == null)
|
if (propertyPath == null)
|
||||||
return null;
|
return null;
|
||||||
if (Path.StartsWith(propertyPath, StringComparison.OrdinalIgnoreCase))
|
if (Path != null && Path.StartsWith(propertyPath, StringComparison.OrdinalIgnoreCase))
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,18 +178,12 @@ namespace Artemis.UI.Shared
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
internal void PopulateProperties(IDataModelUIService dataModelUIService, object overrideValue)
|
internal void PopulateProperties(IDataModelUIService dataModelUIService)
|
||||||
{
|
{
|
||||||
if (IsRootViewModel && overrideValue == null)
|
if (IsRootViewModel)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Type modelType;
|
Type modelType = Parent == null || Parent.IsRootViewModel ? DataModel.GetType() : DataModelPath.GetPropertyType();
|
||||||
if (overrideValue != null)
|
|
||||||
modelType = overrideValue.GetType();
|
|
||||||
else if (Parent.IsRootViewModel)
|
|
||||||
modelType = DataModel.GetType();
|
|
||||||
else
|
|
||||||
modelType = DataModelPath.GetPropertyType();
|
|
||||||
|
|
||||||
// Add missing static children
|
// Add missing static children
|
||||||
foreach (PropertyInfo propertyInfo in modelType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
foreach (PropertyInfo propertyInfo in modelType.GetProperties(BindingFlags.Public | BindingFlags.Instance))
|
||||||
@ -200,17 +194,13 @@ namespace Artemis.UI.Shared
|
|||||||
if (propertyInfo.GetCustomAttribute<DataModelIgnoreAttribute>() != null)
|
if (propertyInfo.GetCustomAttribute<DataModelIgnoreAttribute>() != null)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
DataModelVisualizationViewModel child = CreateChild(dataModelUIService, childPath, GetChildDepth(), overrideValue);
|
DataModelVisualizationViewModel child = CreateChild(dataModelUIService, childPath, GetChildDepth());
|
||||||
if (child != null)
|
if (child != null)
|
||||||
Children.Add(child);
|
Children.Add(child);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Remove static children that should be hidden
|
// Remove static children that should be hidden
|
||||||
ReadOnlyCollection<PropertyInfo> hiddenProperties;
|
ReadOnlyCollection<PropertyInfo> hiddenProperties = DataModel.GetHiddenProperties();
|
||||||
if (overrideValue != null && overrideValue is DataModel overrideValueDataModel)
|
|
||||||
hiddenProperties = overrideValueDataModel.GetHiddenProperties();
|
|
||||||
else
|
|
||||||
hiddenProperties = DataModel.GetHiddenProperties();
|
|
||||||
foreach (PropertyInfo hiddenProperty in hiddenProperties)
|
foreach (PropertyInfo hiddenProperty in hiddenProperties)
|
||||||
{
|
{
|
||||||
string childPath = AppendToPath(hiddenProperty.Name);
|
string childPath = AppendToPath(hiddenProperty.Name);
|
||||||
@ -220,11 +210,7 @@ namespace Artemis.UI.Shared
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add missing dynamic children
|
// Add missing dynamic children
|
||||||
object value;
|
object value = Parent == null || Parent.IsRootViewModel ? DataModel : DataModelPath.GetValue();
|
||||||
if (overrideValue != null)
|
|
||||||
value = overrideValue;
|
|
||||||
else
|
|
||||||
value = Parent.IsRootViewModel ? DataModel : DataModelPath.GetValue();
|
|
||||||
if (value is DataModel dataModel)
|
if (value is DataModel dataModel)
|
||||||
{
|
{
|
||||||
foreach (KeyValuePair<string, DataModel> kvp in dataModel.DynamicDataModels)
|
foreach (KeyValuePair<string, DataModel> kvp in dataModel.DynamicDataModels)
|
||||||
@ -233,7 +219,7 @@ namespace Artemis.UI.Shared
|
|||||||
if (Children.Any(c => c.Path != null && c.Path.Equals(childPath)))
|
if (Children.Any(c => c.Path != null && c.Path.Equals(childPath)))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
DataModelVisualizationViewModel child = CreateChild(dataModelUIService, childPath, GetChildDepth(), overrideValue);
|
DataModelVisualizationViewModel child = CreateChild(dataModelUIService, childPath, GetChildDepth());
|
||||||
if (child != null)
|
if (child != null)
|
||||||
Children.Add(child);
|
Children.Add(child);
|
||||||
}
|
}
|
||||||
@ -245,12 +231,12 @@ namespace Artemis.UI.Shared
|
|||||||
Children.RemoveRange(toRemoveDynamic);
|
Children.RemoveRange(toRemoveDynamic);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataModelVisualizationViewModel CreateChild(IDataModelUIService dataModelUIService, string path, int depth, object overrideValue)
|
private DataModelVisualizationViewModel CreateChild(IDataModelUIService dataModelUIService, string path, int depth)
|
||||||
{
|
{
|
||||||
if (depth > MaxDepth)
|
if (depth > MaxDepth)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
DataModelPath dataModelPath = new DataModelPath(overrideValue ?? DataModel, path);
|
DataModelPath dataModelPath = new DataModelPath(DataModel, path);
|
||||||
if (!dataModelPath.IsValid)
|
if (!dataModelPath.IsValid)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
@ -265,7 +251,7 @@ namespace Artemis.UI.Shared
|
|||||||
return null;
|
return null;
|
||||||
|
|
||||||
// If a display VM was found, prefer to use that in any case
|
// If a display VM was found, prefer to use that in any case
|
||||||
DataModelDisplayViewModel typeViewModel = dataModelUIService.GetDataModelDisplayViewModel(propertyType);
|
DataModelDisplayViewModel typeViewModel = dataModelUIService.GetDataModelDisplayViewModel(propertyType, PropertyDescription);
|
||||||
if (typeViewModel != null)
|
if (typeViewModel != null)
|
||||||
return new DataModelPropertyViewModel(DataModel, this, dataModelPath) {DisplayViewModel = typeViewModel, Depth = depth};
|
return new DataModelPropertyViewModel(DataModel, this, dataModelPath) {DisplayViewModel = typeViewModel, Depth = depth};
|
||||||
// For primitives, create a property view model, it may be null that is fine
|
// For primitives, create a property view model, it may be null that is fine
|
||||||
|
|||||||
@ -1,14 +1,15 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Artemis.Core;
|
||||||
|
|
||||||
namespace Artemis.UI.Shared
|
namespace Artemis.UI.Shared
|
||||||
{
|
{
|
||||||
public class DataModelInputDynamicEventArgs : EventArgs
|
public class DataModelInputDynamicEventArgs : EventArgs
|
||||||
{
|
{
|
||||||
public DataModelVisualizationViewModel DataModelVisualizationViewModel { get; }
|
public DataModelPath DataModelPath { get; }
|
||||||
|
|
||||||
public DataModelInputDynamicEventArgs(DataModelVisualizationViewModel dataModelVisualizationViewModel)
|
public DataModelInputDynamicEventArgs(DataModelPath dataModelPath)
|
||||||
{
|
{
|
||||||
DataModelVisualizationViewModel = dataModelVisualizationViewModel;
|
DataModelPath = dataModelPath;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -159,14 +159,24 @@ namespace Artemis.UI.Shared.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataModelDisplayViewModel GetDataModelDisplayViewModel(Type propertyType, bool fallBackToDefault)
|
public DataModelDisplayViewModel GetDataModelDisplayViewModel(Type propertyType, DataModelPropertyAttribute description, bool fallBackToDefault)
|
||||||
{
|
{
|
||||||
lock (_registeredDataModelDisplays)
|
lock (_registeredDataModelDisplays)
|
||||||
{
|
{
|
||||||
|
DataModelDisplayViewModel result;
|
||||||
|
|
||||||
DataModelVisualizationRegistration match = _registeredDataModelDisplays.FirstOrDefault(d => d.SupportedType == propertyType);
|
DataModelVisualizationRegistration match = _registeredDataModelDisplays.FirstOrDefault(d => d.SupportedType == propertyType);
|
||||||
if (match != null)
|
if (match != null)
|
||||||
return (DataModelDisplayViewModel) match.PluginInfo.Kernel.Get(match.ViewModelType);
|
result = (DataModelDisplayViewModel) match.PluginInfo.Kernel.Get(match.ViewModelType);
|
||||||
return !fallBackToDefault ? null : _kernel.Get<DefaultDataModelDisplayViewModel>();
|
else if (!fallBackToDefault)
|
||||||
|
result = null;
|
||||||
|
else
|
||||||
|
result = _kernel.Get<DefaultDataModelDisplayViewModel>();
|
||||||
|
|
||||||
|
if (result != null)
|
||||||
|
result.PropertyDescription = description;
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,9 +209,9 @@ namespace Artemis.UI.Shared.Services
|
|||||||
return _kernel.Get<DataModelDynamicViewModel>(new ConstructorArgument("module", module));
|
return _kernel.Get<DataModelDynamicViewModel>(new ConstructorArgument("module", module));
|
||||||
}
|
}
|
||||||
|
|
||||||
public DataModelStaticViewModel GetStaticInputViewModel(Type targetType)
|
public DataModelStaticViewModel GetStaticInputViewModel(Type targetType, DataModelPropertyAttribute targetDescription)
|
||||||
{
|
{
|
||||||
return _kernel.Get<DataModelStaticViewModel>(new ConstructorArgument("targetType", targetType));
|
return _kernel.Get<DataModelStaticViewModel>(new ConstructorArgument("targetType", targetType), new ConstructorArgument("targetDescription", targetDescription));
|
||||||
}
|
}
|
||||||
|
|
||||||
private DataModelInputViewModel InstantiateDataModelInputViewModel(DataModelVisualizationRegistration registration, DataModelPropertyAttribute description, object initialValue)
|
private DataModelInputViewModel InstantiateDataModelInputViewModel(DataModelVisualizationRegistration registration, DataModelPropertyAttribute description, object initialValue)
|
||||||
@ -219,7 +229,7 @@ namespace Artemis.UI.Shared.Services
|
|||||||
|
|
||||||
IParameter[] parameters = new IParameter[]
|
IParameter[] parameters = new IParameter[]
|
||||||
{
|
{
|
||||||
new ConstructorArgument("description", description),
|
new ConstructorArgument("targetDescription", description),
|
||||||
new ConstructorArgument("initialValue", initialValue)
|
new ConstructorArgument("initialValue", initialValue)
|
||||||
};
|
};
|
||||||
DataModelInputViewModel viewModel = (DataModelInputViewModel) registration.PluginInfo.Kernel.Get(registration.ViewModelType, parameters);
|
DataModelInputViewModel viewModel = (DataModelInputViewModel) registration.PluginInfo.Kernel.Get(registration.ViewModelType, parameters);
|
||||||
|
|||||||
@ -19,10 +19,10 @@ namespace Artemis.UI.Shared.Services
|
|||||||
void RemoveDataModelInput(DataModelVisualizationRegistration registration);
|
void RemoveDataModelInput(DataModelVisualizationRegistration registration);
|
||||||
void RemoveDataModelDisplay(DataModelVisualizationRegistration registration);
|
void RemoveDataModelDisplay(DataModelVisualizationRegistration registration);
|
||||||
|
|
||||||
DataModelDisplayViewModel GetDataModelDisplayViewModel(Type propertyType, bool fallBackToDefault = false);
|
DataModelDisplayViewModel GetDataModelDisplayViewModel(Type propertyType, DataModelPropertyAttribute description, bool fallBackToDefault = false);
|
||||||
DataModelInputViewModel GetDataModelInputViewModel(Type propertyType, DataModelPropertyAttribute description, object initialValue, Action<object, bool> updateCallback);
|
DataModelInputViewModel GetDataModelInputViewModel(Type propertyType, DataModelPropertyAttribute description, object initialValue, Action<object, bool> updateCallback);
|
||||||
|
|
||||||
DataModelDynamicViewModel GetDynamicSelectionViewModel(Module module);
|
DataModelDynamicViewModel GetDynamicSelectionViewModel(Module module);
|
||||||
DataModelStaticViewModel GetStaticInputViewModel(Type targetType);
|
DataModelStaticViewModel GetStaticInputViewModel(Type targetType, DataModelPropertyAttribute targetDescription);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -6,9 +6,15 @@
|
|||||||
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
|
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
|
||||||
xmlns:s="https://github.com/canton7/Stylet"
|
xmlns:s="https://github.com/canton7/Stylet"
|
||||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||||
|
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignHeight="450" d:DesignWidth="800">
|
d:DesignHeight="450" d:DesignWidth="800">
|
||||||
<TextBox VerticalAlignment="Center" Text="{Binding InputValue}" PreviewTextInput="{s:Action NumberValidationTextBox}" LostFocus="{s:Action Submit}" Width="140">
|
<TextBox VerticalAlignment="Center"
|
||||||
|
Text="{Binding InputValue}"
|
||||||
|
PreviewTextInput="{s:Action NumberValidationTextBox}"
|
||||||
|
materialDesign:TextFieldAssist.SuffixText="{Binding TargetDescription.Affix}"
|
||||||
|
LostFocus="{s:Action Submit}"
|
||||||
|
Width="140">
|
||||||
<b:Interaction.Behaviors>
|
<b:Interaction.Behaviors>
|
||||||
<shared:PutCursorAtEndTextBoxBehavior />
|
<shared:PutCursorAtEndTextBoxBehavior />
|
||||||
</b:Interaction.Behaviors>
|
</b:Interaction.Behaviors>
|
||||||
|
|||||||
@ -6,9 +6,15 @@
|
|||||||
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
|
xmlns:b="http://schemas.microsoft.com/xaml/behaviors"
|
||||||
xmlns:s="https://github.com/canton7/Stylet"
|
xmlns:s="https://github.com/canton7/Stylet"
|
||||||
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
xmlns:shared="clr-namespace:Artemis.UI.Shared;assembly=Artemis.UI.Shared"
|
||||||
|
xmlns:materialDesign="http://materialdesigninxaml.net/winfx/xaml/themes"
|
||||||
mc:Ignorable="d"
|
mc:Ignorable="d"
|
||||||
d:DesignHeight="450" d:DesignWidth="800">
|
d:DesignHeight="450" d:DesignWidth="800">
|
||||||
<TextBox VerticalAlignment="Center" Text="{Binding InputValue}" PreviewTextInput="{s:Action NumberValidationTextBox}" LostFocus="{s:Action Submit}" Width="140">
|
<TextBox VerticalAlignment="Center"
|
||||||
|
Text="{Binding InputValue}"
|
||||||
|
PreviewTextInput="{s:Action NumberValidationTextBox}"
|
||||||
|
materialDesign:TextFieldAssist.SuffixText="{Binding TargetDescription.Suffix}"
|
||||||
|
LostFocus="{s:Action Submit}"
|
||||||
|
Width="140">
|
||||||
<b:Interaction.Behaviors>
|
<b:Interaction.Behaviors>
|
||||||
<shared:PutCursorAtEndTextBoxBehavior />
|
<shared:PutCursorAtEndTextBoxBehavior />
|
||||||
</b:Interaction.Behaviors>
|
</b:Interaction.Behaviors>
|
||||||
|
|||||||
@ -109,11 +109,9 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
NotifyOfPropertyChange(nameof(SelectedBooleanOperator));
|
NotifyOfPropertyChange(nameof(SelectedBooleanOperator));
|
||||||
|
|
||||||
// Remove VMs of effects no longer applied on the layer
|
// Remove VMs of effects no longer applied on the layer
|
||||||
List<DataModelConditionViewModel> toRemove = Items.Where(c => !DataModelConditionGroup.Children.Contains(c.Model)).ToList();
|
Items.RemoveRange(Items.Where(c => !DataModelConditionGroup.Children.Contains(c.Model)).ToList());
|
||||||
// Using RemoveRange breaks our lovely animations
|
|
||||||
foreach (DataModelConditionViewModel DataModelConditionViewModel in toRemove)
|
|
||||||
Items.Remove(DataModelConditionViewModel);
|
|
||||||
|
|
||||||
|
List<DataModelConditionViewModel> viewModels = new List<DataModelConditionViewModel>();
|
||||||
foreach (DataModelConditionPart childModel in Model.Children)
|
foreach (DataModelConditionPart childModel in Model.Children)
|
||||||
{
|
{
|
||||||
if (Items.Any(c => c.Model == childModel))
|
if (Items.Any(c => c.Model == childModel))
|
||||||
@ -122,21 +120,23 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
switch (childModel)
|
switch (childModel)
|
||||||
{
|
{
|
||||||
case DataModelConditionGroup DataModelConditionGroup:
|
case DataModelConditionGroup DataModelConditionGroup:
|
||||||
Items.Add(_dataModelConditionsVmFactory.DataModelConditionGroupViewModel(DataModelConditionGroup, IsListGroup));
|
viewModels.Add(_dataModelConditionsVmFactory.DataModelConditionGroupViewModel(DataModelConditionGroup, IsListGroup));
|
||||||
break;
|
break;
|
||||||
case DataModelConditionList DataModelConditionListPredicate:
|
case DataModelConditionList DataModelConditionListPredicate:
|
||||||
Items.Add(_dataModelConditionsVmFactory.DataModelConditionListViewModel(DataModelConditionListPredicate));
|
viewModels.Add(_dataModelConditionsVmFactory.DataModelConditionListViewModel(DataModelConditionListPredicate));
|
||||||
break;
|
break;
|
||||||
case DataModelConditionPredicate DataModelConditionPredicate:
|
case DataModelConditionPredicate DataModelConditionPredicate:
|
||||||
if (!IsListGroup)
|
if (!IsListGroup)
|
||||||
Items.Add(_dataModelConditionsVmFactory.DataModelConditionPredicateViewModel(DataModelConditionPredicate));
|
viewModels.Add(_dataModelConditionsVmFactory.DataModelConditionPredicateViewModel(DataModelConditionPredicate));
|
||||||
break;
|
break;
|
||||||
case DataModelConditionListPredicate DataModelConditionListPredicate:
|
case DataModelConditionListPredicate DataModelConditionListPredicate:
|
||||||
if (IsListGroup)
|
if (IsListGroup)
|
||||||
Items.Add(_dataModelConditionsVmFactory.DataModelConditionListPredicateViewModel(DataModelConditionListPredicate));
|
viewModels.Add(_dataModelConditionsVmFactory.DataModelConditionListPredicateViewModel(DataModelConditionListPredicate));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (viewModels.Any())
|
||||||
|
Items.AddRange(viewModels);
|
||||||
|
|
||||||
foreach (DataModelConditionViewModel childViewModel in Items)
|
foreach (DataModelConditionViewModel childViewModel in Items)
|
||||||
childViewModel.Update();
|
childViewModel.Update();
|
||||||
|
|||||||
@ -79,18 +79,6 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
|
|
||||||
public DelegateCommand SelectOperatorCommand { get; }
|
public DelegateCommand SelectOperatorCommand { get; }
|
||||||
|
|
||||||
public void Dispose()
|
|
||||||
{
|
|
||||||
if (!_isPrimitiveList)
|
|
||||||
{
|
|
||||||
LeftSideSelectionViewModel.PropertySelected -= LeftSideOnPropertySelected;
|
|
||||||
LeftSideSelectionViewModel.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
DisposeRightSideDynamic();
|
|
||||||
DisposeRightSideStatic();
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Delete()
|
public override void Delete()
|
||||||
{
|
{
|
||||||
base.Delete();
|
base.Delete();
|
||||||
@ -123,30 +111,37 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
|
|
||||||
public override void Update()
|
public override void Update()
|
||||||
{
|
{
|
||||||
Guid listDataModelGuid = DataModelConditionListPredicate.DataModelConditionList.ListDataModel.PluginInfo.Guid;
|
Guid? listDataModelGuid = DataModelConditionListPredicate.DataModelConditionList.ListPath?.DataModelGuid;
|
||||||
|
if (listDataModelGuid == null)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!_isPrimitiveList)
|
if (!_isPrimitiveList)
|
||||||
{
|
{
|
||||||
LeftSideSelectionViewModel.FilterTypes = _supportedInputTypes.ToArray();
|
// Lists use a different color
|
||||||
LeftSideSelectionViewModel.ButtonBrush = new SolidColorBrush(Color.FromRgb(71, 108, 188));
|
LeftSideSelectionViewModel.ButtonBrush = new SolidColorBrush(Color.FromRgb(71, 108, 188));
|
||||||
LeftSideSelectionViewModel.SelectedPropertyViewModel = LeftSideSelectionViewModel.DataModelViewModel.GetChildByPath(
|
LeftSideSelectionViewModel.FilterTypes = _supportedInputTypes.ToArray();
|
||||||
listDataModelGuid, DataModelConditionListPredicate.LeftPropertyPath
|
LeftSideSelectionViewModel.ChangeDataModelPath(DataModelConditionListPredicate.LeftPath);
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Type leftSideType = _isPrimitiveList
|
Type leftSideType = _isPrimitiveList
|
||||||
? DataModelConditionListPredicate.DataModelConditionList.ListType
|
? DataModelConditionListPredicate.DataModelConditionList.ListType
|
||||||
: LeftSideSelectionViewModel.SelectedPropertyViewModel?.DataModelPath?.GetPropertyType();
|
: LeftSideSelectionViewModel.DataModelPath?.GetPropertyType();
|
||||||
|
|
||||||
// Get the supported operators
|
// Get the supported operators
|
||||||
Operators.Clear();
|
Operators.Clear();
|
||||||
Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType));
|
Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType ?? typeof(object)));
|
||||||
if (DataModelConditionListPredicate.Operator == null)
|
if (DataModelConditionListPredicate.Operator == null)
|
||||||
DataModelConditionListPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType)));
|
DataModelConditionListPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType ?? typeof(object))));
|
||||||
SelectedOperator = DataModelConditionListPredicate.Operator;
|
SelectedOperator = DataModelConditionListPredicate.Operator;
|
||||||
|
if (SelectedOperator == null || !SelectedOperator.SupportsRightSide)
|
||||||
|
{
|
||||||
|
DisposeRightSideStatic();
|
||||||
|
DisposeRightSideDynamic();
|
||||||
|
}
|
||||||
|
|
||||||
// Ensure the right side has the proper VM
|
// Ensure the right side has the proper VM
|
||||||
if (DataModelConditionListPredicate.PredicateType == ListRightSideType.Dynamic ||
|
ListRightSideType type = DataModelConditionListPredicate.PredicateType;
|
||||||
DataModelConditionListPredicate.PredicateType == ListRightSideType.DynamicList)
|
if ((type == ListRightSideType.Dynamic || type == ListRightSideType.DynamicList) && SelectedOperator.SupportsRightSide)
|
||||||
{
|
{
|
||||||
DisposeRightSideStatic();
|
DisposeRightSideStatic();
|
||||||
if (RightSideSelectionViewModel == null)
|
if (RightSideSelectionViewModel == null)
|
||||||
@ -160,26 +155,14 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
}
|
}
|
||||||
|
|
||||||
RightSideSelectionViewModel.FilterTypes = new[] {leftSideType};
|
RightSideSelectionViewModel.FilterTypes = new[] {leftSideType};
|
||||||
if (DataModelConditionListPredicate.PredicateType == ListRightSideType.Dynamic)
|
RightSideSelectionViewModel.ChangeDataModelPath(DataModelConditionListPredicate.RightPath);
|
||||||
{
|
|
||||||
RightSideSelectionViewModel.PopulateSelectedPropertyViewModel(
|
|
||||||
DataModelConditionListPredicate.RightDataModel,
|
|
||||||
DataModelConditionListPredicate.RightPropertyPath
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
RightSideSelectionViewModel.SelectedPropertyViewModel = RightSideSelectionViewModel.DataModelViewModel.GetChildByPath(
|
|
||||||
listDataModelGuid, DataModelConditionListPredicate.RightPropertyPath
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else if (SelectedOperator.SupportsRightSide)
|
||||||
{
|
{
|
||||||
DisposeRightSideDynamic();
|
DisposeRightSideDynamic();
|
||||||
if (RightSideInputViewModel == null)
|
if (RightSideInputViewModel == null)
|
||||||
{
|
{
|
||||||
RightSideInputViewModel = _dataModelUIService.GetStaticInputViewModel(leftSideType);
|
RightSideInputViewModel = _dataModelUIService.GetStaticInputViewModel(leftSideType, LeftSideSelectionViewModel.DataModelPath?.GetPropertyDescription());
|
||||||
RightSideInputViewModel.Value = DataModelConditionListPredicate.RightStaticValue;
|
RightSideInputViewModel.Value = DataModelConditionListPredicate.RightStaticValue;
|
||||||
RightSideInputViewModel.ButtonBrush = (Brush) Application.Current.FindResource("PrimaryHueMidBrush");
|
RightSideInputViewModel.ButtonBrush = (Brush) Application.Current.FindResource("PrimaryHueMidBrush");
|
||||||
RightSideInputViewModel.ValueUpdated += RightSideOnValueEntered;
|
RightSideInputViewModel.ValueUpdated += RightSideOnValueEntered;
|
||||||
@ -192,7 +175,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
|
|
||||||
public void ApplyLeftSide()
|
public void ApplyLeftSide()
|
||||||
{
|
{
|
||||||
DataModelConditionListPredicate.UpdateLeftSide(LeftSideSelectionViewModel.SelectedPropertyViewModel.Path);
|
DataModelConditionListPredicate.UpdateLeftSide(LeftSideSelectionViewModel.DataModelPath);
|
||||||
_profileEditorService.UpdateSelectedProfileElement();
|
_profileEditorService.UpdateSelectedProfileElement();
|
||||||
|
|
||||||
SelectedOperator = DataModelConditionListPredicate.Operator;
|
SelectedOperator = DataModelConditionListPredicate.Operator;
|
||||||
@ -202,16 +185,9 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
public void ApplyRightSideDynamic()
|
public void ApplyRightSideDynamic()
|
||||||
{
|
{
|
||||||
if (DataModelConditionListPredicate.PredicateType == ListRightSideType.Dynamic)
|
if (DataModelConditionListPredicate.PredicateType == ListRightSideType.Dynamic)
|
||||||
{
|
DataModelConditionListPredicate.UpdateRightSideDynamic(RightSideSelectionViewModel.DataModelPath);
|
||||||
DataModelConditionListPredicate.UpdateRightSideDynamic(
|
|
||||||
RightSideSelectionViewModel.SelectedPropertyViewModel.DataModel,
|
|
||||||
RightSideSelectionViewModel.SelectedPropertyViewModel.Path
|
|
||||||
);
|
|
||||||
}
|
|
||||||
else if (DataModelConditionListPredicate.PredicateType == ListRightSideType.DynamicList)
|
else if (DataModelConditionListPredicate.PredicateType == ListRightSideType.DynamicList)
|
||||||
{
|
DataModelConditionListPredicate.UpdateRightSideDynamicList(RightSideSelectionViewModel.DataModelPath);
|
||||||
DataModelConditionListPredicate.UpdateRightSideDynamic(RightSideSelectionViewModel.SelectedPropertyViewModel.Path);
|
|
||||||
}
|
|
||||||
|
|
||||||
_profileEditorService.UpdateSelectedProfileElement();
|
_profileEditorService.UpdateSelectedProfileElement();
|
||||||
Update();
|
Update();
|
||||||
@ -235,10 +211,13 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
|
|
||||||
private DataModelVisualizationViewModel GetListDataModel()
|
private DataModelVisualizationViewModel GetListDataModel()
|
||||||
{
|
{
|
||||||
|
if (DataModelConditionListPredicate.DataModelConditionList.ListPath?.DataModelGuid == null)
|
||||||
|
throw new ArtemisUIException("Failed to retrieve the list data model VM for this list predicate because it has no list path");
|
||||||
|
|
||||||
DataModelPropertiesViewModel dataModel = _dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true);
|
DataModelPropertiesViewModel dataModel = _dataModelUIService.GetPluginDataModelVisualization(_profileEditorService.GetCurrentModule(), true);
|
||||||
DataModelListViewModel listDataModel = (DataModelListViewModel) dataModel.GetChildByPath(
|
DataModelListViewModel listDataModel = (DataModelListViewModel) dataModel.GetChildByPath(
|
||||||
DataModelConditionListPredicate.DataModelConditionList.ListDataModel.PluginInfo.Guid,
|
DataModelConditionListPredicate.DataModelConditionList.ListPath.DataModelGuid.Value,
|
||||||
DataModelConditionListPredicate.DataModelConditionList.ListPropertyPath
|
DataModelConditionListPredicate.DataModelConditionList.ListPath.Path
|
||||||
);
|
);
|
||||||
|
|
||||||
return listDataModel.GetListTypeViewModel(_dataModelUIService);
|
return listDataModel.GetListTypeViewModel(_dataModelUIService);
|
||||||
@ -287,5 +266,17 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
RightSideSelectionViewModel = null;
|
RightSideSelectionViewModel = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
if (!_isPrimitiveList)
|
||||||
|
{
|
||||||
|
LeftSideSelectionViewModel.PropertySelected -= LeftSideOnPropertySelected;
|
||||||
|
LeftSideSelectionViewModel.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
DisposeRightSideDynamic();
|
||||||
|
DisposeRightSideStatic();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -96,10 +96,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
|
|
||||||
public void ApplyList()
|
public void ApplyList()
|
||||||
{
|
{
|
||||||
DataModelConditionList.UpdateList(
|
DataModelConditionList.UpdateList(TargetSelectionViewModel.DataModelPath);
|
||||||
TargetSelectionViewModel.SelectedPropertyViewModel.DataModel,
|
|
||||||
TargetSelectionViewModel.SelectedPropertyViewModel.Path
|
|
||||||
);
|
|
||||||
_profileEditorService.UpdateSelectedProfileElement();
|
_profileEditorService.UpdateSelectedProfileElement();
|
||||||
|
|
||||||
Update();
|
Update();
|
||||||
@ -107,15 +104,16 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
|
|
||||||
public override void Update()
|
public override void Update()
|
||||||
{
|
{
|
||||||
TargetSelectionViewModel.PopulateSelectedPropertyViewModel(DataModelConditionList.ListDataModel, DataModelConditionList.ListPropertyPath);
|
TargetSelectionViewModel.ChangeDataModelPath(DataModelConditionList.ListPath);
|
||||||
NotifyOfPropertyChange(nameof(SelectedListOperator));
|
NotifyOfPropertyChange(nameof(SelectedListOperator));
|
||||||
|
|
||||||
// Remove VMs of effects no longer applied on the layer
|
// Remove VMs of effects no longer applied on the layer
|
||||||
List<DataModelConditionViewModel> toRemove = Items.Where(c => !DataModelConditionList.Children.Contains(c.Model)).ToList();
|
Items.RemoveRange(Items.Where(c => !DataModelConditionList.Children.Contains(c.Model)).ToList());
|
||||||
// Using RemoveRange breaks our lovely animations
|
|
||||||
foreach (DataModelConditionViewModel conditionViewModel in toRemove)
|
if (DataModelConditionList.ListPath == null || !DataModelConditionList.ListPath.IsValid)
|
||||||
Items.Remove(conditionViewModel);
|
return;
|
||||||
|
|
||||||
|
List<DataModelConditionViewModel> viewModels = new List<DataModelConditionViewModel>();
|
||||||
foreach (DataModelConditionPart childModel in Model.Children)
|
foreach (DataModelConditionPart childModel in Model.Children)
|
||||||
{
|
{
|
||||||
if (Items.Any(c => c.Model == childModel))
|
if (Items.Any(c => c.Model == childModel))
|
||||||
@ -125,8 +123,10 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
|
|
||||||
DataModelConditionGroupViewModel viewModel = _dataModelConditionsVmFactory.DataModelConditionGroupViewModel(dataModelConditionGroup, true);
|
DataModelConditionGroupViewModel viewModel = _dataModelConditionsVmFactory.DataModelConditionGroupViewModel(dataModelConditionGroup, true);
|
||||||
viewModel.IsRootGroup = true;
|
viewModel.IsRootGroup = true;
|
||||||
Items.Add(viewModel);
|
viewModels.Add(viewModel);
|
||||||
}
|
}
|
||||||
|
if (viewModels.Any())
|
||||||
|
Items.AddRange(viewModels);
|
||||||
|
|
||||||
foreach (DataModelConditionViewModel childViewModel in Items)
|
foreach (DataModelConditionViewModel childViewModel in Items)
|
||||||
childViewModel.Update();
|
childViewModel.Update();
|
||||||
|
|||||||
@ -4,6 +4,7 @@ using System.Linq;
|
|||||||
using System.Windows;
|
using System.Windows;
|
||||||
using System.Windows.Media;
|
using System.Windows.Media;
|
||||||
using Artemis.Core;
|
using Artemis.Core;
|
||||||
|
using Artemis.Core.DataModelExpansions;
|
||||||
using Artemis.Core.Services;
|
using Artemis.Core.Services;
|
||||||
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
|
using Artemis.UI.Screens.ProfileEditor.Conditions.Abstract;
|
||||||
using Artemis.UI.Shared;
|
using Artemis.UI.Shared;
|
||||||
@ -116,26 +117,23 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
public override void Update()
|
public override void Update()
|
||||||
{
|
{
|
||||||
LeftSideSelectionViewModel.FilterTypes = _supportedInputTypes.ToArray();
|
LeftSideSelectionViewModel.FilterTypes = _supportedInputTypes.ToArray();
|
||||||
LeftSideSelectionViewModel.PopulateSelectedPropertyViewModel(
|
LeftSideSelectionViewModel.ChangeDataModelPath(DataModelConditionPredicate.LeftPath);
|
||||||
DataModelConditionPredicate.LeftDataModel,
|
|
||||||
DataModelConditionPredicate.LeftPropertyPath
|
Type leftSideType = LeftSideSelectionViewModel.DataModelPath?.GetPropertyType();
|
||||||
);
|
|
||||||
Type leftSideType = LeftSideSelectionViewModel.SelectedPropertyViewModel?.DataModelPath?.GetPropertyType();
|
|
||||||
|
|
||||||
// Get the supported operators
|
// Get the supported operators
|
||||||
Operators.Clear();
|
Operators.Clear();
|
||||||
Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType));
|
Operators.AddRange(_conditionOperatorService.GetConditionOperatorsForType(leftSideType ?? typeof(object)));
|
||||||
if (DataModelConditionPredicate.Operator == null)
|
if (DataModelConditionPredicate.Operator == null)
|
||||||
DataModelConditionPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType)));
|
DataModelConditionPredicate.UpdateOperator(Operators.FirstOrDefault(o => o.SupportsType(leftSideType ?? typeof(object))));
|
||||||
SelectedOperator = DataModelConditionPredicate.Operator;
|
SelectedOperator = DataModelConditionPredicate.Operator;
|
||||||
if (!SelectedOperator.SupportsRightSide)
|
if (SelectedOperator == null || !SelectedOperator.SupportsRightSide)
|
||||||
{
|
{
|
||||||
DisposeRightSideStatic();
|
DisposeRightSideStatic();
|
||||||
DisposeRightSideDynamic();
|
DisposeRightSideDynamic();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure the right side has the proper VM
|
// Ensure the right side has the proper VM
|
||||||
Type targetType = LeftSideSelectionViewModel?.SelectedPropertyViewModel?.DataModelPath?.GetPropertyType();
|
|
||||||
if (DataModelConditionPredicate.PredicateType == ProfileRightSideType.Dynamic && SelectedOperator.SupportsRightSide)
|
if (DataModelConditionPredicate.PredicateType == ProfileRightSideType.Dynamic && SelectedOperator.SupportsRightSide)
|
||||||
{
|
{
|
||||||
DisposeRightSideStatic();
|
DisposeRightSideStatic();
|
||||||
@ -146,34 +144,28 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
RightSideSelectionViewModel.PropertySelected += RightSideOnPropertySelected;
|
RightSideSelectionViewModel.PropertySelected += RightSideOnPropertySelected;
|
||||||
}
|
}
|
||||||
|
|
||||||
RightSideSelectionViewModel.PopulateSelectedPropertyViewModel(
|
RightSideSelectionViewModel.ChangeDataModelPath(DataModelConditionPredicate.RightPath);
|
||||||
DataModelConditionPredicate.RightDataModel,
|
RightSideSelectionViewModel.FilterTypes = new[] {leftSideType};
|
||||||
DataModelConditionPredicate.RightPropertyPath
|
|
||||||
);
|
|
||||||
RightSideSelectionViewModel.FilterTypes = new[] {targetType};
|
|
||||||
}
|
}
|
||||||
else if (SelectedOperator.SupportsRightSide)
|
else if (SelectedOperator.SupportsRightSide)
|
||||||
{
|
{
|
||||||
DisposeRightSideDynamic();
|
DisposeRightSideDynamic();
|
||||||
if (RightSideInputViewModel == null)
|
if (RightSideInputViewModel == null)
|
||||||
{
|
{
|
||||||
RightSideInputViewModel = _dataModelUIService.GetStaticInputViewModel(targetType);
|
RightSideInputViewModel = _dataModelUIService.GetStaticInputViewModel(leftSideType, LeftSideSelectionViewModel.DataModelPath?.GetPropertyDescription());
|
||||||
RightSideInputViewModel.ButtonBrush = (Brush) Application.Current.FindResource("PrimaryHueMidBrush");
|
RightSideInputViewModel.ButtonBrush = (Brush) Application.Current.FindResource("PrimaryHueMidBrush");
|
||||||
RightSideInputViewModel.ValueUpdated += RightSideOnValueEntered;
|
RightSideInputViewModel.ValueUpdated += RightSideOnValueEntered;
|
||||||
}
|
}
|
||||||
|
|
||||||
RightSideInputViewModel.Value = DataModelConditionPredicate.RightStaticValue;
|
RightSideInputViewModel.Value = DataModelConditionPredicate.RightStaticValue;
|
||||||
if (RightSideInputViewModel.TargetType != targetType)
|
if (RightSideInputViewModel.TargetType != leftSideType)
|
||||||
RightSideInputViewModel.UpdateTargetType(targetType);
|
RightSideInputViewModel.UpdateTargetType(leftSideType);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ApplyLeftSide()
|
public void ApplyLeftSide()
|
||||||
{
|
{
|
||||||
DataModelConditionPredicate.UpdateLeftSide(
|
DataModelConditionPredicate.UpdateLeftSide(LeftSideSelectionViewModel.DataModelPath);
|
||||||
LeftSideSelectionViewModel.SelectedPropertyViewModel.DataModel,
|
|
||||||
LeftSideSelectionViewModel.SelectedPropertyViewModel.Path
|
|
||||||
);
|
|
||||||
_profileEditorService.UpdateSelectedProfileElement();
|
_profileEditorService.UpdateSelectedProfileElement();
|
||||||
|
|
||||||
SelectedOperator = DataModelConditionPredicate.Operator;
|
SelectedOperator = DataModelConditionPredicate.Operator;
|
||||||
@ -182,10 +174,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
|
|
||||||
public void ApplyRightSideDynamic()
|
public void ApplyRightSideDynamic()
|
||||||
{
|
{
|
||||||
DataModelConditionPredicate.UpdateRightSide(
|
DataModelConditionPredicate.UpdateRightSideDynamic(RightSideSelectionViewModel.DataModelPath);
|
||||||
RightSideSelectionViewModel.SelectedPropertyViewModel.DataModel,
|
|
||||||
RightSideSelectionViewModel.SelectedPropertyViewModel.Path
|
|
||||||
);
|
|
||||||
_profileEditorService.UpdateSelectedProfileElement();
|
_profileEditorService.UpdateSelectedProfileElement();
|
||||||
|
|
||||||
Update();
|
Update();
|
||||||
@ -193,7 +182,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
|
|
||||||
public void ApplyRightSideStatic(object value)
|
public void ApplyRightSideStatic(object value)
|
||||||
{
|
{
|
||||||
DataModelConditionPredicate.UpdateRightSide(value);
|
DataModelConditionPredicate.UpdateRightSideStatic(value);
|
||||||
_profileEditorService.UpdateSelectedProfileElement();
|
_profileEditorService.UpdateSelectedProfileElement();
|
||||||
|
|
||||||
Update();
|
Update();
|
||||||
@ -212,7 +201,7 @@ namespace Artemis.UI.Screens.ProfileEditor.Conditions
|
|||||||
ApplyLeftSide();
|
ApplyLeftSide();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void RightSideOnPropertySelected(object? sender, DataModelInputDynamicEventArgs e)
|
private void RightSideOnPropertySelected(object sender, DataModelInputDynamicEventArgs e)
|
||||||
{
|
{
|
||||||
ApplyRightSideDynamic();
|
ApplyRightSideDynamic();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -27,7 +27,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.Conditio
|
|||||||
ActiveItem.Update();
|
ActiveItem.Update();
|
||||||
ActiveItem.Updated += ActiveItemOnUpdated;
|
ActiveItem.Updated += ActiveItemOnUpdated;
|
||||||
|
|
||||||
ValueViewModel = dataModelUIService.GetStaticInputViewModel(typeof(TProperty));
|
ValueViewModel = dataModelUIService.GetStaticInputViewModel(typeof(TProperty), null);
|
||||||
ValueViewModel.ValueUpdated += ValueViewModelOnValueUpdated;
|
ValueViewModel.ValueUpdated += ValueViewModelOnValueUpdated;
|
||||||
ValueViewModel.Value = DataBindingCondition.Value;
|
ValueViewModel.Value = DataBindingCondition.Value;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -42,8 +42,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings
|
|||||||
|
|
||||||
DataBindingModes = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(DataBindingModeType)));
|
DataBindingModes = new BindableCollection<ValueDescription>(EnumUtilities.GetAllValuesAndDescriptions(typeof(DataBindingModeType)));
|
||||||
EasingViewModels = new BindableCollection<TimelineEasingViewModel>();
|
EasingViewModels = new BindableCollection<TimelineEasingViewModel>();
|
||||||
TestInputValue = dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), true);
|
TestInputValue = dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), null, true);
|
||||||
TestResultValue = dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), true);
|
TestResultValue = dataModelUIService.GetDataModelDisplayViewModel(typeof(TProperty), null, true);
|
||||||
|
|
||||||
Initialize();
|
Initialize();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -105,7 +105,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa
|
|||||||
|
|
||||||
private void ParameterSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e)
|
private void ParameterSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e)
|
||||||
{
|
{
|
||||||
Modifier.UpdateParameter(e.DataModelVisualizationViewModel.DataModel, e.DataModelVisualizationViewModel.Path);
|
Modifier.UpdateParameter(e.DataModelPath.Target, e.DataModelPath.Path);
|
||||||
_profileEditorService.UpdateSelectedProfileElement();
|
_profileEditorService.UpdateSelectedProfileElement();
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,7 +145,7 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
DynamicSelectionViewModel = null;
|
DynamicSelectionViewModel = null;
|
||||||
StaticInputViewModel = _dataModelUIService.GetStaticInputViewModel(Modifier.ModifierType.ParameterType ?? sourceType);
|
StaticInputViewModel = _dataModelUIService.GetStaticInputViewModel(Modifier.ModifierType.ParameterType ?? sourceType, null);
|
||||||
if (StaticInputViewModel != null)
|
if (StaticInputViewModel != null)
|
||||||
StaticInputViewModel.ValueUpdated += StaticInputViewModelOnValueUpdated;
|
StaticInputViewModel.ValueUpdated += StaticInputViewModelOnValueUpdated;
|
||||||
}
|
}
|
||||||
@ -166,10 +166,11 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa
|
|||||||
SelectedModifierType = Modifier.ModifierType;
|
SelectedModifierType = Modifier.ModifierType;
|
||||||
|
|
||||||
// Parameter
|
// Parameter
|
||||||
if (DynamicSelectionViewModel != null)
|
throw new NotImplementedException();
|
||||||
DynamicSelectionViewModel.PopulateSelectedPropertyViewModel(Modifier.ParameterDataModel, Modifier.ParameterPropertyPath);
|
// if (DynamicSelectionViewModel != null)
|
||||||
else if (StaticInputViewModel != null)
|
// DynamicSelectionViewModel.PopulateSelectedPropertyViewModel(Modifier.ParameterDataModel, Modifier.ParameterPropertyPath);
|
||||||
StaticInputViewModel.Value = Modifier.ParameterStaticValue;
|
// else if (StaticInputViewModel != null)
|
||||||
|
// StaticInputViewModel.Value = Modifier.ParameterStaticValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExecuteSelectModifierTypeCommand(object context)
|
private void ExecuteSelectModifierTypeCommand(object context)
|
||||||
|
|||||||
@ -56,7 +56,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa
|
|||||||
|
|
||||||
public void Update()
|
public void Update()
|
||||||
{
|
{
|
||||||
TargetSelectionViewModel.PopulateSelectedPropertyViewModel(DirectDataBinding.SourceDataModel, DirectDataBinding.SourcePropertyPath);
|
throw new NotImplementedException();
|
||||||
|
// TargetSelectionViewModel.PopulateSelectedPropertyViewModel(DirectDataBinding.SourceDataModel, DirectDataBinding.SourcePropertyPath);
|
||||||
TargetSelectionViewModel.FilterTypes = new[] {DirectDataBinding.DataBinding.GetTargetType()};
|
TargetSelectionViewModel.FilterTypes = new[] {DirectDataBinding.DataBinding.GetTargetType()};
|
||||||
|
|
||||||
CanAddModifier = DirectDataBinding.SourceDataModel != null;
|
CanAddModifier = DirectDataBinding.SourceDataModel != null;
|
||||||
@ -65,7 +66,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa
|
|||||||
|
|
||||||
public object GetTestValue()
|
public object GetTestValue()
|
||||||
{
|
{
|
||||||
return TargetSelectionViewModel.SelectedPropertyViewModel?.GetCurrentValue();
|
throw new NotImplementedException();
|
||||||
|
// return TargetSelectionViewModel.SelectedPropertyViewModel?.GetCurrentValue();
|
||||||
}
|
}
|
||||||
|
|
||||||
#region IDisposable
|
#region IDisposable
|
||||||
@ -112,7 +114,8 @@ namespace Artemis.UI.Screens.ProfileEditor.LayerProperties.DataBindings.DirectDa
|
|||||||
|
|
||||||
private void TargetSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e)
|
private void TargetSelectionViewModelOnPropertySelected(object sender, DataModelInputDynamicEventArgs e)
|
||||||
{
|
{
|
||||||
DirectDataBinding.UpdateSource(e.DataModelVisualizationViewModel.DataModel, e.DataModelVisualizationViewModel.Path);
|
throw new NotImplementedException();
|
||||||
|
// DirectDataBinding.UpdateSource(e.DataModelVisualizationViewModel.DataModel, e.DataModelVisualizationViewModel.Path);
|
||||||
Update();
|
Update();
|
||||||
|
|
||||||
_profileEditorService.UpdateSelectedProfileElement();
|
_profileEditorService.UpdateSelectedProfileElement();
|
||||||
|
|||||||
@ -117,7 +117,7 @@
|
|||||||
<ContentControl Grid.Column="2" s:View.Model="{Binding DisplayViewModel}" FontFamily="Consolas" />
|
<ContentControl Grid.Column="2" s:View.Model="{Binding DisplayViewModel}" FontFamily="Consolas" />
|
||||||
</Grid>
|
</Grid>
|
||||||
</HierarchicalDataTemplate>
|
</HierarchicalDataTemplate>
|
||||||
<HierarchicalDataTemplate DataType="{x:Type dataModel:DataModelListPropertiesViewModel}" ItemsSource="{Binding Children}">
|
<HierarchicalDataTemplate DataType="{x:Type dataModel:DataModelListPropertiesViewModel}" ItemsSource="{Binding DisplayViewModel.Children}">
|
||||||
<TextBlock>
|
<TextBlock>
|
||||||
<Run>List item [</Run><Run Text="{Binding Index, Mode=OneWay}" /><Run>]</Run>
|
<Run>List item [</Run><Run Text="{Binding Index, Mode=OneWay}" /><Run>]</Run>
|
||||||
</TextBlock>
|
</TextBlock>
|
||||||
|
|||||||
@ -50,7 +50,7 @@
|
|||||||
</Entry.SortBy>
|
</Entry.SortBy>
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry DisplayName="All other members" />
|
<Entry DisplayName="All other members" />
|
||||||
<Entry DisplayName="Test Methods" Priority="100">
|
<Entry Priority="100" DisplayName="Test Methods">
|
||||||
<Entry.Match>
|
<Entry.Match>
|
||||||
<And>
|
<And>
|
||||||
<Kind Is="Method" />
|
<Kind Is="Method" />
|
||||||
@ -97,7 +97,7 @@
|
|||||||
</Entry.Match>
|
</Entry.Match>
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry DisplayName="All other members" />
|
<Entry DisplayName="All other members" />
|
||||||
<Entry DisplayName="Test Methods" Priority="100">
|
<Entry Priority="100" DisplayName="Test Methods">
|
||||||
<Entry.Match>
|
<Entry.Match>
|
||||||
<And>
|
<And>
|
||||||
<Kind Is="Method" />
|
<Kind Is="Method" />
|
||||||
@ -112,7 +112,7 @@
|
|||||||
</Entry>
|
</Entry>
|
||||||
</TypePattern>
|
</TypePattern>
|
||||||
<TypePattern DisplayName="Default Pattern">
|
<TypePattern DisplayName="Default Pattern">
|
||||||
<Entry DisplayName="Public Delegates" Priority="100">
|
<Entry Priority="100" DisplayName="Public Delegates">
|
||||||
<Entry.Match>
|
<Entry.Match>
|
||||||
<And>
|
<And>
|
||||||
<Access Is="Public" />
|
<Access Is="Public" />
|
||||||
@ -123,7 +123,7 @@
|
|||||||
<Name />
|
<Name />
|
||||||
</Entry.SortBy>
|
</Entry.SortBy>
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry DisplayName="Public Enums" Priority="100">
|
<Entry Priority="100" DisplayName="Public Enums">
|
||||||
<Entry.Match>
|
<Entry.Match>
|
||||||
<And>
|
<And>
|
||||||
<Access Is="Public" />
|
<Access Is="Public" />
|
||||||
@ -191,7 +191,7 @@
|
|||||||
<Kind Is="Type" />
|
<Kind Is="Type" />
|
||||||
</Entry.Match>
|
</Entry.Match>
|
||||||
</Entry>
|
</Entry>
|
||||||
<Entry DisplayName="Interface Implementations" Priority="100">
|
<Entry Priority="100" DisplayName="Interface Implementations">
|
||||||
<Entry.Match>
|
<Entry.Match>
|
||||||
<And>
|
<And>
|
||||||
<Kind Is="Member" />
|
<Kind Is="Member" />
|
||||||
@ -206,4 +206,10 @@
|
|||||||
</Patterns></s:String>
|
</Patterns></s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FElsewhere/@EntryIndexedValue">ERROR</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FElsewhere/@EntryIndexedValue">ERROR</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FSimpleTypes/@EntryIndexedValue">ERROR</s:String>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FSimpleTypes/@EntryIndexedValue">ERROR</s:String>
|
||||||
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FBuiltInTypes/@EntryIndexedValue">ERROR</s:String></wpf:ResourceDictionary>
|
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=SuggestVarOrType_005FBuiltInTypes/@EntryIndexedValue">ERROR</s:String>
|
||||||
|
<s:String x:Key="/Default/CodeStyle/Naming/CSharpNaming/Abbreviations/=UI/@EntryIndexedValue">UI</s:String>
|
||||||
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpKeepExistingMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpPlaceEmbeddedOnSameLineMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ECSharpUseContinuousIndentInsideBracesMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EAlwaysTreatStructAsNotReorderableMigration/@EntryIndexedValue">True</s:Boolean>
|
||||||
|
<s:Boolean x:Key="/Default/Environment/SettingsMigration/IsMigratorApplied/=JetBrains_002EReSharper_002EPsi_002ECSharp_002ECodeStyle_002ESettingsUpgrade_002EMigrateBlankLinesAroundFieldToBlankLinesAroundProperty/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>
|
||||||
Loading…
x
Reference in New Issue
Block a user