2015-04-07 09:35:12 +01:00
|
|
|
//---------------------------------------------------------------------
|
|
|
|
// <copyright file="BoolExpression.cs" company="Microsoft">
|
|
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
|
|
// </copyright>
|
|
|
|
//
|
|
|
|
// @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<BoolLiteral, Constant>;
|
|
|
|
using DomainAndExpr = AndExpr<DomainConstraint<BoolLiteral, Constant>>;
|
|
|
|
using DomainBoolExpr = BoolExpr<DomainConstraint<BoolLiteral, Constant>>;
|
|
|
|
using DomainFalseExpr = FalseExpr<DomainConstraint<BoolLiteral, Constant>>;
|
|
|
|
using DomainNotExpr = NotExpr<DomainConstraint<BoolLiteral, Constant>>;
|
|
|
|
using DomainOrExpr = OrExpr<DomainConstraint<BoolLiteral, Constant>>;
|
|
|
|
using DomainTermExpr = TermExpr<DomainConstraint<BoolLiteral, Constant>>;
|
|
|
|
using DomainTrueExpr = TrueExpr<DomainConstraint<BoolLiteral, Constant>>;
|
|
|
|
|
|
|
|
|
|
|
|
// 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<BoolExpression> children)
|
|
|
|
{
|
|
|
|
List<BoolExpression> childList = new List<BoolExpression>(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<BoolDomainConstraint> m_converter;
|
|
|
|
|
|
|
|
internal static readonly IEqualityComparer<BoolExpression> 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<BoolExpression> 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<DomainTermExpr> 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<BoolLiteral> Leaves
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
// Create the term visitor and visit it to get terms
|
|
|
|
IEnumerable<DomainTermExpr> 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<BoolLiteral, Constant> domainConstraint = term.Identifier;
|
|
|
|
DomainVariable<BoolLiteral, Constant> 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<DomainConstraint<BoolLiteral, Constant>> VariableConstraints
|
|
|
|
{
|
|
|
|
get { return LeafVisitor<DomainConstraint<BoolLiteral, Constant>>.GetLeaves(m_tree); }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal IEnumerable<DomainVariable<BoolLiteral, Constant>> Variables
|
|
|
|
{
|
|
|
|
get { return VariableConstraints.Select(domainConstraint => domainConstraint.Variable); }
|
|
|
|
}
|
|
|
|
|
|
|
|
internal IEnumerable<MemberRestriction> MemberRestrictions
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
foreach (DomainVariable<BoolLiteral, Constant> 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<DomainBoolExpr> ToBoolExprList(IEnumerable<BoolExpression> nodes)
|
|
|
|
{
|
|
|
|
foreach (BoolExpression node in nodes)
|
|
|
|
{
|
|
|
|
yield return node.m_tree;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Whether the boolean expression contains only OneOFTypeConst variables.
|
|
|
|
/// </summary>
|
|
|
|
internal bool RepresentsAllTypeConditions
|
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
return this.MemberRestrictions.All(var => (var is TypeRestriction));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
internal BoolExpression RemapLiterals(Dictionary<BoolLiteral, BoolLiteral> remap)
|
|
|
|
{
|
|
|
|
var rewriter = new BooleanExpressionTermRewriter<BoolDomainConstraint, BoolDomainConstraint>(
|
|
|
|
// 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);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Given the <paramref name="blockAlias"/> for the block in which the expression resides, converts the expression into eSQL.
|
|
|
|
/// </summary>
|
|
|
|
internal StringBuilder AsEsql(StringBuilder builder, string blockAlias)
|
|
|
|
{
|
|
|
|
return AsEsqlVisitor.AsEsql(m_tree, builder, blockAlias);
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Given the <paramref name="row"/> for the input, converts the expression into CQT.
|
|
|
|
/// </summary>
|
|
|
|
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<MemberPath, MemberPath> 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<BoolExpression> AddConjunctionToBools(List<BoolExpression> bools,
|
|
|
|
BoolExpression conjunct)
|
|
|
|
{
|
|
|
|
List<BoolExpression> result = new List<BoolExpression>();
|
|
|
|
// 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<BoolDomainConstraint>(m_tree,
|
|
|
|
IdentifierService<BoolDomainConstraint>.Instance.CreateConversionContext());
|
|
|
|
}
|
|
|
|
|
|
|
|
internal BoolExpression MakeCopy()
|
|
|
|
{
|
|
|
|
BoolExpression copy = Create(m_tree.Accept(CopyVisitorInstance));
|
|
|
|
return copy;
|
|
|
|
}
|
|
|
|
static readonly CopyVisitor CopyVisitorInstance = new CopyVisitor();
|
|
|
|
private class CopyVisitor : BasicVisitor<BoolDomainConstraint> { }
|
|
|
|
|
|
|
|
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<BoolExpression>
|
|
|
|
{
|
|
|
|
|
|
|
|
#region IEqualityComparer<BoolExpression> 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
|
|
|
|
}
|
|
|
|
}
|