1 //--------------------------------------------------------------------- 2 // <copyright file="MemberRestriction.cs" company="Microsoft"> 3 // Copyright (c) Microsoft Corporation. All rights reserved. 4 // </copyright> 5 // 6 // @owner Microsoft 7 // @backupOwner Microsoft 8 //--------------------------------------------------------------------- 9 10 using System.Collections.Generic; 11 using System.Data.Common.Utils; 12 using System.Data.Entity; 13 using System.Data.Metadata.Edm; 14 using System.Diagnostics; 15 using System.Linq; 16 using System.Text; 17 18 namespace System.Data.Mapping.ViewGeneration.Structures 19 { 20 using DomainBoolExpr = System.Data.Common.Utils.Boolean.BoolExpr<System.Data.Common.Utils.Boolean.DomainConstraint<BoolLiteral, Constant>>; 21 using DomainTermExpr = System.Data.Common.Utils.Boolean.TermExpr<System.Data.Common.Utils.Boolean.DomainConstraint<BoolLiteral, Constant>>; 22 23 /// <summary> 24 /// An abstract class that denotes the boolean expression: "var in values". 25 /// An object of this type can be complete or incomplete. 26 /// An incomplete object is one whose domain was not created with all possible values. 27 /// Incomplete objects have a limited set of methods that can be called. 28 /// </summary> 29 internal abstract class MemberRestriction : BoolLiteral 30 { 31 #region Constructors 32 /// <summary> 33 /// Creates an incomplete member restriction with the meaning "<paramref name="slot"/> = <paramref name="value"/>". 34 /// "Partial" means that the <see cref="Domain"/> in this restriction is partial - hence the operations on the restriction are limited. 35 /// </summary> MemberRestriction(MemberProjectedSlot slot, Constant value)36 protected MemberRestriction(MemberProjectedSlot slot, Constant value) 37 : this(slot, new Constant[] { value }) 38 { } 39 40 /// <summary> 41 /// Creates an incomplete member restriction with the meaning "<paramref name="slot"/> in <paramref name="values"/>". 42 /// </summary> MemberRestriction(MemberProjectedSlot slot, IEnumerable<Constant> values)43 protected MemberRestriction(MemberProjectedSlot slot, IEnumerable<Constant> values) 44 { 45 m_restrictedMemberSlot = slot; 46 m_domain = new Domain(values, values); 47 } 48 49 /// <summary> 50 /// Creates a complete member restriction with the meaning "<paramref name="slot"/> in <paramref name="domain"/>". 51 /// </summary> MemberRestriction(MemberProjectedSlot slot, Domain domain)52 protected MemberRestriction(MemberProjectedSlot slot, Domain domain) 53 { 54 m_restrictedMemberSlot = slot; 55 m_domain = domain; 56 m_isComplete = true; 57 Debug.Assert(m_domain.Count != 0, "If you want a boolean that evaluates to false, " + 58 "use the ConstantBool abstraction"); 59 } 60 61 /// <summary> 62 /// Creates a complete member restriction with the meaning "<paramref name="slot"/> in <paramref name="values"/>". 63 /// </summary> 64 /// <param name="possibleValues">all the values that the <paramref name="slot"/> can take</param> MemberRestriction(MemberProjectedSlot slot, IEnumerable<Constant> values, IEnumerable<Constant> possibleValues)65 protected MemberRestriction(MemberProjectedSlot slot, IEnumerable<Constant> values, IEnumerable<Constant> possibleValues) 66 : this(slot, new Domain(values, possibleValues)) 67 { 68 Debug.Assert(possibleValues != null); 69 } 70 #endregion 71 72 #region Fields 73 private readonly MemberProjectedSlot m_restrictedMemberSlot; 74 private readonly Domain m_domain; 75 private readonly bool m_isComplete; 76 #endregion 77 78 #region Properties 79 internal bool IsComplete 80 { 81 get { return m_isComplete; } 82 } 83 84 /// <summary> 85 /// Returns the variable in the member restriction. 86 /// </summary> 87 internal MemberProjectedSlot RestrictedMemberSlot 88 { 89 get { return m_restrictedMemberSlot; } 90 } 91 92 /// <summary> 93 /// Returns the values that <see cref="RestrictedMemberSlot"/> is being checked for. 94 /// </summary> 95 internal Domain Domain 96 { 97 get { return m_domain; } 98 } 99 #endregion 100 101 #region BoolLiteral Members 102 /// <summary> 103 /// Returns a boolean expression that is domain-aware and ready for optimizations etc. 104 /// </summary> 105 /// <param name="domainMap">Maps members to the values that each member can take; 106 /// it can be null in which case the possible and actual values are the same.</param> GetDomainBoolExpression(MemberDomainMap domainMap)107 internal override DomainBoolExpr GetDomainBoolExpression(MemberDomainMap domainMap) 108 { 109 // Get the variable name from the slot's memberpath and the possible domain values from the slot 110 DomainTermExpr result; 111 if (domainMap != null) 112 { 113 // Look up the domain from the domainMap 114 IEnumerable<Constant> domain = domainMap.GetDomain(m_restrictedMemberSlot.MemberPath); 115 result = MakeTermExpression(this, domain, m_domain.Values); 116 } 117 else 118 { 119 result = MakeTermExpression(this, m_domain.AllPossibleValues, m_domain.Values); 120 } 121 return result; 122 } 123 124 /// <summary> 125 /// Creates a complete member restriction based on the existing restriction with possible values for the domain being given by <paramref name="possibleValues"/>. 126 /// </summary> CreateCompleteMemberRestriction(IEnumerable<Constant> possibleValues)127 internal abstract MemberRestriction CreateCompleteMemberRestriction(IEnumerable<Constant> possibleValues); 128 129 /// <summary> 130 /// See <see cref="BoolLiteral.GetRequiredSlots"/>. 131 /// </summary> GetRequiredSlots(MemberProjectionIndex projectedSlotMap, bool[] requiredSlots)132 internal override void GetRequiredSlots(MemberProjectionIndex projectedSlotMap, bool[] requiredSlots) 133 { 134 // Simply get the slot for the variable var in "var in values" 135 MemberPath member = RestrictedMemberSlot.MemberPath; 136 int slotNum = projectedSlotMap.IndexOf(member); 137 requiredSlots[slotNum] = true; 138 } 139 140 /// <summary> 141 /// See <see cref="BoolLiteral.IsEqualTo"/>. Member restriction can be incomplete for this operation. 142 /// </summary> IsEqualTo(BoolLiteral right)143 protected override bool IsEqualTo(BoolLiteral right) 144 { 145 MemberRestriction rightRestriction= right as MemberRestriction; 146 if (rightRestriction == null) 147 { 148 return false; 149 } 150 if (object.ReferenceEquals(this, rightRestriction)) 151 { 152 return true; 153 } 154 if (false == MemberProjectedSlot.EqualityComparer.Equals(m_restrictedMemberSlot, rightRestriction.m_restrictedMemberSlot)) 155 { 156 return false; 157 } 158 159 return m_domain.IsEqualTo(rightRestriction.m_domain); 160 } 161 162 /// <summary> 163 /// Member restriction can be incomplete for this operation. 164 /// </summary> GetHashCode()165 public override int GetHashCode() 166 { 167 int result = MemberProjectedSlot.EqualityComparer.GetHashCode(m_restrictedMemberSlot); 168 result ^= m_domain.GetHash(); 169 return result; 170 } 171 172 /// <summary> 173 /// See <see cref="BoolLiteral.IsIdentifierEqualTo"/>. Member restriction can be incomplete for this operation. 174 /// </summary> IsIdentifierEqualTo(BoolLiteral right)175 protected override bool IsIdentifierEqualTo(BoolLiteral right) 176 { 177 MemberRestriction rightOneOfConst = right as MemberRestriction; 178 if (rightOneOfConst == null) 179 { 180 return false; 181 } 182 if (object.ReferenceEquals(this, rightOneOfConst)) 183 { 184 return true; 185 } 186 return MemberProjectedSlot.EqualityComparer.Equals(m_restrictedMemberSlot, rightOneOfConst.m_restrictedMemberSlot); 187 } 188 189 /// <summary> 190 /// See <see cref="BoolLiteral.GetIdentifierHash"/>. Member restriction can be incomplete for this operation. 191 /// </summary> GetIdentifierHash()192 protected override int GetIdentifierHash() 193 { 194 int result = MemberProjectedSlot.EqualityComparer.GetHashCode(m_restrictedMemberSlot); 195 return result; 196 } 197 #endregion 198 199 #region Other Methods 200 /// <summary> 201 /// Converts this to a user-understandable string. 202 /// </summary> 203 /// <param name="invertOutput">indicates whether the text needs to say "x in .." or "x in NOT ..." (i.e., the latter if <paramref name="invertOutput"/> is true)</param> 204 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] ToUserString(bool invertOutput, StringBuilder builder, MetadataWorkspace workspace)205 internal void ToUserString(bool invertOutput, StringBuilder builder, MetadataWorkspace workspace) 206 { 207 // If there is a negated cell constant, get the inversion of the domain 208 NegatedConstant negatedConstant = null; 209 foreach (Constant constant in Domain.Values) 210 { 211 negatedConstant = constant as NegatedConstant; 212 if (negatedConstant != null) 213 { 214 break; 215 } 216 } 217 218 Set<Constant> constants; 219 if (negatedConstant != null) 220 { 221 // Invert the domain and invert "invertOutput" 222 invertOutput = !invertOutput; 223 // Add all the values to negatedConstant's values to get the 224 // final set of constants 225 constants = new Set<Constant>(negatedConstant.Elements, Constant.EqualityComparer); 226 foreach (Constant constant in Domain.Values) 227 { 228 if (!(constant is NegatedConstant)) 229 { 230 Debug.Assert(constants.Contains(constant), "Domain of negated constant does not have positive constant"); 231 constants.Remove(constant); 232 } 233 } 234 } 235 else 236 { 237 constants = new Set<Constant>(Domain.Values, Constant.EqualityComparer); 238 } 239 240 // Determine the resource to use 241 Debug.Assert(constants.Count > 0, "one of const is false?"); 242 bool isNull = constants.Count == 1 && constants.Single().IsNull(); 243 bool isTypeConstant = this is TypeRestriction; 244 245 Func<object, string> resourceName0 = null; 246 Func<object, object, string> resourceName1 = null; 247 248 if (invertOutput) 249 { 250 if (isNull) 251 { 252 resourceName0 = isTypeConstant ? (Func<object, string>)Strings.ViewGen_OneOfConst_IsNonNullable : (Func<object, string>)Strings.ViewGen_OneOfConst_MustBeNonNullable; 253 } 254 else if (constants.Count == 1) 255 { 256 resourceName1 = isTypeConstant ? (Func<object, object, string>)Strings.ViewGen_OneOfConst_IsNotEqualTo : (Func<object, object, string>)Strings.ViewGen_OneOfConst_MustNotBeEqualTo; 257 } 258 else 259 { 260 resourceName1 = isTypeConstant ? (Func<object, object, string>)Strings.ViewGen_OneOfConst_IsNotOneOf : (Func<object, object, string>)Strings.ViewGen_OneOfConst_MustNotBeOneOf; 261 } 262 } 263 else 264 { 265 if (isNull) 266 { 267 resourceName0 = isTypeConstant ? (Func<object, string>)Strings.ViewGen_OneOfConst_MustBeNull : (Func<object, string>)Strings.ViewGen_OneOfConst_MustBeNull; 268 } 269 else if (constants.Count == 1) 270 { 271 resourceName1 = isTypeConstant ? (Func<object, object, string>)Strings.ViewGen_OneOfConst_IsEqualTo : (Func<object, object, string>)Strings.ViewGen_OneOfConst_MustBeEqualTo; 272 } 273 else 274 { 275 resourceName1 = isTypeConstant ? (Func<object, object, string>)Strings.ViewGen_OneOfConst_IsOneOf : (Func<object, object, string>)Strings.ViewGen_OneOfConst_MustBeOneOf; 276 } 277 } 278 279 // Get the constants 280 StringBuilder constantBuilder = new StringBuilder(); 281 Constant.ConstantsToUserString(constantBuilder, constants); 282 283 Debug.Assert((resourceName0 == null) != (resourceName1 == null), 284 "Both resources must not have been set or be null"); 285 string variableName = m_restrictedMemberSlot.MemberPath.PathToString(false); 286 if (isTypeConstant) 287 { 288 variableName = "TypeOf(" + variableName + ")"; 289 } 290 291 if (resourceName0 != null) 292 { 293 builder.Append(resourceName0(variableName)); 294 } 295 else 296 { 297 builder.Append(resourceName1(variableName, constantBuilder.ToString())); 298 } 299 300 if (invertOutput && isTypeConstant) 301 { 302 InvertOutputStringForTypeConstant(builder, constants, workspace); 303 } 304 } 305 306 /// <summary> 307 /// Modifies builder to contain a message for inverting the typeConstants, i.e., NOT(p in Person) becomes p in Customer. 308 /// </summary> 309 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] InvertOutputStringForTypeConstant(StringBuilder builder, Set<Constant> constants, MetadataWorkspace workspace)310 private void InvertOutputStringForTypeConstant(StringBuilder builder, Set<Constant> constants, MetadataWorkspace workspace) 311 { 312 // Get all the types that this type can take (i.e., all 313 // subtypes - the types present in this 314 StringBuilder typeBuilder = new StringBuilder(); 315 Set<EdmType> allTypes = new Set<EdmType>(); 316 317 // Get all types 318 EdmType memberType = RestrictedMemberSlot.MemberPath.EdmType; 319 foreach (EdmType type in MetadataHelper.GetTypeAndSubtypesOf(memberType, workspace, false)) 320 { 321 allTypes.Add(type); 322 } 323 324 // Get the types in this 325 Set<EdmType> oneOfTypes = new Set<EdmType>(); 326 foreach (Constant constant in constants) 327 { 328 TypeConstant typeConstant = (TypeConstant)constant; 329 oneOfTypes.Add(typeConstant.EdmType); 330 } 331 332 // Get the difference 333 allTypes.Subtract(oneOfTypes); 334 bool isFirst = true; 335 foreach (EdmType type in allTypes) 336 { 337 if (isFirst == false) 338 { 339 typeBuilder.Append(System.Data.Entity.Strings.ViewGen_CommaBlank); 340 } 341 isFirst = false; 342 typeBuilder.Append(type.Name); 343 } 344 builder.Append(Strings.ViewGen_OneOfConst_IsOneOfTypes(typeBuilder.ToString())); 345 } 346 AsUserString(StringBuilder builder, string blockAlias, bool skipIsNotNull)347 internal override StringBuilder AsUserString(StringBuilder builder, string blockAlias, bool skipIsNotNull) 348 { 349 return AsEsql(builder, blockAlias, skipIsNotNull); 350 } 351 AsNegatedUserString(StringBuilder builder, string blockAlias, bool skipIsNotNull)352 internal override StringBuilder AsNegatedUserString(StringBuilder builder, string blockAlias, bool skipIsNotNull) 353 { 354 builder.Append("NOT("); 355 builder = AsUserString(builder, blockAlias, skipIsNotNull); 356 builder.Append(")"); 357 return builder; 358 } 359 #endregion 360 } 361 } 362