362 lines
15 KiB
C#
362 lines
15 KiB
C#
|
//---------------------------------------------------------------------
|
||
|
// <copyright file="MemberRestriction.cs" company="Microsoft">
|
||
|
// Copyright (c) Microsoft Corporation. All rights reserved.
|
||
|
// </copyright>
|
||
|
//
|
||
|
// @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<System.Data.Common.Utils.Boolean.DomainConstraint<BoolLiteral, Constant>>;
|
||
|
using DomainTermExpr = System.Data.Common.Utils.Boolean.TermExpr<System.Data.Common.Utils.Boolean.DomainConstraint<BoolLiteral, Constant>>;
|
||
|
|
||
|
/// <summary>
|
||
|
/// 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.
|
||
|
/// </summary>
|
||
|
internal abstract class MemberRestriction : BoolLiteral
|
||
|
{
|
||
|
#region Constructors
|
||
|
/// <summary>
|
||
|
/// Creates an incomplete member restriction with the meaning "<paramref name="slot"/> = <paramref name="value"/>".
|
||
|
/// "Partial" means that the <see cref="Domain"/> in this restriction is partial - hence the operations on the restriction are limited.
|
||
|
/// </summary>
|
||
|
protected MemberRestriction(MemberProjectedSlot slot, Constant value)
|
||
|
: this(slot, new Constant[] { value })
|
||
|
{ }
|
||
|
|
||
|
/// <summary>
|
||
|
/// Creates an incomplete member restriction with the meaning "<paramref name="slot"/> in <paramref name="values"/>".
|
||
|
/// </summary>
|
||
|
protected MemberRestriction(MemberProjectedSlot slot, IEnumerable<Constant> values)
|
||
|
{
|
||
|
m_restrictedMemberSlot = slot;
|
||
|
m_domain = new Domain(values, values);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Creates a complete member restriction with the meaning "<paramref name="slot"/> in <paramref name="domain"/>".
|
||
|
/// </summary>
|
||
|
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");
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Creates a complete member restriction with the meaning "<paramref name="slot"/> in <paramref name="values"/>".
|
||
|
/// </summary>
|
||
|
/// <param name="possibleValues">all the values that the <paramref name="slot"/> can take</param>
|
||
|
protected MemberRestriction(MemberProjectedSlot slot, IEnumerable<Constant> values, IEnumerable<Constant> 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; }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns the variable in the member restriction.
|
||
|
/// </summary>
|
||
|
internal MemberProjectedSlot RestrictedMemberSlot
|
||
|
{
|
||
|
get { return m_restrictedMemberSlot; }
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Returns the values that <see cref="RestrictedMemberSlot"/> is being checked for.
|
||
|
/// </summary>
|
||
|
internal Domain Domain
|
||
|
{
|
||
|
get { return m_domain; }
|
||
|
}
|
||
|
#endregion
|
||
|
|
||
|
#region BoolLiteral Members
|
||
|
/// <summary>
|
||
|
/// Returns a boolean expression that is domain-aware and ready for optimizations etc.
|
||
|
/// </summary>
|
||
|
/// <param name="domainMap">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.</param>
|
||
|
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<Constant> 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;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Creates a complete member restriction based on the existing restriction with possible values for the domain being given by <paramref name="possibleValues"/>.
|
||
|
/// </summary>
|
||
|
internal abstract MemberRestriction CreateCompleteMemberRestriction(IEnumerable<Constant> possibleValues);
|
||
|
|
||
|
/// <summary>
|
||
|
/// See <see cref="BoolLiteral.GetRequiredSlots"/>.
|
||
|
/// </summary>
|
||
|
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;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// See <see cref="BoolLiteral.IsEqualTo"/>. Member restriction can be incomplete for this operation.
|
||
|
/// </summary>
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Member restriction can be incomplete for this operation.
|
||
|
/// </summary>
|
||
|
public override int GetHashCode()
|
||
|
{
|
||
|
int result = MemberProjectedSlot.EqualityComparer.GetHashCode(m_restrictedMemberSlot);
|
||
|
result ^= m_domain.GetHash();
|
||
|
return result;
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// See <see cref="BoolLiteral.IsIdentifierEqualTo"/>. Member restriction can be incomplete for this operation.
|
||
|
/// </summary>
|
||
|
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);
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// See <see cref="BoolLiteral.GetIdentifierHash"/>. Member restriction can be incomplete for this operation.
|
||
|
/// </summary>
|
||
|
protected override int GetIdentifierHash()
|
||
|
{
|
||
|
int result = MemberProjectedSlot.EqualityComparer.GetHashCode(m_restrictedMemberSlot);
|
||
|
return result;
|
||
|
}
|
||
|
#endregion
|
||
|
|
||
|
#region Other Methods
|
||
|
/// <summary>
|
||
|
/// Converts this to a user-understandable string.
|
||
|
/// </summary>
|
||
|
/// <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>
|
||
|
[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<Constant> 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<Constant>(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<Constant>(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<object, string> resourceName0 = null;
|
||
|
Func<object, object, string> resourceName1 = null;
|
||
|
|
||
|
if (invertOutput)
|
||
|
{
|
||
|
if (isNull)
|
||
|
{
|
||
|
resourceName0 = isTypeConstant ? (Func<object, string>)Strings.ViewGen_OneOfConst_IsNonNullable : (Func<object, string>)Strings.ViewGen_OneOfConst_MustBeNonNullable;
|
||
|
}
|
||
|
else if (constants.Count == 1)
|
||
|
{
|
||
|
resourceName1 = isTypeConstant ? (Func<object, object, string>)Strings.ViewGen_OneOfConst_IsNotEqualTo : (Func<object, object, string>)Strings.ViewGen_OneOfConst_MustNotBeEqualTo;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
resourceName1 = isTypeConstant ? (Func<object, object, string>)Strings.ViewGen_OneOfConst_IsNotOneOf : (Func<object, object, string>)Strings.ViewGen_OneOfConst_MustNotBeOneOf;
|
||
|
}
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
if (isNull)
|
||
|
{
|
||
|
resourceName0 = isTypeConstant ? (Func<object, string>)Strings.ViewGen_OneOfConst_MustBeNull : (Func<object, string>)Strings.ViewGen_OneOfConst_MustBeNull;
|
||
|
}
|
||
|
else if (constants.Count == 1)
|
||
|
{
|
||
|
resourceName1 = isTypeConstant ? (Func<object, object, string>)Strings.ViewGen_OneOfConst_IsEqualTo : (Func<object, object, string>)Strings.ViewGen_OneOfConst_MustBeEqualTo;
|
||
|
}
|
||
|
else
|
||
|
{
|
||
|
resourceName1 = isTypeConstant ? (Func<object, object, string>)Strings.ViewGen_OneOfConst_IsOneOf : (Func<object, object, string>)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);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/// <summary>
|
||
|
/// Modifies builder to contain a message for inverting the typeConstants, i.e., NOT(p in Person) becomes p in Customer.
|
||
|
/// </summary>
|
||
|
[System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
|
||
|
private void InvertOutputStringForTypeConstant(StringBuilder builder, Set<Constant> 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<EdmType> allTypes = new Set<EdmType>();
|
||
|
|
||
|
// 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<EdmType> oneOfTypes = new Set<EdmType>();
|
||
|
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
|
||
|
}
|
||
|
}
|