//---------------------------------------------------------------------
//
// Copyright (c) Microsoft Corporation. All rights reserved.
//
//
// @owner [....]
// @backupOwner [....]
//---------------------------------------------------------------------
using System.Collections.Generic;
using System.Data.Common.CommandTrees;
using System.Data.Common.Utils;
using System.Data.Common.Utils.Boolean;
using System.Data.Entity;
using System.Diagnostics;
using System.Linq;
using System.Text;
namespace System.Data.Mapping.ViewGeneration.Structures
{
using BoolDomainConstraint = DomainConstraint;
using DomainAndExpr = AndExpr>;
using DomainBoolExpr = BoolExpr>;
using DomainFalseExpr = FalseExpr>;
using DomainNotExpr = NotExpr>;
using DomainOrExpr = OrExpr>;
using DomainTermExpr = TermExpr>;
using DomainTrueExpr = TrueExpr>;
// This class represents an arbitrary boolean expression
internal partial class BoolExpression : InternalBase
{
#region Constructors
// effects: Create a boolean expression from a literal value
internal static BoolExpression CreateLiteral(BoolLiteral literal, MemberDomainMap memberDomainMap)
{
DomainBoolExpr expr = literal.GetDomainBoolExpression(memberDomainMap);
return new BoolExpression(expr, memberDomainMap);
}
// effects: Creates a new boolean expression using the memberDomainMap of this expression
internal BoolExpression Create(BoolLiteral literal)
{
DomainBoolExpr expr = literal.GetDomainBoolExpression(m_memberDomainMap);
return new BoolExpression(expr, m_memberDomainMap);
}
// effects: Create a boolean expression of the form "NOT expression"
internal static BoolExpression CreateNot(BoolExpression expression)
{
return new BoolExpression(ExprType.Not, new BoolExpression[] { expression });
}
// effects: Create a boolean expression of the form "children[0] AND
// children[1] AND ..."
internal static BoolExpression CreateAnd(params BoolExpression[] children)
{
return new BoolExpression(ExprType.And, children);
}
// effects: Create a boolean expression of the form "children[0] OR
// children[1] OR ..."
internal static BoolExpression CreateOr(params BoolExpression[] children)
{
return new BoolExpression(ExprType.Or, children);
}
internal static BoolExpression CreateAndNot(BoolExpression e1, BoolExpression e2)
{
return CreateAnd(e1, CreateNot(e2));
}
// effects: Creates a new boolean expression using the memberDomainMap of this expression
internal BoolExpression Create(DomainBoolExpr expression)
{
return new BoolExpression(expression, m_memberDomainMap);
}
// effects: Creates a boolean expression corresponding to TRUE (if
// isTrue is true) or FALSE (if isTrue is false)
private BoolExpression(bool isTrue)
{
if (isTrue)
{
m_tree = DomainTrueExpr.Value;
}
else
{
m_tree = DomainFalseExpr.Value;
}
}
// effects: Given the operation type (AND/OR/NOT) and the relevant number of
// children, returns the corresponding bool expression
private BoolExpression(ExprType opType, IEnumerable children)
{
List childList = new List(children);
Debug.Assert(childList.Count > 0);
// If any child is other than true or false, it will have m_memberDomainMap set
foreach (BoolExpression child in children)
{
if (child.m_memberDomainMap != null)
{
m_memberDomainMap = child.m_memberDomainMap;
break;
}
}
switch (opType)
{
case ExprType.And:
m_tree = new DomainAndExpr(ToBoolExprList(childList));
break;
case ExprType.Or:
m_tree = new DomainOrExpr(ToBoolExprList(childList));
break;
case ExprType.Not:
Debug.Assert(childList.Count == 1);
m_tree = new DomainNotExpr(childList[0].m_tree);
break;
default:
Debug.Fail("Unknown expression type");
break;
}
}
// effects: Creates a boolean expression based on expr
internal BoolExpression(DomainBoolExpr expr, MemberDomainMap memberDomainMap)
{
m_tree = expr;
m_memberDomainMap = memberDomainMap;
}
#endregion
#region Fields
private DomainBoolExpr m_tree; // The actual tree that has the expression
// Domain map for various member paths - can be null
private readonly MemberDomainMap m_memberDomainMap;
private Converter m_converter;
internal static readonly IEqualityComparer EqualityComparer = new BoolComparer();
internal static readonly BoolExpression True = new BoolExpression(true);
internal static readonly BoolExpression False = new BoolExpression(false);
#endregion
#region Properties
// requires: this is of the form "True", "Literal" or "Literal AND ... AND Literal".
// effects: Yields the individual atoms in this (for True does not
// yield anything)
internal IEnumerable Atoms
{
get
{
// Create the terms visitor and visit it to get atoms (it
// ensures that there are no ANDs or NOTs in the expression)
IEnumerable atoms = TermVisitor.GetTerms(m_tree, false);
foreach (DomainTermExpr atom in atoms)
{
yield return new BoolExpression(atom, m_memberDomainMap);
}
}
}
// effects: Yields all the leaves in this
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
internal IEnumerable Leaves
{
get
{
// Create the term visitor and visit it to get terms
IEnumerable terms = TermVisitor.GetTerms(m_tree, true);
foreach (DomainTermExpr term in terms)
{
yield return term.Identifier.Variable.Identifier;
}
}
}
// effects: if this expression is a boolean expression of type BoolLiteral
// Returns the literal, else returns null
internal BoolLiteral AsLiteral
{
get
{
DomainTermExpr literal = m_tree as DomainTermExpr;
if (literal == null) { return null; }
BoolLiteral result = GetBoolLiteral(literal);
return result;
}
}
// effects: Given a term expression, extracts the BoolLiteral from it
internal static BoolLiteral GetBoolLiteral(DomainTermExpr term)
{
DomainConstraint domainConstraint = term.Identifier;
DomainVariable variable = domainConstraint.Variable;
return variable.Identifier;
}
// effects: Returns true iff this corresponds to the boolean literal "true"
internal bool IsTrue
{
get
{
return m_tree.ExprType == ExprType.True;
}
}
// effects: Returns true iff this corresponds to the boolean literal "false"
internal bool IsFalse
{
get
{
return m_tree.ExprType == ExprType.False;
}
}
// effects: Returns true if the expression always evaluates to true
internal bool IsAlwaysTrue()
{
InitializeConverter();
return m_converter.Vertex.IsOne();
}
// effects: Returns true if there is a possible assignment to
// variables in this such that the expression evaluates to true
internal bool IsSatisfiable()
{
return !IsUnsatisfiable();
}
// effects: Returns true if there is no possible assignment to
// variables in this such that the expression evaluates to true,
// i.e., the expression will always evaluate to false
internal bool IsUnsatisfiable()
{
InitializeConverter();
return m_converter.Vertex.IsZero();
}
// effects: Returns the internal tree in this
internal DomainBoolExpr Tree
{
get { return m_tree; }
}
internal IEnumerable> VariableConstraints
{
get { return LeafVisitor>.GetLeaves(m_tree); }
}
internal IEnumerable> Variables
{
get { return VariableConstraints.Select(domainConstraint => domainConstraint.Variable); }
}
internal IEnumerable MemberRestrictions
{
get
{
foreach (DomainVariable var in Variables)
{
MemberRestriction variableCondition = var.Identifier as MemberRestriction;
if (variableCondition != null)
{
yield return variableCondition;
}
}
}
}
#endregion
#region Methods
// effects: Given a sequence of boolean expressions, yields the
// corresponding trees in it in the same order
private IEnumerable ToBoolExprList(IEnumerable nodes)
{
foreach (BoolExpression node in nodes)
{
yield return node.m_tree;
}
}
///
/// Whether the boolean expression contains only OneOFTypeConst variables.
///
internal bool RepresentsAllTypeConditions
{
get
{
return this.MemberRestrictions.All(var => (var is TypeRestriction));
}
}
internal BoolExpression RemapLiterals(Dictionary remap)
{
var rewriter = new BooleanExpressionTermRewriter(
// term => remap[BoolExpression.GetBoolLiteral(term)].GetDomainBoolExpression(m_memberDomainMap));
delegate(DomainTermExpr term)
{
BoolLiteral newLiteral;
return remap.TryGetValue(BoolExpression.GetBoolLiteral(term), out newLiteral) ?
newLiteral.GetDomainBoolExpression(m_memberDomainMap) : term;
});
return new BoolExpression(m_tree.Accept(rewriter), m_memberDomainMap);
}
// effects: Given a boolean expression, modifies requiredSlots
// to indicate which slots are required to generate the expression
// projectedSlotMap indicates a mapping from member paths to slot
// numbers (that need to be checked off in requiredSlots)
internal virtual void GetRequiredSlots(MemberProjectionIndex projectedSlotMap, bool[] requiredSlots)
{
RequiredSlotsVisitor.GetRequiredSlots(m_tree, projectedSlotMap, requiredSlots);
}
///
/// Given the for the block in which the expression resides, converts the expression into eSQL.
///
internal StringBuilder AsEsql(StringBuilder builder, string blockAlias)
{
return AsEsqlVisitor.AsEsql(m_tree, builder, blockAlias);
}
///
/// Given the for the input, converts the expression into CQT.
///
internal DbExpression AsCqt(DbExpression row)
{
return AsCqtVisitor.AsCqt(m_tree, row);
}
internal StringBuilder AsUserString(StringBuilder builder, string blockAlias, bool writeRoundtrippingMessage)
{
if (writeRoundtrippingMessage)
{
builder.AppendLine(Strings.Viewgen_ConfigurationErrorMsg(blockAlias));
builder.Append(" ");
}
return AsUserStringVisitor.AsUserString(m_tree, builder, blockAlias);
}
internal override void ToCompactString(StringBuilder builder)
{
CompactStringVisitor.ToBuilder(m_tree, builder);
}
// effects: Given a mapping from old jointree nodes to new ones,
// creates a boolean expression from "this" in which the references
// to old join tree nodes are replaced by references to new nodes
// from remap (boolean expressions other than constants can contain
// references to jointree nodes, e.g., "var in values" -- var is a
// reference to a JoinTreeNode
internal BoolExpression RemapBool(Dictionary remap)
{
DomainBoolExpr expr = RemapBoolVisitor.RemapExtentTreeNodes(m_tree, m_memberDomainMap, remap);
return new BoolExpression(expr, m_memberDomainMap);
}
// effects: Given a list of bools, returns a list of boolean expressions where each
// boolean in bools has been ANDed with conjunct
// CHANGE_[....]_IMPROVE: replace with lambda pattern
internal static List AddConjunctionToBools(List bools,
BoolExpression conjunct)
{
List result = new List();
// Go through the list -- AND each non-null boolean with conjunct
foreach (BoolExpression b in bools)
{
if (null == b)
{ // unused boolean -- leave as it is
result.Add(null);
}
else
{
result.Add(CreateAnd(b, conjunct));
}
}
return result;
}
private void InitializeConverter()
{
if (null != m_converter)
{
// already done
return;
}
m_converter = new Converter(m_tree,
IdentifierService.Instance.CreateConversionContext());
}
internal BoolExpression MakeCopy()
{
BoolExpression copy = Create(m_tree.Accept(CopyVisitorInstance));
return copy;
}
static readonly CopyVisitor CopyVisitorInstance = new CopyVisitor();
private class CopyVisitor : BasicVisitor { }
internal void ExpensiveSimplify()
{
if (!IsFinal())
{
m_tree = m_tree.Simplify();
return;
}
InitializeConverter();
m_tree = m_tree.ExpensiveSimplify(out m_converter);
// this call is needed because the possible values on restriction and TrueFalseLiterals
// may change and need to be synchronized
FixDomainMap(m_memberDomainMap);
}
internal void FixDomainMap(MemberDomainMap domainMap)
{
Debug.Assert(domainMap != null, "Member domain map is not set");
m_tree = FixRangeVisitor.FixRange(m_tree, domainMap);
}
private bool IsFinal()
{
// First call simplify to get rid of tautologies and true, false
// etc. and then collapse the OneOfs
return (m_memberDomainMap != null && IsFinalVisitor.IsFinal(m_tree));
}
#endregion
#region Comparer class
// This class compares boolean expressions
private class BoolComparer : IEqualityComparer
{
#region IEqualityComparer Members
public bool Equals(BoolExpression left, BoolExpression right)
{
// Quick check with references
if (object.ReferenceEquals(left, right))
{
// Gets the Null and Undefined case as well
return true;
}
// One of them is non-null at least
if (left == null || right == null)
{
return false;
}
// Both are non-null at this point
return left.m_tree.Equals(right.m_tree);
}
public int GetHashCode(BoolExpression expression)
{
return expression.m_tree.GetHashCode();
}
#endregion
}
#endregion
}
}