using System;
using System.Linq.Expressions;
using System.Reflection;
namespace Artemis.Core
{
///
/// Represents a data binding converter that acts as the bridge between a
/// and a
///
public abstract class DataBindingConverter : IDataBindingConverter
{
///
/// Gets a dynamically compiled getter pointing to the data bound property
///
public Func GetExpression { get; private set; }
///
/// Gets a dynamically compiled setter pointing to the data bound property used for value types
///
public Action ValueTypeSetExpression { get; private set; }
///
/// Gets a dynamically compiled setter pointing to the data bound property used for reference types
///
public Action ReferenceTypeSetExpression { get; private set; }
///
/// Gets the data binding this converter is applied to
///
public DataBinding DataBinding { get; private set; }
///
/// Gets whether or not this data binding converter supports the method
///
public bool SupportsSum { get; protected set; }
///
/// Gets whether or not this data binding converter supports the method
///
public bool SupportsInterpolate { get; protected set; }
///
public Type SupportedType => typeof(TProperty);
///
/// Returns the sum of and
///
public abstract TProperty Sum(TProperty a, TProperty b);
///
/// Returns the the interpolated value between and on a scale (generally)
/// between 0.0 and 1.0 defined by the
/// Note: The progress may go be negative or go beyond 1.0 depending on the easing method used
///
/// The value to interpolate away from
/// The value to interpolate towards
/// The progress of the interpolation between 0.0 and 1.0
///
public abstract TProperty Interpolate(TProperty a, TProperty b, double progress);
///
/// Applies the to the layer property
///
///
public virtual void ApplyValue(TProperty value)
{
if (ReferenceTypeSetExpression != null)
ReferenceTypeSetExpression(DataBinding.LayerProperty.CurrentValue, value);
else if (ValueTypeSetExpression != null)
ValueTypeSetExpression(value);
}
///
/// Returns the current base value of the data binding
///
public virtual TProperty GetValue()
{
return GetExpression(DataBinding.LayerProperty.CurrentValue);
}
///
/// Converts the provided object to a type of
///
public virtual TProperty ConvertFromObject(object? source)
{
return (TProperty) Convert.ChangeType(source, typeof(TProperty));
}
///
/// Called when the data binding converter has been initialized and the is available
///
protected virtual void OnInitialized()
{
}
internal void Initialize(DataBinding dataBinding)
{
DataBinding = dataBinding;
GetExpression = dataBinding.Registration.PropertyExpression.Compile();
CreateSetExpression();
OnInitialized();
}
private void CreateSetExpression()
{
// If the registration does not point towards a member of LayerProperty.CurrentValue, assign directly to LayerProperty.CurrentValue
if (DataBinding.Registration.Member == null)
{
CreateSetCurrentValueExpression();
return;
}
// Ensure the member of LayerProperty.CurrentValue has a setter
MethodInfo setterMethod = null;
if (DataBinding.Registration.Member is PropertyInfo propertyInfo)
setterMethod = propertyInfo.GetSetMethod();
// If there is no setter, the built-in data binding cannot do its job, stay null
if (setterMethod == null)
return;
// If LayerProperty.CurrentValue is a value type, assign it directly to LayerProperty.CurrentValue after applying the changes
if (typeof(TLayerProperty).IsValueType)
CreateSetValueTypeExpression();
// If it is a reference type it can safely be updated by its reference
else
CreateSetReferenceTypeExpression();
}
private void CreateSetReferenceTypeExpression()
{
ParameterExpression propertyValue = Expression.Parameter(typeof(TProperty), "propertyValue");
ParameterExpression parameter = Expression.Parameter(typeof(TLayerProperty), "currentValue");
MemberExpression memberAccess = Expression.MakeMemberAccess(parameter, DataBinding.Registration.Member);
BinaryExpression assignment = Expression.Assign(memberAccess, propertyValue);
Expression> referenceTypeLambda = Expression.Lambda>(assignment, parameter, propertyValue);
ReferenceTypeSetExpression = referenceTypeLambda.Compile();
}
private void CreateSetValueTypeExpression()
{
ParameterExpression propertyValue = Expression.Parameter(typeof(TProperty), "propertyValue");
ParameterExpression variableCurrent = Expression.Variable(typeof(TLayerProperty), "current");
ConstantExpression layerProperty = Expression.Constant(DataBinding.LayerProperty);
MemberExpression layerPropertyMemberAccess = Expression.MakeMemberAccess(layerProperty,
DataBinding.LayerProperty.GetType().GetMember(nameof(DataBinding.LayerProperty.CurrentValue))[0]);
BlockExpression body = Expression.Block(
new[] {variableCurrent},
Expression.Assign(variableCurrent, layerPropertyMemberAccess),
Expression.Assign(Expression.MakeMemberAccess(variableCurrent, DataBinding.Registration.Member), propertyValue),
Expression.Assign(layerPropertyMemberAccess, variableCurrent)
);
Expression> valueTypeLambda = Expression.Lambda>(body, propertyValue);
ValueTypeSetExpression = valueTypeLambda.Compile();
}
private void CreateSetCurrentValueExpression()
{
ParameterExpression propertyValue = Expression.Parameter(typeof(TProperty), "propertyValue");
ConstantExpression layerProperty = Expression.Constant(DataBinding.LayerProperty);
MemberExpression layerPropertyMemberAccess = Expression.MakeMemberAccess(layerProperty,
DataBinding.LayerProperty.GetType().GetMember(nameof(DataBinding.LayerProperty.CurrentValue))[0]);
BinaryExpression body = Expression.Assign(layerPropertyMemberAccess, propertyValue);
Expression> lambda = Expression.Lambda>(body, propertyValue);
ValueTypeSetExpression = lambda.Compile();
}
}
}