1 //---------------------------------------------------------------------
2 // <copyright file="NegatedConstant.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
10 namespace System.Data.Mapping.ViewGeneration.Structures
12 using System.Collections.Generic;
13 using System.Data.Common.CommandTrees;
14 using System.Data.Common.CommandTrees.ExpressionBuilder;
15 using System.Data.Common.Utils;
16 using System.Data.Entity;
17 using System.Diagnostics;
22 /// A class that represents NOT(elements), e.g., NOT(1, 2, NULL), i.e., all values other than null, 1 and 2
24 internal sealed class NegatedConstant : Constant
28 /// Creates a negated constant with the <paramref name="values"/> in it.
30 /// <param name="values">must have no <see cref=" NegatedConstant"/> items</param>
31 internal NegatedConstant(IEnumerable<Constant> values)
33 Debug.Assert(!values.Any(v => v is NegatedConstant), "Negated constant values must not contain another negated constant.");
34 m_negatedDomain = new Set<Constant>(values, Constant.EqualityComparer);
40 /// e.g., NOT(1, 2, Undefined)
42 private readonly Set<Constant> m_negatedDomain;
46 internal IEnumerable<Constant> Elements
48 get { return m_negatedDomain; }
54 /// Returns true if the negated constant contains <paramref name="constant"/>.
56 internal bool Contains(Constant constant)
58 return m_negatedDomain.Contains(constant);
61 internal override bool IsNull()
66 internal override bool IsNotNull()
68 if (object.ReferenceEquals(this, Constant.NotNull))
74 return m_negatedDomain.Count == 1 && m_negatedDomain.Contains(Constant.Null);
78 internal override bool IsUndefined()
84 /// Returns true if the negated constant contains <see cref="Constant.Null"/>.
86 internal override bool HasNotNull()
88 return m_negatedDomain.Contains(Constant.Null);
91 public override int GetHashCode()
94 foreach (Constant constant in m_negatedDomain)
96 result ^= Constant.EqualityComparer.GetHashCode(constant);
101 protected override bool IsEqualTo(Constant right)
103 NegatedConstant rightNegatedConstant = right as NegatedConstant;
104 if (rightNegatedConstant == null)
109 return m_negatedDomain.SetEquals(rightNegatedConstant.m_negatedDomain);
113 /// Not supported in this class.
115 internal override StringBuilder AsEsql(StringBuilder builder, MemberPath outputMember, string blockAlias)
117 Debug.Fail("Should not be called.");
118 return null; // To keep the compiler happy
122 /// Not supported in this class.
124 internal override DbExpression AsCqt(DbExpression row, MemberPath outputMember)
126 Debug.Fail("Should not be called.");
127 return null; // To keep the compiler happy
130 internal StringBuilder AsEsql(StringBuilder builder, string blockAlias, IEnumerable<Constant> constants, MemberPath outputMember, bool skipIsNotNull)
132 return ToStringHelper(builder, blockAlias, constants, outputMember, skipIsNotNull, false);
135 internal DbExpression AsCqt(DbExpression row, IEnumerable<Constant> constants, MemberPath outputMember, bool skipIsNotNull)
137 DbExpression cqt = null;
140 // trueLiteral action
141 () => cqt = DbExpressionBuilder.True,
142 // varIsNotNull action
143 () => cqt = outputMember.AsCqt(row).IsNull().Not(),
144 // varNotEqualsTo action
147 DbExpression notEqualsExpr = outputMember.AsCqt(row).NotEqual(constant.AsCqt(row, outputMember));
150 cqt = cqt.And(notEqualsExpr);
157 constants, outputMember, skipIsNotNull);
162 internal StringBuilder AsUserString(StringBuilder builder, string blockAlias, IEnumerable<Constant> constants, MemberPath outputMember, bool skipIsNotNull)
164 return ToStringHelper(builder, blockAlias, constants, outputMember, skipIsNotNull, true);
168 /// Given a set of positive <paramref name="constants"/> generates a simplified negated constant Cql expression.
170 /// - 7, NOT(7, NULL) means NOT(NULL)
171 /// - 7, 8, NOT(7, 8, 9, 10) means NOT(9, 10)
173 private void AsCql(Action trueLiteral, Action varIsNotNull, Action<Constant> varNotEqualsTo, IEnumerable<Constant> constants, MemberPath outputMember, bool skipIsNotNull)
175 bool isNullable = outputMember.IsNullable;
176 // Remove all the constants from negated and then print "x <> C1 .. AND x <> C2 .. AND x <> C3 ..."
177 Set<Constant> negatedConstants = new Set<Constant>(this.Elements, Constant.EqualityComparer);
178 foreach (Constant constant in constants)
180 if (constant.Equals(this)) { continue; }
181 Debug.Assert(negatedConstants.Contains(constant), "Negated constant must contain all positive constants");
182 negatedConstants.Remove(constant);
185 if (negatedConstants.Count == 0)
187 // All constants cancel out - emit True.
192 bool hasNull = negatedConstants.Contains(Constant.Null);
193 negatedConstants.Remove(Constant.Null);
195 // We always add IS NOT NULL if the property is nullable (and we cannot skip IS NOT NULL).
196 // Also, if the domain contains NOT NULL, we must add it.
198 if (hasNull || (isNullable && !skipIsNotNull))
203 foreach (Constant constant in negatedConstants)
205 varNotEqualsTo(constant);
210 private StringBuilder ToStringHelper(StringBuilder builder, string blockAlias, IEnumerable<Constant> constants, MemberPath outputMember, bool skipIsNotNull, bool userString)
212 bool anyAdded = false;
214 // trueLiteral action
215 () => builder.Append("true"),
216 // varIsNotNull action
221 outputMember.ToCompactString(builder, blockAlias);
222 builder.Append(" is not NULL");
226 outputMember.AsEsql(builder, blockAlias);
227 builder.Append(" IS NOT NULL");
231 // varNotEqualsTo action
236 builder.Append(" AND ");
242 outputMember.ToCompactString(builder, blockAlias);
243 builder.Append(" <>");
244 constant.ToCompactString(builder);
248 outputMember.AsEsql(builder, blockAlias);
249 builder.Append(" <>");
250 constant.AsEsql(builder, outputMember, blockAlias);
253 constants, outputMember, skipIsNotNull);
257 internal override string ToUserString()
261 return System.Data.Entity.Strings.ViewGen_NotNull;
265 StringBuilder builder = new StringBuilder();
267 foreach (Constant constant in m_negatedDomain)
269 // Skip printing out Null if m_negatedDomain has other values
270 if (m_negatedDomain.Count > 1 && constant.IsNull())
274 if (isFirst == false)
276 builder.Append(System.Data.Entity.Strings.ViewGen_CommaBlank);
279 builder.Append(constant.ToUserString());
281 StringBuilder result = new StringBuilder();
282 result.Append(Strings.ViewGen_NegatedCellConstant(builder.ToString()));
283 return result.ToString();
287 internal override void ToCompactString(StringBuilder builder)
291 builder.Append("NOT_NULL");
295 builder.Append("NOT(");
296 StringUtil.ToCommaSeparatedStringSorted(builder, m_negatedDomain);