/*
* QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
* Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
namespace QuantConnect.Util
{
///
/// Provides methods for constructing expressions at runtime
///
public static class ExpressionBuilder
{
///
/// Constructs a selector of the form: x => x.propertyOrField where x is an instance of 'type'
///
/// The type of the parameter in the expression
/// The name of the property or field to bind to
/// A new lambda expression that represents accessing the property or field on 'type'
public static LambdaExpression MakePropertyOrFieldSelector(Type type, string propertyOrField)
{
var parameter = Expression.Parameter(type);
var property = Expression.PropertyOrField(parameter, propertyOrField);
var lambda = Expression.Lambda(property, parameter);
return lambda;
}
///
/// Constructs a selector of the form: x => x.propertyOrField where x is an instance of 'type'
///
/// The type of the parameter in the expression
/// The type of the property or field being accessed in the expression
/// The name of the property or field to bind to
/// A new lambda expression that represents accessing the property or field on 'type'
public static Expression> MakePropertyOrFieldSelector(string propertyOrField)
{
return (Expression>) MakePropertyOrFieldSelector(typeof (T), propertyOrField);
}
///
/// Constructs a lambda expression that accepts two parameters of type and applies
/// the specified binary comparison and returns the boolean result.
///
public static Expression> MakeBinaryComparisonLambda(ExpressionType type)
{
if (!type.IsBinaryComparison())
{
throw new ArgumentException($"Provided ExpressionType '{type}' is not a binary comparison.");
}
var left = Expression.Parameter(typeof(T), "left");
var right = Expression.Parameter(typeof(T), "right");
var body = Expression.MakeBinary(type, left, right);
var lambda = Expression.Lambda>(body, left, right);
return lambda;
}
///
/// Determines whether or not the specified is a binary comparison.
///
public static bool IsBinaryComparison(this ExpressionType type)
{
switch (type)
{
case ExpressionType.Equal:
case ExpressionType.NotEqual:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
return true;
default:
return false;
}
}
///
/// Converts the specified expression into an enumerable of expressions by walking the expression tree
///
/// The expression to enumerate
/// An enumerable containing all expressions in the input expression
public static IEnumerable AsEnumerable(this Expression expression)
{
var walker = new ExpressionWalker();
walker.Visit(expression);
return walker.Expressions;
}
///
/// Returns all the expressions of the specified type in the given expression tree
///
/// The type of expression to search for
/// The expression to search
/// All expressions of the given type in the specified expression
public static IEnumerable OfType(this Expression expression)
where T : Expression
{
return expression.AsEnumerable().OfType();
}
///
/// Returns the single expression of the specified type or throws if none or more than one expression
/// of the specified type is contained within the expression.
///
/// The type of expression to search for
/// The expression to search
/// Expression of the specified type
public static T Single(this Expression expression)
where T : Expression
{
return expression.AsEnumerable().OfType().Single();
}
///
/// Returns the single expression of the specified type or throws if none or more than one expression
/// of the specified type is contained within the expression.
///
/// The type of expression to search for
/// The expressions to search
/// Expression of the specified type
public static T Single(this IEnumerable expressions)
where T : Expression
{
return expressions.OfType().Single();
}
private class ExpressionWalker : ExpressionVisitor
{
public readonly HashSet Expressions = new HashSet();
public override Expression Visit(Expression node)
{
Expressions.Add(node);
return base.Visit(node);
}
}
}
}