Expressions/Binary/BinaryExpressionBase.cs
using Expressive.Exceptions;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
namespace Expressive.Expressions.Binary
{
/// <summary>
/// Base class implementation of <see cref="IExpression"/>.
/// </summary>
public abstract class BinaryExpressionBase : IExpression
{
#region Fields
private readonly IExpression leftHandSide;
private readonly IExpression rightHandSide;
#endregion
/// <summary>
/// Gets the underlying <see cref="Context"/>.
/// </summary>
protected Context Context { get; }
#region Constructors
/// <summary>
/// Initializes a new instance of <see cref="BinaryExpressionBase"/>.
/// </summary>
/// <param name="lhs">The left hand <see cref="IExpression"/>.</param>
/// <param name="rhs">The right hand <see cref="IExpression"/>.</param>
/// <param name="context">The <see cref="Context"/> used during the evaluation.</param>
protected BinaryExpressionBase(IExpression lhs, IExpression rhs, Context context)
{
this.leftHandSide = lhs;
this.Context = context;
this.rightHandSide = rhs;
}
#endregion
#region IExpression Members
/// <inheritdoc />
public object Evaluate(IDictionary<string, object> variables)
{
if (this.leftHandSide is null)
{
throw new MissingParticipantException("The left hand side of the operation is missing.");
}
if (this.rightHandSide is null)
{
throw new MissingParticipantException("The right hand side of the operation is missing.");
}
// We will evaluate the left hand side but hold off on the right hand side as it may not be necessary
var lhsResult = this.leftHandSide.Evaluate(variables);
return this.EvaluateImpl(lhsResult, this.rightHandSide, variables);
}
#endregion
/// <summary>
/// The core evaluation logic for the overriding expression implementation.
/// </summary>
/// <param name="lhsResult">The already evaluated result for the left hand side of the expression.</param>
/// <param name="rightHandSide">
/// The <see cref="IExpression"/> right hand side of the expression.
/// <remarks>
/// This is left up to the implementor to evaluate to allow for short-circuiting.
/// </remarks>
/// </param>
/// <param name="variables">The list of variables for use in evaluating.</param>
/// <returns>The result of the evaluation.</returns>
protected abstract object EvaluateImpl(object lhsResult, IExpression rightHandSide, IDictionary<string, object> variables);
/// <summary>
/// Evaluates the supplied <paramref name="lhsResult"/> and <paramref name="rhs"/> and checks for any possible aggregate value results.
/// </summary>
/// <param name="lhsResult">The left hand side result.</param>
/// <param name="rhs">The <see cref="IExpression"/> right hand side of the expression.</param>
/// <param name="variables">The list of variables for use in evaluating.</param>
/// <param name="resultSelector">How to return the result(s). <remarks>NOTE this will be called once per aggregate value if they exist.</remarks></param>
/// <returns>The result of the evaluation.</returns>
public static object EvaluateAggregates(object lhsResult, IExpression rhs, IDictionary<string, object> variables, Func<object, object, object> resultSelector)
{
if (rhs is null)
{
throw new ArgumentNullException(nameof(rhs));
}
if (resultSelector is null)
{
throw new ArgumentNullException(nameof(resultSelector));
}
IList<object> lhsParticipants = new List<object>();
IList<object> rhsParticipants = new List<object>();
var rhsResult = rhs.Evaluate(variables);
if (!(lhsResult is ICollection) && !(rhsResult is ICollection))
{
return resultSelector(lhsResult, rhsResult);
}
if (lhsResult is ICollection leftCollection)
{
foreach (var item in leftCollection)
{
lhsParticipants.Add(item);
}
}
if (rhsResult is ICollection rightCollection)
{
foreach (var item in rightCollection)
{
rhsParticipants.Add(item);
}
}
object[] result = null;
if (lhsParticipants.Count == rhsParticipants.Count)
{
IList<object> resultList = new List<object>();
for (var i = 0; i < lhsParticipants.Count; i++)
{
resultList.Add(resultSelector(lhsParticipants[i], rhsParticipants[i]));
}
result = resultList.ToArray();
}
else if (lhsParticipants.Count == 0)
{
IList<object> resultList = new List<object>();
for (var i = 0; i < rhsParticipants.Count; i++)
{
resultList.Add(resultSelector(lhsResult, rhsParticipants[i]));
}
result = resultList.ToArray();
}
else if (rhsParticipants.Count == 0)
{
IList<object> resultList = new List<object>();
for (var i = 0; i < lhsParticipants.Count; i++)
{
resultList.Add(resultSelector(lhsParticipants[i], rhsResult));
}
result = resultList.ToArray();
}
return result;
}
}
}