using JetBrains.Annotations;
using System.Collections.ObjectModel;
using System.Linq.Expressions;
using System.Runtime.CompilerServices;
namespace VberZero.Tools;
// Codes below are taken from https://github.com/scottksmith95/LINQKit project.
/// The Predicate Operator
public enum PredicateOperator
{
/// The "Or"
Or,
/// The "And"
And
}
///
/// See http://www.albahari.com/expressions for information and examples.
///
public static class PredicateBuilder
{
private class RebindParameterVisitor : ExpressionVisitor
{
private readonly ParameterExpression _oldParameter;
private readonly ParameterExpression _newParameter;
public RebindParameterVisitor(ParameterExpression oldParameter, ParameterExpression newParameter)
{
_oldParameter = oldParameter;
_newParameter = newParameter;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node == _oldParameter)
{
return _newParameter;
}
return base.VisitParameter(node);
}
}
/// Start an expression
public static ExpressionStarter New(Expression>? expr = null)
{ return new ExpressionStarter(expr); }
/// Create an expression with a stub expression true or false to use when the expression is not yet started.
public static ExpressionStarter New(bool defaultExpression)
{ return new ExpressionStarter(defaultExpression); }
/// Always true
[Obsolete("Use PredicateBuilder.New() instead.")]
public static Expression> True()
{ return new ExpressionStarter(true); }
/// Always false
[Obsolete("Use PredicateBuilder.New() instead.")]
public static Expression> False()
{ return new ExpressionStarter(false); }
/// OR
public static Expression> Or([NotNull] this Expression> expr1, [NotNull] Expression> expr2)
{
var expr2Body = new RebindParameterVisitor(expr2.Parameters[0], expr1.Parameters[0]).Visit(expr2.Body);
return Expression.Lambda>(Expression.OrElse(expr1.Body, expr2Body), expr1.Parameters);
}
/// AND
public static Expression> And([NotNull] this Expression> expr1, [NotNull] Expression> expr2)
{
var expr2Body = new RebindParameterVisitor(expr2.Parameters[0], expr1.Parameters[0]).Visit(expr2.Body);
return Expression.Lambda>(Expression.AndAlso(expr1.Body, expr2Body), expr1.Parameters);
}
///
/// Extends the specified source Predicate with another Predicate and the specified PredicateOperator.
///
/// The type
/// The source Predicate.
/// The second Predicate.
/// The Operator (can be "And" or "Or").
/// Expression{Func{T, bool}}
public static Expression> Extend([NotNull] this Expression> first, [NotNull] Expression> second, PredicateOperator @operator = PredicateOperator.Or)
{
return @operator == PredicateOperator.Or ? first.Or(second) : first.And(second);
}
///
/// Extends the specified source Predicate with another Predicate and the specified PredicateOperator.
///
/// The type
/// The source Predicate.
/// The second Predicate.
/// The Operator (can be "And" or "Or").
/// Expression{Func{T, bool}}
public static Expression> Extend([NotNull] this ExpressionStarter first, [NotNull] Expression> second, PredicateOperator @operator = PredicateOperator.Or)
{
return @operator == PredicateOperator.Or ? first.Or(second) : first.And(second);
}
}
///
/// ExpressionStarter{T} which eliminates the default 1=0 or 1=1 stub expressions
///
/// The type
public class ExpressionStarter
{
internal ExpressionStarter() : this(false)
{
}
internal ExpressionStarter(bool defaultExpression)
{
if (defaultExpression)
DefaultExpression = f => true;
else
DefaultExpression = f => false;
}
internal ExpressionStarter(Expression> exp) : this(false)
{
_predicate = exp;
}
/// The actual Predicate. It can only be set by calling Start.
private Expression> Predicate => (IsStarted || !UseDefaultExpression) ? _predicate : DefaultExpression;
private Expression> _predicate;
/// Determines if the predicate is started.
public bool IsStarted => _predicate != null;
/// A default expression to use only when the expression is null
public bool UseDefaultExpression => DefaultExpression != null;
/// The default expression
public Expression> DefaultExpression { get; set; }
/// Set the Expression predicate
/// The first expression
public Expression> Start(Expression> exp)
{
if (IsStarted)
throw new Exception("Predicate cannot be started again.");
return _predicate = exp;
}
/// Or
public Expression> Or([NotNull] Expression> expr2)
{
return (IsStarted) ? _predicate = Predicate.Or(expr2) : Start(expr2);
}
/// And
public Expression> And([NotNull] Expression> expr2)
{
return (IsStarted) ? _predicate = Predicate.And(expr2) : Start(expr2);
}
/// Show predicate string
public override string ToString()
{
return Predicate == null ? null : Predicate.ToString();
}
#region Implicit Operators
///
/// Allows this object to be implicitely converted to an Expression{Func{T, bool}}.
///
///
public static implicit operator Expression>(ExpressionStarter right)
{
return right == null ? null : right.Predicate;
}
///
/// Allows this object to be implicitely converted to an Expression{Func{T, bool}}.
///
///
public static implicit operator Func(ExpressionStarter right)
{
return right == null ? null : (right.IsStarted || right.UseDefaultExpression) ? right.Predicate.Compile() : null;
}
///
/// Allows this object to be implicitely converted to an Expression{Func{T, bool}}.
///
///
public static implicit operator ExpressionStarter(Expression> right)
{
return right == null ? null : new ExpressionStarter(right);
}
#endregion Implicit Operators
#region Implement Expression methods and properties
#if !(NET35)
///
public Func Compile()
{ return Predicate.Compile(); }
#endif
#if !(NET35 || WINDOWS_APP || NETSTANDARD || PORTABLE || PORTABLE40 || UAP)
///
public Func Compile(DebugInfoGenerator debugInfoGenerator)
{ return Predicate.Compile(debugInfoGenerator); }
///
public Expression> Update(Expression body, IEnumerable parameters)
{ return Predicate.Update(body, parameters); }
#endif
#endregion Implement Expression methods and properties
#region Implement LamdaExpression methods and properties
///
public Expression Body => Predicate.Body;
///
public ExpressionType NodeType => Predicate.NodeType;
///
public ReadOnlyCollection Parameters => Predicate.Parameters;
///
public Type Type => Predicate.Type;
#if !(NET35)
///
public string Name => Predicate.Name;
///
public Type ReturnType => Predicate.ReturnType;
///
public bool TailCall => Predicate.TailCall;
#endif
#endregion Implement LamdaExpression methods and properties
#region Implement Expression methods and properties
#if !(NET35)
///
public virtual bool CanReduce => Predicate.CanReduce;
#endif
#endregion Implement Expression methods and properties
}