//---------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// @owner [....]
// @backupOwner [....]
//---------------------------------------------------------------------
using System.Data.Common.CommandTrees;
using System.Collections.Generic;
using System.Data.Metadata.Edm;
using System.Diagnostics;
using System.Data.Common.Utils;
using System.Linq;
using System.Globalization;
using System.Data.Common.CommandTrees.ExpressionBuilder;
namespace System.Data.Common.CommandTrees.Internal
{
///
/// PatternMatchRule is a specialization of that uses a Func<DbExpression, bool> 'pattern'
/// to implement and a Func<DbExpression, DbExpression> 'processor' to implement
/// . The 'processor' should return null to indicate that the expression was not
/// successfully processed, otherwise it should return the new result expression.
///
internal class PatternMatchRule : DbExpressionRule
{
private readonly Func isMatch;
private readonly Func process;
private readonly ProcessedAction processed;
private PatternMatchRule(Func matchFunc, Func processor, ProcessedAction onProcessed)
{
this.isMatch = matchFunc;
this.process = processor;
this.processed = onProcessed;
}
internal override bool ShouldProcess(DbExpression expression)
{
return this.isMatch(expression);
}
internal override bool TryProcess(DbExpression expression, out DbExpression result)
{
result = this.process(expression);
return (result != null);
}
internal override ProcessedAction OnExpressionProcessed
{
get { return this.processed; }
}
///
/// Constructs a new PatternMatch rule with the specified pattern, processor and default of
///
internal static PatternMatchRule Create(Func matchFunc, Func processor)
{
return PatternMatchRule.Create(matchFunc, processor, ProcessedAction.Reset);
}
///
/// Constructs a new PatternMatchRule with the specified pattern, processor and
///
internal static PatternMatchRule Create(Func matchFunc, Func processor, ProcessedAction onProcessed)
{
EntityUtil.CheckArgumentNull(matchFunc, "matchFunc");
EntityUtil.CheckArgumentNull(processor, "processor");
return new PatternMatchRule(matchFunc, processor, onProcessed);
}
}
///
/// PatternMatchRuleProcessor is a specialization of that uses a collection of s
/// as its ruleset. The static Create methods can be used to construct a new PatternMatchRuleProcessor that applies the specified PatternMatchRules, which is
/// returned as a Func<DbExpression, DbExpression> that can be invoked directly on an expression to apply the ruleset to it.
///
internal class PatternMatchRuleProcessor : DbExpressionRuleProcessingVisitor
{
private readonly System.Collections.ObjectModel.ReadOnlyCollection ruleSet;
private PatternMatchRuleProcessor(System.Collections.ObjectModel.ReadOnlyCollection rules)
{
Debug.Assert(rules.Count() != 0, "At least one PatternMatchRule is required");
Debug.Assert(rules.Where(r => r == null).Count() == 0, "Individual PatternMatchRules must not be null");
this.ruleSet = rules;
}
private DbExpression Process(DbExpression expression)
{
EntityUtil.CheckArgumentNull(expression, "expression");
expression = this.VisitExpression(expression);
return expression;
}
protected override IEnumerable GetRules()
{
return this.ruleSet;
}
internal static Func Create(params PatternMatchRule[] rules)
{
EntityUtil.CheckArgumentNull(rules, "rules");
return new PatternMatchRuleProcessor(new System.Collections.ObjectModel.ReadOnlyCollection(rules)).Process;
}
}
///
/// Provides a means of constructing Func<DbExpression, bool> 'patterns' for use with s.
///
internal static class Patterns
{
#region Pattern Combinators
///
/// Constructs a new pattern that is matched iff both and are matched. Does NOT return a pattern that matches . Use with an argument of to match an AND expression
///
internal static Func And(Func pattern1, Func pattern2)
{
return (e => pattern1(e) && pattern2(e));
}
///
/// Constructs a new pattern that is matched iff all of , and are matched. Does NOT return a pattern that matches . Use with an argument of to match an AND expression
///
internal static Func And(Func pattern1, Func pattern2, Func pattern3)
{
return (e => pattern1(e) && pattern2(e) && pattern3(e));
}
///
/// Constructs a new pattern that is matched if either or are matched. Does NOT return a pattern that matches . Use with an argument of to match an OR expression
///
internal static Func Or(Func pattern1, Func pattern2)
{
return (e => pattern1(e) || pattern2(e));
}
///
/// Constructs a new pattern that is matched if either , or are matched. Does NOT return a pattern that matches . Use with an argument of to match an OR expression
///
internal static Func Or(Func pattern1, Func pattern2, Func pattern3)
{
return (e => pattern1(e) || pattern2(e) || pattern3(e));
}
#if _ENABLE_UNUSED_PATTERNS_
///
/// Constructs a new pattern that is matched iff the argument pattern is not matched. Does NOT return a pattern that matches . Use with an argument of to match a NOT expression
///
internal static Func Not(Func pattern)
{
return (e => !pattern(e));
}
#endif
#endregion
#region Constant Patterns
///
/// Returns a pattern that will match any expression, returning true for any argument, including null.
///
internal static Func AnyExpression { get { return (e => true); } }
///
/// Returns a pattern that will match any collection of expressions, returning true for any argument, including a null or empty enumerable.
///
internal static Func, bool> AnyExpressions { get { return (elems => true); } }
#endregion
#region Result Type Patterns
#if _ENABLE_UNUSED_PATTERNS_
///
/// Returns a pattern that is matched if the the argument has a Boolean result type
///
internal static Func MatchBooleanType { get { return (e => TypeSemantics.IsBooleanType(e.ResultType)); } }
#endif
///
/// Returns a pattern that is matched if the argument has a complex result type
///
internal static Func MatchComplexType { get { return (e => TypeSemantics.IsComplexType(e.ResultType)); } }
///
/// Returns a pattern that is matched if the argument has an entity result type
///
internal static Func MatchEntityType { get { return (e => TypeSemantics.IsEntityType(e.ResultType)); } }
///
/// Returns a pattern that is matched if the argument has a row result type
///
internal static Func MatchRowType { get { return (e => TypeSemantics.IsRowType(e.ResultType)); } }
#endregion
#region General Patterns
///
/// Constructs a new pattern that will match an expression with the specified .
///
internal static Func MatchKind(DbExpressionKind kindToMatch)
{
return (e => e.ExpressionKind == kindToMatch);
}
///
/// Constructs a new pattern that will match iff the specified pattern argument is matched for all expressions in the collection argument.
///
internal static Func, bool> MatchForAll(Func elementPattern)
{
return (elems => elems.FirstOrDefault(e => !elementPattern(e)) == null);
}
#if _ENABLE_UNUSED_PATTERNS_
///
/// Constructs a new pattern that will match if the specified pattern argument is matched for any expression in the collection argument.
///
internal static Func, bool> MatchForAny(Func elementPattern)
{
return (elems => elems.FirstOrDefault(e => elementPattern(e)) != null);
}
#endif
#endregion
#region Type-specific Patterns
#if _ENABLE_UNUSED_PATTERNS_
///
/// Returns a pattern that is matched if the argument expression is a
///
internal static Func MatchUnary()
{
return (e => e is DbUnaryExpression);
}
///
/// Constructs a new pattern that is matched iff the argument expression is a and matches
///
internal static Func MatchUnary(Func argumentPattern)
{
return (e => (e is DbUnaryExpression) && argumentPattern(((DbUnaryExpression)e).Argument));
}
#endif
///
/// Returns a pattern that is matched if the argument expression is a
///
internal static Func MatchBinary()
{
return (e => e is DbBinaryExpression);
}
#if _ENABLE_UNUSED_PATTERNS_
///
/// Constructs a new pattern that is matched iff the argument expression is a with left and right subexpressions that match the corresponding and patterns
///
internal static Func MatchBinary(Func leftPattern, Func rightPattern)
{
return (e => { DbBinaryExpression binEx = (e as DbBinaryExpression); return (binEx != null && leftPattern(binEx.Left) && rightPattern(binEx.Right)); });
}
#endif
///
/// Constructs a new pattern that is matched iff the argument expression is a with input and predicate subexpressions that match the corresponding and patterns
///
internal static Func MatchFilter(Func inputPattern, Func predicatePattern)
{
return (e => { if (e.ExpressionKind != DbExpressionKind.Filter) { return false; } else { DbFilterExpression filterEx = (DbFilterExpression)e; return inputPattern(filterEx.Input.Expression) && predicatePattern(filterEx.Predicate); } });
}
///
/// Constructs a new pattern that is matched iff the argument expression is a with input and projection subexpressions that match the corresponding and patterns
///
internal static Func MatchProject(Func inputPattern, Func projectionPattern)
{
return (e => { if (e.ExpressionKind != DbExpressionKind.Project) { return false; } else { DbProjectExpression projectEx = (DbProjectExpression)e; return inputPattern(projectEx.Input.Expression) && projectionPattern(projectEx.Projection); } });
}
///
/// Constructs a new pattern that is matched iff the argument expression is a with 'when' and 'then' subexpression lists that match the specified and collection patterns and an 'else' subexpression that matches the specified expression pattern
///
internal static Func MatchCase(Func, bool> whenPattern, Func, bool> thenPattern, Func elsePattern)
{
return (e => { if (e.ExpressionKind != DbExpressionKind.Case) { return false; } else { DbCaseExpression caseEx = (DbCaseExpression)e; return whenPattern(caseEx.When) && thenPattern(caseEx.Then) && elsePattern(caseEx.Else); } });
}
///
/// Gets a pattern that is matched if the argument expression is a . This property can be used instead of repeated calls to with an argument of
///
internal static Func MatchNewInstance() { return (e => e.ExpressionKind == DbExpressionKind.NewInstance); }
///
/// Constructs a new pattern that is matched iff the argument expression is a with arguments that match the specified collection pattern
///
internal static Func MatchNewInstance(Func, bool> argumentsPattern)
{
return (e => { if (e.ExpressionKind != DbExpressionKind.NewInstance) { return false; } else { DbNewInstanceExpression newInst = (DbNewInstanceExpression)e; return argumentsPattern(newInst.Arguments); } });
}
#endregion
}
}