303 lines
11 KiB
C#
303 lines
11 KiB
C#
//---------------------------------------------------------------------
|
|
// <copyright file="NegatedConstant.cs" company="Microsoft">
|
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
|
// </copyright>
|
|
//
|
|
// @owner [....]
|
|
// @backupOwner [....]
|
|
//---------------------------------------------------------------------
|
|
|
|
namespace System.Data.Mapping.ViewGeneration.Structures
|
|
{
|
|
using System.Collections.Generic;
|
|
using System.Data.Common.CommandTrees;
|
|
using System.Data.Common.CommandTrees.ExpressionBuilder;
|
|
using System.Data.Common.Utils;
|
|
using System.Data.Entity;
|
|
using System.Diagnostics;
|
|
using System.Linq;
|
|
using System.Text;
|
|
|
|
/// <summary>
|
|
/// A class that represents NOT(elements), e.g., NOT(1, 2, NULL), i.e., all values other than null, 1 and 2
|
|
/// </summary>
|
|
internal sealed class NegatedConstant : Constant
|
|
{
|
|
#region Constructors
|
|
/// <summary>
|
|
/// Creates a negated constant with the <paramref name="values"/> in it.
|
|
/// </summary>
|
|
/// <param name="values">must have no <see cref=" NegatedConstant"/> items</param>
|
|
internal NegatedConstant(IEnumerable<Constant> values)
|
|
{
|
|
Debug.Assert(!values.Any(v => v is NegatedConstant), "Negated constant values must not contain another negated constant.");
|
|
m_negatedDomain = new Set<Constant>(values, Constant.EqualityComparer);
|
|
}
|
|
#endregion
|
|
|
|
#region Fields
|
|
/// <summary>
|
|
/// e.g., NOT(1, 2, Undefined)
|
|
/// </summary>
|
|
private readonly Set<Constant> m_negatedDomain;
|
|
#endregion
|
|
|
|
#region Properties
|
|
internal IEnumerable<Constant> Elements
|
|
{
|
|
get { return m_negatedDomain; }
|
|
}
|
|
#endregion
|
|
|
|
#region Methods
|
|
/// <summary>
|
|
/// Returns true if the negated constant contains <paramref name="constant"/>.
|
|
/// </summary>
|
|
internal bool Contains(Constant constant)
|
|
{
|
|
return m_negatedDomain.Contains(constant);
|
|
}
|
|
|
|
internal override bool IsNull()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
internal override bool IsNotNull()
|
|
{
|
|
if (object.ReferenceEquals(this, Constant.NotNull))
|
|
{
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return m_negatedDomain.Count == 1 && m_negatedDomain.Contains(Constant.Null);
|
|
}
|
|
}
|
|
|
|
internal override bool IsUndefined()
|
|
{
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Returns true if the negated constant contains <see cref="Constant.Null"/>.
|
|
/// </summary>
|
|
internal override bool HasNotNull()
|
|
{
|
|
return m_negatedDomain.Contains(Constant.Null);
|
|
}
|
|
|
|
public override int GetHashCode()
|
|
{
|
|
int result = 0;
|
|
foreach (Constant constant in m_negatedDomain)
|
|
{
|
|
result ^= Constant.EqualityComparer.GetHashCode(constant);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
protected override bool IsEqualTo(Constant right)
|
|
{
|
|
NegatedConstant rightNegatedConstant = right as NegatedConstant;
|
|
if (rightNegatedConstant == null)
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return m_negatedDomain.SetEquals(rightNegatedConstant.m_negatedDomain);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Not supported in this class.
|
|
/// </summary>
|
|
internal override StringBuilder AsEsql(StringBuilder builder, MemberPath outputMember, string blockAlias)
|
|
{
|
|
Debug.Fail("Should not be called.");
|
|
return null; // To keep the compiler happy
|
|
}
|
|
|
|
/// <summary>
|
|
/// Not supported in this class.
|
|
/// </summary>
|
|
internal override DbExpression AsCqt(DbExpression row, MemberPath outputMember)
|
|
{
|
|
Debug.Fail("Should not be called.");
|
|
return null; // To keep the compiler happy
|
|
}
|
|
|
|
internal StringBuilder AsEsql(StringBuilder builder, string blockAlias, IEnumerable<Constant> constants, MemberPath outputMember, bool skipIsNotNull)
|
|
{
|
|
return ToStringHelper(builder, blockAlias, constants, outputMember, skipIsNotNull, false);
|
|
}
|
|
|
|
internal DbExpression AsCqt(DbExpression row, IEnumerable<Constant> constants, MemberPath outputMember, bool skipIsNotNull)
|
|
{
|
|
DbExpression cqt = null;
|
|
|
|
AsCql(
|
|
// trueLiteral action
|
|
() => cqt = DbExpressionBuilder.True,
|
|
// varIsNotNull action
|
|
() => cqt = outputMember.AsCqt(row).IsNull().Not(),
|
|
// varNotEqualsTo action
|
|
(constant) =>
|
|
{
|
|
DbExpression notEqualsExpr = outputMember.AsCqt(row).NotEqual(constant.AsCqt(row, outputMember));
|
|
if (cqt != null)
|
|
{
|
|
cqt = cqt.And(notEqualsExpr);
|
|
}
|
|
else
|
|
{
|
|
cqt = notEqualsExpr;
|
|
}
|
|
},
|
|
constants, outputMember, skipIsNotNull);
|
|
|
|
return cqt;
|
|
}
|
|
|
|
internal StringBuilder AsUserString(StringBuilder builder, string blockAlias, IEnumerable<Constant> constants, MemberPath outputMember, bool skipIsNotNull)
|
|
{
|
|
return ToStringHelper(builder, blockAlias, constants, outputMember, skipIsNotNull, true);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Given a set of positive <paramref name="constants"/> generates a simplified negated constant Cql expression.
|
|
/// Examples:
|
|
/// - 7, NOT(7, NULL) means NOT(NULL)
|
|
/// - 7, 8, NOT(7, 8, 9, 10) means NOT(9, 10)
|
|
/// </summary>
|
|
private void AsCql(Action trueLiteral, Action varIsNotNull, Action<Constant> varNotEqualsTo, IEnumerable<Constant> constants, MemberPath outputMember, bool skipIsNotNull)
|
|
{
|
|
bool isNullable = outputMember.IsNullable;
|
|
// Remove all the constants from negated and then print "x <> C1 .. AND x <> C2 .. AND x <> C3 ..."
|
|
Set<Constant> negatedConstants = new Set<Constant>(this.Elements, Constant.EqualityComparer);
|
|
foreach (Constant constant in constants)
|
|
{
|
|
if (constant.Equals(this)) { continue; }
|
|
Debug.Assert(negatedConstants.Contains(constant), "Negated constant must contain all positive constants");
|
|
negatedConstants.Remove(constant);
|
|
}
|
|
|
|
if (negatedConstants.Count == 0)
|
|
{
|
|
// All constants cancel out - emit True.
|
|
trueLiteral();
|
|
}
|
|
else
|
|
{
|
|
bool hasNull = negatedConstants.Contains(Constant.Null);
|
|
negatedConstants.Remove(Constant.Null);
|
|
|
|
// We always add IS NOT NULL if the property is nullable (and we cannot skip IS NOT NULL).
|
|
// Also, if the domain contains NOT NULL, we must add it.
|
|
|
|
if (hasNull || (isNullable && !skipIsNotNull))
|
|
{
|
|
varIsNotNull();
|
|
}
|
|
|
|
foreach (Constant constant in negatedConstants)
|
|
{
|
|
varNotEqualsTo(constant);
|
|
}
|
|
}
|
|
}
|
|
|
|
private StringBuilder ToStringHelper(StringBuilder builder, string blockAlias, IEnumerable<Constant> constants, MemberPath outputMember, bool skipIsNotNull, bool userString)
|
|
{
|
|
bool anyAdded = false;
|
|
AsCql(
|
|
// trueLiteral action
|
|
() => builder.Append("true"),
|
|
// varIsNotNull action
|
|
() =>
|
|
{
|
|
if (userString)
|
|
{
|
|
outputMember.ToCompactString(builder, blockAlias);
|
|
builder.Append(" is not NULL");
|
|
}
|
|
else
|
|
{
|
|
outputMember.AsEsql(builder, blockAlias);
|
|
builder.Append(" IS NOT NULL");
|
|
}
|
|
anyAdded = true;
|
|
},
|
|
// varNotEqualsTo action
|
|
(constant) =>
|
|
{
|
|
if (anyAdded)
|
|
{
|
|
builder.Append(" AND ");
|
|
}
|
|
anyAdded = true;
|
|
|
|
if (userString)
|
|
{
|
|
outputMember.ToCompactString(builder, blockAlias);
|
|
builder.Append(" <>");
|
|
constant.ToCompactString(builder);
|
|
}
|
|
else
|
|
{
|
|
outputMember.AsEsql(builder, blockAlias);
|
|
builder.Append(" <>");
|
|
constant.AsEsql(builder, outputMember, blockAlias);
|
|
}
|
|
},
|
|
constants, outputMember, skipIsNotNull);
|
|
return builder;
|
|
}
|
|
|
|
internal override string ToUserString()
|
|
{
|
|
if (IsNotNull())
|
|
{
|
|
return System.Data.Entity.Strings.ViewGen_NotNull;
|
|
}
|
|
else
|
|
{
|
|
StringBuilder builder = new StringBuilder();
|
|
bool isFirst = true;
|
|
foreach (Constant constant in m_negatedDomain)
|
|
{
|
|
// Skip printing out Null if m_negatedDomain has other values
|
|
if (m_negatedDomain.Count > 1 && constant.IsNull())
|
|
{
|
|
continue;
|
|
}
|
|
if (isFirst == false)
|
|
{
|
|
builder.Append(System.Data.Entity.Strings.ViewGen_CommaBlank);
|
|
}
|
|
isFirst = false;
|
|
builder.Append(constant.ToUserString());
|
|
}
|
|
StringBuilder result = new StringBuilder();
|
|
result.Append(Strings.ViewGen_NegatedCellConstant(builder.ToString()));
|
|
return result.ToString();
|
|
}
|
|
}
|
|
|
|
internal override void ToCompactString(StringBuilder builder)
|
|
{
|
|
if (IsNotNull())
|
|
{
|
|
builder.Append("NOT_NULL");
|
|
}
|
|
else
|
|
{
|
|
builder.Append("NOT(");
|
|
StringUtil.ToCommaSeparatedStringSorted(builder, m_negatedDomain);
|
|
builder.Append(")");
|
|
}
|
|
}
|
|
#endregion
|
|
}
|
|
}
|