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 }