diff --git a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionGroup.cs b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionGroup.cs index 46577f280..aa92c2476 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionGroup.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionGroup.cs @@ -10,12 +10,13 @@ namespace Artemis.Core.Models.Profile.Conditions { Parent = parent; EntityId = Guid.NewGuid(); + DisplayConditionGroupEntity = new DisplayConditionGroupEntity(); } public DisplayConditionGroup(DisplayConditionPart parent, DisplayConditionGroupEntity entity) { - DisplayConditionGroupEntity = entity; Parent = parent; + DisplayConditionGroupEntity = entity; EntityId = DisplayConditionGroupEntity.Id; BooleanOperator = (BooleanOperator) DisplayConditionGroupEntity.BooleanOperator; diff --git a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionPredicate.cs b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionPredicate.cs index 6536352e3..8dca8a826 100644 --- a/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionPredicate.cs +++ b/src/Artemis.Core/Models/Profile/Conditions/DisplayConditionPredicate.cs @@ -1,9 +1,10 @@ using System; using System.Linq; using System.Linq.Expressions; +using Artemis.Core.Exceptions; +using Artemis.Core.Extensions; using Artemis.Core.Models.Profile.Conditions.Abstract; using Artemis.Core.Plugins.Abstract.DataModels; -using Artemis.Core.Services.Interfaces; using Artemis.Storage.Entities.Profile; namespace Artemis.Core.Models.Profile.Conditions @@ -13,12 +14,17 @@ namespace Artemis.Core.Models.Profile.Conditions public DisplayConditionPredicate(DisplayConditionPart parent) { Parent = parent; + DisplayConditionPredicateEntity = new DisplayConditionPredicateEntity(); } public DisplayConditionPredicate(DisplayConditionPart parent, DisplayConditionPredicateEntity entity) { Parent = parent; DisplayConditionPredicateEntity = entity; + + // TODO: This has to be done from somewhere + // LeftDataModel = dataModelService.GetPluginDataModelByGuid(DisplayConditionPredicateEntity.LeftDataModelGuid); + // RightDataModel = dataModelService.GetPluginDataModelByGuid(DisplayConditionPredicateEntity.RightDataModelGuid); } public DisplayConditionPredicateEntity DisplayConditionPredicateEntity { get; set; } @@ -26,51 +32,122 @@ namespace Artemis.Core.Models.Profile.Conditions public PredicateType PredicateType { get; set; } public DisplayConditionOperator Operator { get; set; } - public Guid LeftDataModelGuid { get; set; } - public string LeftPropertyPath { get; set; } - - public Guid RightDataModelGuid { get; set; } - public string RightPropertyPath { get; set; } - - // TODO: Implement type-checking or perhaps convert it here - public object RightStaticValue { get; set; } + public DataModel LeftDataModel { get; private set; } + public string LeftPropertyPath { get; private set; } + public DataModel RightDataModel { get; private set; } + public string RightPropertyPath { get; private set; } + public object RightStaticValue { get; private set; } public Expression> DynamicConditionLambda { get; private set; } public Func CompiledDynamicConditionLambda { get; private set; } public Expression> StaticConditionLambda { get; private set; } public Func CompiledStaticConditionLambda { get; private set; } - public void CreateExpression(IDataModelService dataModelService) + public void UpdateLeftSide(DataModel dataModel, string path) { - if (PredicateType == PredicateType.Dynamic) - CreateDynamicExpression(dataModelService); - else - CreateStaticExpression(dataModelService); + 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; + + ValidateRightSide(); + CreateExpression(); } - private void CreateDynamicExpression(IDataModelService dataModelService) + public void UpdateRightSide(DataModel dataModel, string path) { - if (LeftDataModelGuid == Guid.Empty || string.IsNullOrWhiteSpace(LeftPropertyPath)) - return; - if (RightDataModelGuid == Guid.Empty || string.IsNullOrWhiteSpace(RightPropertyPath)) - return; + if (!dataModel.ContainsPath(path)) + throw new ArtemisCoreException($"Data model of type {dataModel.GetType().Name} does not contain a property at path '{path}'"); - var leftDataModel = dataModelService.GetPluginDataModelByGuid(LeftDataModelGuid); - if (leftDataModel == null) - return; + PredicateType = PredicateType.Dynamic; + RightDataModel = dataModel; + RightPropertyPath = path; - var rightDataModel = dataModelService.GetPluginDataModelByGuid(RightDataModelGuid); - if (rightDataModel == null) + CreateExpression(); + } + + public void UpdateRightSide(object staticValue) + { + PredicateType = PredicateType.Static; + RightDataModel = null; + RightPropertyPath = null; + + SetStaticValue(staticValue); + CreateExpression(); + } + + public void CreateExpression() + { + if (PredicateType == PredicateType.Dynamic) + CreateDynamicExpression(); + else + CreateStaticExpression(); + } + + public override void ApplyToEntity() + { + } + + /// + /// Validates the right side, ensuring it is still compatible with the current left side + /// + private void ValidateRightSide() + { + var leftSideType = LeftDataModel.GetTypeAtPath(LeftPropertyPath); + if (PredicateType == PredicateType.Dynamic) + { + var rightSideType = RightDataModel.GetTypeAtPath(RightPropertyPath); + if (!leftSideType.IsCastableFrom(rightSideType)) + UpdateRightSide(null, null); + } + else + { + // Just update the value with itself, it'll validate :) + UpdateRightSide(RightStaticValue); + } + } + + /// + /// Updates the current static value, ensuring it is a valid type. This assumes the types are compatible if they + /// differ. + /// + private void SetStaticValue(object staticValue) + { + // If the left side is empty simply apply the value, any validation will wait + if (LeftDataModel == null) + { + RightStaticValue = staticValue; + return; + } + + var leftSideType = LeftDataModel.GetTypeAtPath(LeftPropertyPath); + + // If not null ensure the types match and if not, convert it + if (staticValue != null && staticValue.GetType() == leftSideType) + RightStaticValue = staticValue; + else if (staticValue != null) + RightStaticValue = Convert.ChangeType(staticValue, leftSideType); + // If null create a default instance for value types or simply make it null for reference types + else if (leftSideType.IsValueType) + RightStaticValue = Activator.CreateInstance(leftSideType); + else + RightStaticValue = null; + } + + private void CreateDynamicExpression() + { + if (LeftDataModel == null || RightDataModel == null) return; var leftSideParameter = Expression.Parameter(typeof(DataModel), "leftDataModel"); var leftSideAccessor = LeftPropertyPath.Split('.').Aggregate( - Expression.Convert(leftSideParameter, leftDataModel.GetType()), // Cast to the appropriate type + Expression.Convert(leftSideParameter, LeftDataModel.GetType()), // Cast to the appropriate type Expression.Property ); var rightSideParameter = Expression.Parameter(typeof(DataModel), "rightDataModel"); var rightSideAccessor = RightPropertyPath.Split('.').Aggregate( - Expression.Convert(rightSideParameter, rightDataModel.GetType()), // Cast to the appropriate type + Expression.Convert(rightSideParameter, LeftDataModel.GetType()), // Cast to the appropriate type Expression.Property ); @@ -85,18 +162,14 @@ namespace Artemis.Core.Models.Profile.Conditions CompiledDynamicConditionLambda = DynamicConditionLambda.Compile(); } - private void CreateStaticExpression(IDataModelService dataModelService) + private void CreateStaticExpression() { - if (LeftDataModelGuid == Guid.Empty || string.IsNullOrWhiteSpace(LeftPropertyPath)) - return; - - var leftDataModel = dataModelService.GetPluginDataModelByGuid(LeftDataModelGuid); - if (leftDataModel == null) + if (LeftDataModel == null) return; var leftSideParameter = Expression.Parameter(typeof(DataModel), "leftDataModel"); var leftSideAccessor = LeftPropertyPath.Split('.').Aggregate( - Expression.Convert(leftSideParameter, leftDataModel.GetType()), // Cast to the appropriate type + Expression.Convert(leftSideParameter, LeftDataModel.GetType()), // Cast to the appropriate type Expression.Property ); @@ -110,11 +183,6 @@ namespace Artemis.Core.Models.Profile.Conditions StaticConditionLambda = Expression.Lambda>(conditionExpression, leftSideParameter); CompiledStaticConditionLambda = StaticConditionLambda.Compile(); } - - public override void ApplyToEntity() - { - - } } public enum PredicateType diff --git a/src/Artemis.Core/Plugins/Abstract/DataModels/DataModel.cs b/src/Artemis.Core/Plugins/Abstract/DataModels/DataModel.cs index 0c8882e9a..5720867d1 100644 --- a/src/Artemis.Core/Plugins/Abstract/DataModels/DataModel.cs +++ b/src/Artemis.Core/Plugins/Abstract/DataModels/DataModel.cs @@ -1,4 +1,5 @@ -using Artemis.Core.Plugins.Abstract.DataModels.Attributes; +using System; +using Artemis.Core.Plugins.Abstract.DataModels.Attributes; using Artemis.Core.Plugins.Models; namespace Artemis.Core.Plugins.Abstract.DataModels @@ -16,5 +17,39 @@ namespace Artemis.Core.Plugins.Abstract.DataModels /// [DataModelIgnore] public DataModelPropertyAttribute DataModelDescription { get; internal set; } + + public bool ContainsPath(string path) + { + var parts = path.Split('.'); + var current = GetType(); + foreach (var part in parts) + { + var property = current?.GetProperty(part); + current = property?.PropertyType; + if (property == null) + return false; + } + + return true; + } + + public Type GetTypeAtPath(string path) + { + if (!ContainsPath(path)) + return null; + + var parts = path.Split('.'); + var current = GetType(); + + Type result = null; + foreach (var part in parts) + { + var property = current.GetProperty(part); + current = property.PropertyType; + result = property.PropertyType; + } + + return result; + } } } \ No newline at end of file diff --git a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs index 53d93bbe5..cc1042542 100644 --- a/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs +++ b/src/Artemis.UI.Shared/DataModelVisualization/Shared/DataModelVisualizationViewModel.cs @@ -4,6 +4,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using Artemis.Core.Extensions; +using Artemis.Core.Models.Profile.Conditions; using Artemis.Core.Plugins.Abstract.DataModels; using Artemis.Core.Plugins.Abstract.DataModels.Attributes; using Artemis.UI.Shared.Exceptions; @@ -113,6 +114,11 @@ namespace Artemis.UI.Shared.DataModelVisualization.Shared IsMatchingFilteredTypes = filteredTypes.Any(t => t == PropertyInfo.PropertyType); } + public DataModelVisualizationViewModel GetChildForCondition(DisplayConditionPredicate predicate) + { + + } + public DataModelVisualizationViewModel GetChildByPath(Guid dataModelGuid, string propertyPath) { var path = propertyPath.Split(".");