//--------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- using System.Collections.Generic; using System.Data.Common.Utils; using System.Data.Entity; using System.Data.Metadata.Edm; using System.Diagnostics; using System.Linq; using System.Text; namespace System.Data.Mapping.ViewGeneration.Structures { using DomainBoolExpr = System.Data.Common.Utils.Boolean.BoolExpr>; using DomainTermExpr = System.Data.Common.Utils.Boolean.TermExpr>; /// /// An abstract class that denotes the boolean expression: "var in values". /// An object of this type can be complete or incomplete. /// An incomplete object is one whose domain was not created with all possible values. /// Incomplete objects have a limited set of methods that can be called. /// internal abstract class MemberRestriction : BoolLiteral { #region Constructors /// /// Creates an incomplete member restriction with the meaning " = ". /// "Partial" means that the in this restriction is partial - hence the operations on the restriction are limited. /// protected MemberRestriction(MemberProjectedSlot slot, Constant value) : this(slot, new Constant[] { value }) { } /// /// Creates an incomplete member restriction with the meaning " in ". /// protected MemberRestriction(MemberProjectedSlot slot, IEnumerable values) { m_restrictedMemberSlot = slot; m_domain = new Domain(values, values); } /// /// Creates a complete member restriction with the meaning " in ". /// protected MemberRestriction(MemberProjectedSlot slot, Domain domain) { m_restrictedMemberSlot = slot; m_domain = domain; m_isComplete = true; Debug.Assert(m_domain.Count != 0, "If you want a boolean that evaluates to false, " + "use the ConstantBool abstraction"); } /// /// Creates a complete member restriction with the meaning " in ". /// /// all the values that the can take protected MemberRestriction(MemberProjectedSlot slot, IEnumerable values, IEnumerable possibleValues) : this(slot, new Domain(values, possibleValues)) { Debug.Assert(possibleValues != null); } #endregion #region Fields private readonly MemberProjectedSlot m_restrictedMemberSlot; private readonly Domain m_domain; private readonly bool m_isComplete; #endregion #region Properties internal bool IsComplete { get { return m_isComplete; } } /// /// Returns the variable in the member restriction. /// internal MemberProjectedSlot RestrictedMemberSlot { get { return m_restrictedMemberSlot; } } /// /// Returns the values that is being checked for. /// internal Domain Domain { get { return m_domain; } } #endregion #region BoolLiteral Members /// /// Returns a boolean expression that is domain-aware and ready for optimizations etc. /// /// Maps members to the values that each member can take; /// it can be null in which case the possible and actual values are the same. internal override DomainBoolExpr GetDomainBoolExpression(MemberDomainMap domainMap) { // Get the variable name from the slot's memberpath and the possible domain values from the slot DomainTermExpr result; if (domainMap != null) { // Look up the domain from the domainMap IEnumerable domain = domainMap.GetDomain(m_restrictedMemberSlot.MemberPath); result = MakeTermExpression(this, domain, m_domain.Values); } else { result = MakeTermExpression(this, m_domain.AllPossibleValues, m_domain.Values); } return result; } /// /// Creates a complete member restriction based on the existing restriction with possible values for the domain being given by . /// internal abstract MemberRestriction CreateCompleteMemberRestriction(IEnumerable possibleValues); /// /// See . /// internal override void GetRequiredSlots(MemberProjectionIndex projectedSlotMap, bool[] requiredSlots) { // Simply get the slot for the variable var in "var in values" MemberPath member = RestrictedMemberSlot.MemberPath; int slotNum = projectedSlotMap.IndexOf(member); requiredSlots[slotNum] = true; } /// /// See . Member restriction can be incomplete for this operation. /// protected override bool IsEqualTo(BoolLiteral right) { MemberRestriction rightRestriction= right as MemberRestriction; if (rightRestriction == null) { return false; } if (object.ReferenceEquals(this, rightRestriction)) { return true; } if (false == MemberProjectedSlot.EqualityComparer.Equals(m_restrictedMemberSlot, rightRestriction.m_restrictedMemberSlot)) { return false; } return m_domain.IsEqualTo(rightRestriction.m_domain); } /// /// Member restriction can be incomplete for this operation. /// public override int GetHashCode() { int result = MemberProjectedSlot.EqualityComparer.GetHashCode(m_restrictedMemberSlot); result ^= m_domain.GetHash(); return result; } /// /// See . Member restriction can be incomplete for this operation. /// protected override bool IsIdentifierEqualTo(BoolLiteral right) { MemberRestriction rightOneOfConst = right as MemberRestriction; if (rightOneOfConst == null) { return false; } if (object.ReferenceEquals(this, rightOneOfConst)) { return true; } return MemberProjectedSlot.EqualityComparer.Equals(m_restrictedMemberSlot, rightOneOfConst.m_restrictedMemberSlot); } /// /// See . Member restriction can be incomplete for this operation. /// protected override int GetIdentifierHash() { int result = MemberProjectedSlot.EqualityComparer.GetHashCode(m_restrictedMemberSlot); return result; } #endregion #region Other Methods /// /// Converts this to a user-understandable string. /// /// indicates whether the text needs to say "x in .." or "x in NOT ..." (i.e., the latter if is true) [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal void ToUserString(bool invertOutput, StringBuilder builder, MetadataWorkspace workspace) { // If there is a negated cell constant, get the inversion of the domain NegatedConstant negatedConstant = null; foreach (Constant constant in Domain.Values) { negatedConstant = constant as NegatedConstant; if (negatedConstant != null) { break; } } Set constants; if (negatedConstant != null) { // Invert the domain and invert "invertOutput" invertOutput = !invertOutput; // Add all the values to negatedConstant's values to get the // final set of constants constants = new Set(negatedConstant.Elements, Constant.EqualityComparer); foreach (Constant constant in Domain.Values) { if (!(constant is NegatedConstant)) { Debug.Assert(constants.Contains(constant), "Domain of negated constant does not have positive constant"); constants.Remove(constant); } } } else { constants = new Set(Domain.Values, Constant.EqualityComparer); } // Determine the resource to use Debug.Assert(constants.Count > 0, "one of const is false?"); bool isNull = constants.Count == 1 && constants.Single().IsNull(); bool isTypeConstant = this is TypeRestriction; Func resourceName0 = null; Func resourceName1 = null; if (invertOutput) { if (isNull) { resourceName0 = isTypeConstant ? (Func)Strings.ViewGen_OneOfConst_IsNonNullable : (Func)Strings.ViewGen_OneOfConst_MustBeNonNullable; } else if (constants.Count == 1) { resourceName1 = isTypeConstant ? (Func)Strings.ViewGen_OneOfConst_IsNotEqualTo : (Func)Strings.ViewGen_OneOfConst_MustNotBeEqualTo; } else { resourceName1 = isTypeConstant ? (Func)Strings.ViewGen_OneOfConst_IsNotOneOf : (Func)Strings.ViewGen_OneOfConst_MustNotBeOneOf; } } else { if (isNull) { resourceName0 = isTypeConstant ? (Func)Strings.ViewGen_OneOfConst_MustBeNull : (Func)Strings.ViewGen_OneOfConst_MustBeNull; } else if (constants.Count == 1) { resourceName1 = isTypeConstant ? (Func)Strings.ViewGen_OneOfConst_IsEqualTo : (Func)Strings.ViewGen_OneOfConst_MustBeEqualTo; } else { resourceName1 = isTypeConstant ? (Func)Strings.ViewGen_OneOfConst_IsOneOf : (Func)Strings.ViewGen_OneOfConst_MustBeOneOf; } } // Get the constants StringBuilder constantBuilder = new StringBuilder(); Constant.ConstantsToUserString(constantBuilder, constants); Debug.Assert((resourceName0 == null) != (resourceName1 == null), "Both resources must not have been set or be null"); string variableName = m_restrictedMemberSlot.MemberPath.PathToString(false); if (isTypeConstant) { variableName = "TypeOf(" + variableName + ")"; } if (resourceName0 != null) { builder.Append(resourceName0(variableName)); } else { builder.Append(resourceName1(variableName, constantBuilder.ToString())); } if (invertOutput && isTypeConstant) { InvertOutputStringForTypeConstant(builder, constants, workspace); } } /// /// Modifies builder to contain a message for inverting the typeConstants, i.e., NOT(p in Person) becomes p in Customer. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] private void InvertOutputStringForTypeConstant(StringBuilder builder, Set constants, MetadataWorkspace workspace) { // Get all the types that this type can take (i.e., all // subtypes - the types present in this StringBuilder typeBuilder = new StringBuilder(); Set allTypes = new Set(); // Get all types EdmType memberType = RestrictedMemberSlot.MemberPath.EdmType; foreach (EdmType type in MetadataHelper.GetTypeAndSubtypesOf(memberType, workspace, false)) { allTypes.Add(type); } // Get the types in this Set oneOfTypes = new Set(); foreach (Constant constant in constants) { TypeConstant typeConstant = (TypeConstant)constant; oneOfTypes.Add(typeConstant.EdmType); } // Get the difference allTypes.Subtract(oneOfTypes); bool isFirst = true; foreach (EdmType type in allTypes) { if (isFirst == false) { typeBuilder.Append(System.Data.Entity.Strings.ViewGen_CommaBlank); } isFirst = false; typeBuilder.Append(type.Name); } builder.Append(Strings.ViewGen_OneOfConst_IsOneOfTypes(typeBuilder.ToString())); } internal override StringBuilder AsUserString(StringBuilder builder, string blockAlias, bool skipIsNotNull) { return AsEsql(builder, blockAlias, skipIsNotNull); } internal override StringBuilder AsNegatedUserString(StringBuilder builder, string blockAlias, bool skipIsNotNull) { builder.Append("NOT("); builder = AsUserString(builder, blockAlias, skipIsNotNull); builder.Append(")"); return builder; } #endregion } }