//--------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @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; /// /// A class that represents NOT(elements), e.g., NOT(1, 2, NULL), i.e., all values other than null, 1 and 2 /// internal sealed class NegatedConstant : Constant { #region Constructors /// /// Creates a negated constant with the in it. /// /// must have no items internal NegatedConstant(IEnumerable values) { Debug.Assert(!values.Any(v => v is NegatedConstant), "Negated constant values must not contain another negated constant."); m_negatedDomain = new Set(values, Constant.EqualityComparer); } #endregion #region Fields /// /// e.g., NOT(1, 2, Undefined) /// private readonly Set m_negatedDomain; #endregion #region Properties internal IEnumerable Elements { get { return m_negatedDomain; } } #endregion #region Methods /// /// Returns true if the negated constant contains . /// 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; } /// /// Returns true if the negated constant contains . /// 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); } /// /// Not supported in this class. /// internal override StringBuilder AsEsql(StringBuilder builder, MemberPath outputMember, string blockAlias) { Debug.Fail("Should not be called."); return null; // To keep the compiler happy } /// /// Not supported in this class. /// 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 constants, MemberPath outputMember, bool skipIsNotNull) { return ToStringHelper(builder, blockAlias, constants, outputMember, skipIsNotNull, false); } internal DbExpression AsCqt(DbExpression row, IEnumerable 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 constants, MemberPath outputMember, bool skipIsNotNull) { return ToStringHelper(builder, blockAlias, constants, outputMember, skipIsNotNull, true); } /// /// Given a set of positive 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) /// private void AsCql(Action trueLiteral, Action varIsNotNull, Action varNotEqualsTo, IEnumerable 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 negatedConstants = new Set(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 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 } }