CellConstantDomain.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

/ Dotnetfx_Win7_3.5.1 / Dotnetfx_Win7_3.5.1 / 3.5.1 / DEVDIV / depot / DevDiv / releases / Orcas / NetFXw7 / ndp / fx / src / DataEntity / System / Data / Map / ViewGeneration / Structures / CellConstantDomain.cs / 1 / CellConstantDomain.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

using System.Data.Common.Utils; 
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Data.Metadata.Edm; 
using System.Data.Mapping.ViewGeneration.Utils;
using System.Linq; 
 
namespace System.Data.Mapping.ViewGeneration.Structures {
 
    using CellConstantSet = Set;
    using CellConstantRWSet = Set;
    using System.Data.Entity;
 
    // A set of cell constants -- to keep track of a cell constant's domain
    // values. It encapsulates the notions of NULL, NOT NULL and can be 
    // enhanced in the future with more functionality 
    // To represent "infinite" domains such as integer, a special constant CellConstant.NotNull is used.
    // For example: domain of System.Boolean is {true, false}, domain of 
    // (nullable) System.Int32 property is {Null, NotNull}.
    internal class CellConstantDomain : InternalBase {

        #region Constructors 
        // effects: Creates an "fully-done" set with no values -- possibleDiscreteValues are the values
        // that this domain can take 
        internal CellConstantDomain(CellConstant value, IEnumerable possibleDiscreteValues) : 
                                    this(new CellConstant[] {value}, possibleDiscreteValues) {
        } 

        // effects: Creates a domain populated using values -- possibleValues
        // are all possible values that this can take
        internal CellConstantDomain(IEnumerable values, 
                                    IEnumerable possibleDiscreteValues) {
            // Note that the values can contain both null and not null 
            Debug.Assert(values != null); 
            Debug.Assert(possibleDiscreteValues != null);
            // Determine the possibleValues first and then create the negatedConstant 
            m_possibleValues = DeterminePossibleValues(values, possibleDiscreteValues);

            // Now we need to make sure that m_domain is correct. if "values" (v) already has
            // the negated stuff, we need to make sure it is in conformance 
            // with what m_possibleValues (p) has
 
            // For NOT --> Add all constants into d that are present in p but 
            // not in the NOT
            // v = 1, NOT(1, 2); p = 1, 2, 3 => d = 1, NOT(1, 2, 3), 3 
            // v = 1, 2, NOT(1); p = 1, 2, 4 => d = 1, 2, 4, NOT(1, 2, 4)
            // v = 1, 2, NOT(1, 2, 4), NOT(1, 2, 4, 5); p = 1, 2, 4, 5, 6 =>
            // d = 1, 2, 5, 6, NOT(1, 2, 4, 5, 6)
 
            // NotNull works naturally now. If possibleValues has (1, 2,
            // NULL) and values has NOT(NULL), add 1, 2 to m_domain 
 
            m_domain = NormalizeDomain(values, m_possibleValues);
            Debug.Assert(CheckRepInvariantLocal()); 
        }

        // effects: Creates a copy of the set "domain"
        internal CellConstantDomain(CellConstantDomain domain) { 
            m_domain = new Set(domain.m_domain, CellConstant.EqualityComparer);
            m_possibleValues = new Set(domain.m_possibleValues, CellConstant.EqualityComparer); 
            Debug.Assert(CheckRepInvariantLocal()); 
        }
        #endregion 

        #region Fields
        // The set of values in the cell constant domain
        private CellConstantSet m_domain; // e.g., 1, 2, NULL, NOT(1, 2, NULL) 
        private CellConstantSet m_possibleValues; // e.g., 1, 2, NULL, Undefined
        // Invariant: m_domain is a subset of m_possibleValues except for a 
        // negated constant 
        #endregion
 
        #region Properties
        // effects: Returns all the possible values that this can contain (including the negated constants)
        internal IEnumerable AllPossibleValues {
            get { return AllPossibleValuesInternal;} 
        }
 
        // effects: Returns all the possible values that this can contain (including the negated constants) 
        private Set AllPossibleValuesInternal {
            get { 
                NegatedCellConstant negatedPossibleValue = new NegatedCellConstant(m_possibleValues);
                return m_possibleValues.Union(new CellConstant[] { negatedPossibleValue });
            }
        } 

        // effects: Returns the number of constants in this (including a negated constant) 
        internal int Count { 
            get { return m_domain.Count;}
        } 

        // effects: Yields all the values in this
        internal IEnumerable Values {
            get { return m_domain;} 
        }
        #endregion 
 
        #region Static Helper Methods to create cell constant sets from metadata
        // effects: Given a member, determines all possible values that can be created from Metadata 
        internal static CellConstantSet CreateDomainSetFromMemberPath(MemberPath memberPath, MetadataWorkspace workspace) {
            CellConstantSet domain = CreateDomainFromType(memberPath.EdmType, workspace);
            if (memberPath.IsNullable) {
                domain.Add(CellConstant.Null); 
            }
            return domain; 
        } 

        // effects: Given a slot, determines all possible values that can be created from Metadata 
        private static CellConstantSet CreateDomainSetFromSlot(JoinTreeSlot slot, MetadataWorkspace workspace) {
            CellConstantSet domain = CreateDomainSetFromMemberPath(slot.MemberPath, workspace);
            return domain;
        } 

        // effects: Given a type, determines all possible values that can be created from Metadata 
        private static CellConstantSet CreateDomainFromType(EdmType type, MetadataWorkspace workspace) { 
            CellConstantSet domain = null;
            PrimitiveType scalarType = type as PrimitiveType; 
            EnumType enumType = type as EnumType;
            if (scalarType != null) {
                // Get the domain for scalars -- for booleans, we special case.
                if (scalarType.PrimitiveTypeKind == PrimitiveTypeKind.Boolean) { 
                    domain = new Set(CreateList(true, false), CellConstant.EqualityComparer);
                } else { 
                    // Unbounded domain -- null is added by caller if the 
                    // property of this type is nullable
                    domain = new Set(CellConstant.EqualityComparer); 
                    domain.Add(CellConstant.NotNull);
                }
            } else if (enumType != null) {
                // Get all the values for the enumeration 
                domain = GetEnumeratedDomain(enumType);
            } else { 
                Debug.Assert(Helper.IsEntityType(type) || Helper.IsComplexType(type) || Helper.IsRefType(type) || Helper.IsAssociationType(type)); 
                // Treat ref types as their referenced entity types
                if (Helper.IsRefType(type)) { 
                    type = ((RefType)type).ElementType;
                }
                List types = new List();
                foreach (EdmType derivedType in MetadataHelper.GetTypeAndSubtypesOf(type, workspace, false /*includeAbstractTypes*/)) 
                {
                    TypeConstant derivedTypeConstant = new TypeConstant(derivedType); 
                    types.Add(derivedTypeConstant); 
                }
                domain = new Set(types, CellConstant.EqualityComparer); 
            }
            Debug.Assert(domain != null, "Domain not set up for some type");
            return domain;
        } 

        // effect: returns the default value for the member 
        // if the member is nullable and has no default, changes default value to CellConstant.NULL and returns true 
        // if the mebmer is not nullable and has no default, returns false
        // CHANGE_[....]_FEATURE_DEFAULT_VALUES: return the right default once metadata supports it 
        internal static bool TryGetDefaultValueForMemberPath(MemberPath memberPath, out CellConstant defaultConstant) {
            object defaultValue = memberPath.DefaultValue;
            defaultConstant = CellConstant.Null;
            if (defaultValue != null) { 
                defaultConstant = new ScalarConstant(defaultValue);
                return true; 
            } 
            else if (memberPath.IsNullable || memberPath.IsComputed) {
                return true; 
            }
            return false;
        }
 
        internal static CellConstant GetDefaultValueForMemberPath(MemberPath memberPath, IEnumerable wrappersForErrorReporting,
                                                                  ConfigViewGenerator config) 
        { 
            CellConstant defaultValue = null;
            if (!CellConstantDomain.TryGetDefaultValueForMemberPath(memberPath, out defaultValue)) 
            {
                string message = Strings.ViewGen_No_Default_Value_1( memberPath.Extent.Name, memberPath.PathToString(false));
                ErrorLog.Record record = new ErrorLog.Record(true, ViewGenErrorCode.NoDefaultValue, message, wrappersForErrorReporting, String.Empty);
                ExceptionHelpers.ThrowMappingException(record, config); 
            }
            return defaultValue; 
        } 

        // effects: Given an enumeration type, enumType, returns the set of 
        // values that enumType can take
        private static CellConstantSet GetEnumeratedDomain(EnumType enumType) {
            Set values = new Set(CellConstant.EqualityComparer);
            foreach (EnumMember enumerationValue in enumType.EnumMembers) { 
                values.Add(new ScalarConstant(enumerationValue));
            } 
            return values; 
        }
        #endregion 

        #region External methods
        internal int GetHash() {
            int result = 0; 
            foreach (CellConstant constant in m_domain) {
                result ^= CellConstant.EqualityComparer.GetHashCode(constant); 
            } 
            return result;
        } 

        // effects: Returns true iff this domain has the same values as
        // second. Note that this method performs a semantic check not just
        // an element by element check 
        internal bool IsEqualTo(CellConstantDomain second) {
            return m_domain.SetEquals(second.m_domain); 
        } 

        // requires: this is fully done 
        // effects: Returns true iff this contains NOT(NULL OR ....)
        internal bool ContainsNotNull() {
            NegatedCellConstant negated = GetNegatedConstant(m_domain);
            return negated != null && negated.Contains(CellConstant.Null); 
        }
 
        ///  
        /// Returns true if the domain contains the given Cell Constant
        ///  
        internal bool Contains(CellConstant constant)
        {
            return m_domain.Contains(constant);
        } 

        internal override bool CheckRepInvariant() { 
            return CheckRepInvariantLocal(); 
        }
 
        // effects: Given a set of values in domain, "normalizes" it, i.e.,
        // all positive constants are seperated out and any negative constant
        // is changed s.t. it is the negative of all positive values
        // extraValues indicates more constants that domain could take, e.g., 
        // domain could be "1, 2, NOT(1, 2)", extraValues could be "3". In
        // this case, we return "1, 2, NOT(1, 2, 3)" 
        internal static CellConstantSet NormalizeDomain(IEnumerable domain, IEnumerable extraValues) { 
            CellConstantSet possibleValues = DeterminePossibleValues(domain, extraValues);
 
            // For NOT --> Add all constants into d that are present in p but
            // not in the NOT
            // v = 1, NOT(1, 2); p = 1, 2, 3 => d = 1, NOT(1, 2, 3), 3
            // v = 1, 2, NOT(1); p = 1, 2, 4 => d = 1, 2, 4, NOT(1, 2, 4) 
            // v = 1, 2, NOT(1, 2, 4), NOT(1, 2, 4, 5); p = 1, 2, 4, 5, 6 => d = 1, 2, 5, 6, NOT(1, 2, 4, 5, 6)
 
            // NotNull works naturally now. If possibleValues has (1, 2, NULL) 
            // and values has NOT(NULL), add 1, 2 to m_domain
 
            CellConstantSet result = new Set(CellConstant.EqualityComparer);

            foreach (CellConstant constant in domain) {
                NegatedCellConstant negated = constant as NegatedCellConstant; 
                if (negated != null) {
                    result.Add(new NegatedCellConstant(possibleValues)); 
                    // Compute all elements in possibleValues that are not present in negated. E.g., if 
                    // negated is NOT(1, 2, 3) and possibleValues is 1, 2, 3,
                    // 4, we need to add 4 to result 
                    CellConstantSet remainingElements = possibleValues.Difference(negated.Elements);
                    result.AddRange(remainingElements);
                } else {
                    result.Add(constant); 
                }
            } 
            return result; 
        }
        // effects: Given a set of values in domain 
        // Returns all possible values that are present in domain. If
        // addNegatedIfPresent is true, the negated of all possible values is
        // added if a negated value is present
        static CellConstantSet DeterminePossibleValues(IEnumerable domain, bool addNegatedIfPresent) { 

            // E.g., if we have 1, 2, NOT(1) --> Result = 1, 2 
            // 1, NOT(1, 2) --> Result = 1, 2 
            // 1, 2, NOT(NULL) --> Result = 1, 2, NULL
            // 1, 2, NOT(2), NOT(3, 4) --> Result = 1, 2, 3, 4 

            CellConstantSet result = new CellConstantRWSet(CellConstant.EqualityComparer);
            bool hasNegated = false;
 
            foreach (CellConstant constant in domain) {
                NegatedCellConstant negated = constant as NegatedCellConstant; 
                if (negated != null) { 
                    hasNegated = true;
                    // Go through all the constants in negated and add them to domain 
                    // We add them to possible values also even if (say) Null is not
                    // allowed because we want the complete partitioning of
                    // the space, e.g., if the values specified by the
                    // caller are 1, NotNull -> we want 1, Null, Not(1, 
                    // Null) we can represent Not-Null as 1 + Not(1, NotNull)
                    foreach (CellConstant constElement in negated.Elements) { 
                        Debug.Assert(constElement as NegatedCellConstant == null, 
                                     "Negated cell constant inside NegatedCellConstant");
                        result.Add(constElement); 
                    }
                } else {
                    result.Add(constant);
                } 
            }
 
            if (hasNegated && addNegatedIfPresent) { 
                NegatedCellConstant negatedValue = new NegatedCellConstant(result);
                result.Add(negatedValue); 
            }

            return result;
        } 
        #endregion
 
        #region Helper methods for determining domains from cells 
        // effects: Given a set of cells, returns all the different values
        // that each memberPath in cells can take 
        internal static Dictionary
            ComputeConstantDomainSetsForSlotsInQueryViews(IEnumerable cells, MetadataWorkspace workspace) {

            Dictionary cDomainMap = 
                new Dictionary(MemberPath.EqualityComparer);
 
            foreach (Cell cell in cells) { 
                CellQuery cQuery = cell.CQuery;
                // Go through the conjuncts to get the constants (e.g., we 
                // just don't want to NULL, NOT(NULL). We want to say that
                // the possible values are NULL, 4, NOT(NULL, 4)
                foreach (OneOfConst oneOfConst in cQuery.GetConjunctsFromWhereClause()) {
                    JoinTreeSlot slot = oneOfConst.Slot; 
                    CellConstantSet cDomain = CreateDomainSetFromSlot(slot, workspace);
                    // Now we add the domain of oneConst into this 
                        //Isnull=true and Isnull=false conditions should not contribute to a member's domain 
                    cDomain.AddRange(oneOfConst.Values.Values.Where(c=> !(c.Equals(CellConstant.Null)||c.Equals(CellConstant.NotNull))));
                    CellConstantSet values; 
                    bool found = cDomainMap.TryGetValue(slot.MemberPath, out values);
                    if (!found) {
                        cDomainMap[slot.MemberPath] = cDomain;
                    } else { 
                        values.AddRange(cDomain);
                    } 
                } 
            }
            return cDomainMap; 
        }

        // effects: returns a dictionary that maps each S-side slot whose domain can be restricted to such an enumerated domain
        // The resulting domain is a union of 
        // (a) constants appearing in conditions on that slot on S-side
        // (b) constants appearing in conditions on the respective slot on C-side, if the given slot 
        //     is projected (on the C-side) and no conditions are placed on it on S-side 
        // (c) default value of the slot based on metadata
        internal static Dictionary 
            ComputeConstantDomainSetsForSlotsInUpdateViews(IEnumerable cells, MetadataWorkspace workspace) {

            Dictionary sDomainMap =
                new Dictionary(MemberPath.EqualityComparer); 

            foreach (Cell cell in cells) { 
                CellQuery cQuery = cell.CQuery; 
                CellQuery sQuery = cell.SQuery;
                foreach (JoinTreeSlot sSlot in sQuery.GetAllQuerySlots()) { 
                    // obtain initial slot domain and restrict it if the slot has conditions
                    CellConstantSet sDomain = CreateDomainSetFromSlot(sSlot, workspace);
                    CellConstantSet restrictedDomain = RestrictDomainByWhereClause(sDomain, sSlot, sQuery);
 
                    // Suppose that we have a cell: Proj(ID, A) Select(A=5)(E) == T(ID, B)
                    // In the above cell, B on the S-side is 5 and we add 
                    // that to its range. But if B had a restriction, we do 
                    // not add 5. Note that do we not have a problem
                    // w.r.t. possibleValues since if A=5 and B=1, we have an 
                    // empty cell -- we should catch that as an error. If A = 5
                    // and B = 5 is present then restrictedDomain and sDomain
                    // are the same
 
                    // if no restriction on the S-side and the slot is projected then take the domain from the C-side
                    if (restrictedDomain.SetEquals(sDomain)) { 
                        int projectedPosition = sQuery.GetProjectedPosition(sSlot); 
                        if (projectedPosition >= 0) {
                            // get the domain of the respective C-side slot 
                            JoinTreeSlot cSlot = cQuery.ProjectedSlotAt(projectedPosition) as JoinTreeSlot;
                            Debug.Assert(cSlot != null, "Assuming constants are not projected");
                            CellConstantSet cDomain = CreateDomainSetFromSlot(cSlot, workspace);
                            restrictedDomain = RestrictDomainByWhereClause(cDomain, cSlot, cQuery); 
                        }
                    } 
                    // Add the default value to the domain 
                    MemberPath sSlotMemberPath = sSlot.MemberPath;
                    CellConstant defaultValue; 
                    if (TryGetDefaultValueForMemberPath(sSlotMemberPath, out defaultValue)) {
                        restrictedDomain.Add(defaultValue);
                    }
 
                    // add all constants appearing in the domain to sDomainMap
                    CellConstantSet sSlotDomain; 
                    if (false == sDomainMap.TryGetValue(sSlotMemberPath, out sSlotDomain)) { 
                        sDomainMap[sSlotMemberPath] = restrictedDomain;
                    } else { 
                        sSlotDomain.AddRange(restrictedDomain);
                    }
                }
            } 
            return sDomainMap;
        } 
 
        // requires: domain not have any Negated constants other than NotNull
        // Also, cellQuery contains all final oneOfConsts or all partial oneOfConsts 
        // cellquery must contain a whereclause of the form "True", "OneOfConst" or "
        // "OneOfConst AND ... AND OneOfConst"
        // slot must present in cellQuery and incomingDomain is the domain for it
        // effects: Returns the set of values that slot can take as restricted by cellQuery's whereClause 
        private static CellConstantSet RestrictDomainByWhereClause(IEnumerable domain, JoinTreeSlot slot,
                                                                   CellQuery cellQuery) { 
            // Keep track of different values 
            CellConstantSet allValuesInQuery = null;
 
            foreach (OneOfConst oneOfConst in cellQuery.GetConjunctsFromWhereClause()) {
                // Determine the clause that is relevant for this slot
                if (false == MemberPath.EqualityComparer.Equals(oneOfConst.Slot.MemberPath,
                                                                slot.MemberPath)) { 
                    continue;
                } 
                Debug.Assert(allValuesInQuery == null, "More than one Clause with the same path"); 
                allValuesInQuery = new CellConstantRWSet(oneOfConst.Values.Values, CellConstant.EqualityComparer);
            } 

            // If the slot was not mentioned in the query, do nothing
            if (allValuesInQuery == null) {
                return new CellConstantRWSet(domain, CellConstant.EqualityComparer); 
            }
 
            // Now get all the possible values from domain and allValuesInQuery 
            CellConstantSet combined = DeterminePossibleValues(allValuesInQuery, domain);
 
            // We can now create CellConstantDomains!
            CellConstantDomain restrictedDomain = new CellConstantDomain(domain, combined);
            CellConstantDomain allDomainsInQuery = new CellConstantDomain(allValuesInQuery, combined);
 
            // Intersect the two domains to get the result
            restrictedDomain = restrictedDomain.Intersect(allDomainsInQuery); 
                                                                       CellConstantSet result = new CellConstantRWSet(restrictedDomain.Values, CellConstant.EqualityComparer); 
            return result;
        } 
        #endregion

        #region Private helper methods
        // effects: Intersects the values in second with this domain and 
        // returns the result
        private CellConstantDomain Intersect(CellConstantDomain second) { 
            CheckTwoDomainInvariants(this, second); 
            CellConstantDomain result = new CellConstantDomain(this);
            result.m_domain.Intersect(second.m_domain); 
            return result;
        }

        // requires: constants has at most one NegatedCellConstant 
        // effects: Returns the NegatedCellConstant in this if any. Else
        // returns null 
        private static NegatedCellConstant GetNegatedConstant(IEnumerable constants) { 
            NegatedCellConstant result = null;
            foreach (CellConstant constant in constants) { 
                NegatedCellConstant negated = constant as NegatedCellConstant;
                if (negated != null) {
                    Debug.Assert(result == null, "Multiple negated cell constants?");
                    result = negated; 
                }
            } 
            return result; 
        }
 
        // effects: Given a set of values in domain1 and domain2,
        // Returns all possible positive values that are present in domain1 and domain2
        private static CellConstantSet DeterminePossibleValues(IEnumerable domain1,
                                                               IEnumerable domain2) { 

            // E.g., if we have 1, 2, NOT(1) --> Result = 1, 2 
            // 1, NOT(1, 2) --> Result = 1, 2 
            // 1, 2, NOT(NULL) --> Result = 1, 2, NULL
            // 1, 2, NOT(2), NOT(3, 4) --> Result = 1, 2, 3, 4 

                                                                   CellConstantSet union = new CellConstantRWSet(domain1, CellConstant.EqualityComparer);
            union.Unite(domain2);
            CellConstantSet result = DeterminePossibleValues(union, false); 
            return result;
        } 
 
        // effects: Checks that two domains, domain1 and domain2, that are being compared/unioned/intersected, etc
        // are compatible with each other 
        [Conditional("DEBUG")]
        private static void CheckTwoDomainInvariants(CellConstantDomain domain1, CellConstantDomain domain2) {
            domain1.CheckRepInvariant();
            domain2.CheckRepInvariant(); 

            // The possible values must match 
            Debug.Assert(domain1.m_possibleValues.SetEquals(domain2.m_possibleValues), "domains must be compatible"); 
        }
 
        // effects: A helper method. Given two
        // values, yields a list of CellConstants in the order of values
        private static IEnumerable CreateList(object value1, object value2) {
            yield return new ScalarConstant(value1); 
            yield return new ScalarConstant(value2);
        } 
 
        // effects: Checks the invariants in "this"
        private bool CheckRepInvariantLocal() { 
            // Make sure m_domain has at most one negatedCellConstant
            //  m_possibleValues has none
            NegatedCellConstant negated = GetNegatedConstant(m_domain); // Can be null or not-null
            Debug.Assert(negated == null || negated.CheckRepInvariantLocal(), 
                         "Negated cell constant's invariant broken");
            negated = GetNegatedConstant(m_possibleValues); 
            Debug.Assert(negated == null, "m_possibleValues cannot contain negated constant"); 

            Debug.Assert(m_domain.IsSubsetOf(AllPossibleValuesInternal), 
                         "All domain values must be contained in possibleValues");
            return true;
        }
        #endregion 

        #region String methods 
        // effects: Returns a user-friendly string that can be reported to an end-user 
        internal string ToUserString() {
            StringBuilder builder = new StringBuilder(); 
            bool isFirst = true;
            foreach (CellConstant constant in m_domain) {
                if (isFirst == false) {
                    builder.Append(", "); 
                }
                builder.Append(constant.ToUserString()); 
                isFirst = false; 
            }
            return builder.ToString(); 
        }

        internal override void ToCompactString(StringBuilder builder) {
            builder.Append(ToUserString()); 
        }
        #endregion 
 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner [....]
// @backupOwner [....] 
//--------------------------------------------------------------------- 

using System.Data.Common.Utils; 
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Data.Metadata.Edm; 
using System.Data.Mapping.ViewGeneration.Utils;
using System.Linq; 
 
namespace System.Data.Mapping.ViewGeneration.Structures {
 
    using CellConstantSet = Set;
    using CellConstantRWSet = Set;
    using System.Data.Entity;
 
    // A set of cell constants -- to keep track of a cell constant's domain
    // values. It encapsulates the notions of NULL, NOT NULL and can be 
    // enhanced in the future with more functionality 
    // To represent "infinite" domains such as integer, a special constant CellConstant.NotNull is used.
    // For example: domain of System.Boolean is {true, false}, domain of 
    // (nullable) System.Int32 property is {Null, NotNull}.
    internal class CellConstantDomain : InternalBase {

        #region Constructors 
        // effects: Creates an "fully-done" set with no values -- possibleDiscreteValues are the values
        // that this domain can take 
        internal CellConstantDomain(CellConstant value, IEnumerable possibleDiscreteValues) : 
                                    this(new CellConstant[] {value}, possibleDiscreteValues) {
        } 

        // effects: Creates a domain populated using values -- possibleValues
        // are all possible values that this can take
        internal CellConstantDomain(IEnumerable values, 
                                    IEnumerable possibleDiscreteValues) {
            // Note that the values can contain both null and not null 
            Debug.Assert(values != null); 
            Debug.Assert(possibleDiscreteValues != null);
            // Determine the possibleValues first and then create the negatedConstant 
            m_possibleValues = DeterminePossibleValues(values, possibleDiscreteValues);

            // Now we need to make sure that m_domain is correct. if "values" (v) already has
            // the negated stuff, we need to make sure it is in conformance 
            // with what m_possibleValues (p) has
 
            // For NOT --> Add all constants into d that are present in p but 
            // not in the NOT
            // v = 1, NOT(1, 2); p = 1, 2, 3 => d = 1, NOT(1, 2, 3), 3 
            // v = 1, 2, NOT(1); p = 1, 2, 4 => d = 1, 2, 4, NOT(1, 2, 4)
            // v = 1, 2, NOT(1, 2, 4), NOT(1, 2, 4, 5); p = 1, 2, 4, 5, 6 =>
            // d = 1, 2, 5, 6, NOT(1, 2, 4, 5, 6)
 
            // NotNull works naturally now. If possibleValues has (1, 2,
            // NULL) and values has NOT(NULL), add 1, 2 to m_domain 
 
            m_domain = NormalizeDomain(values, m_possibleValues);
            Debug.Assert(CheckRepInvariantLocal()); 
        }

        // effects: Creates a copy of the set "domain"
        internal CellConstantDomain(CellConstantDomain domain) { 
            m_domain = new Set(domain.m_domain, CellConstant.EqualityComparer);
            m_possibleValues = new Set(domain.m_possibleValues, CellConstant.EqualityComparer); 
            Debug.Assert(CheckRepInvariantLocal()); 
        }
        #endregion 

        #region Fields
        // The set of values in the cell constant domain
        private CellConstantSet m_domain; // e.g., 1, 2, NULL, NOT(1, 2, NULL) 
        private CellConstantSet m_possibleValues; // e.g., 1, 2, NULL, Undefined
        // Invariant: m_domain is a subset of m_possibleValues except for a 
        // negated constant 
        #endregion
 
        #region Properties
        // effects: Returns all the possible values that this can contain (including the negated constants)
        internal IEnumerable AllPossibleValues {
            get { return AllPossibleValuesInternal;} 
        }
 
        // effects: Returns all the possible values that this can contain (including the negated constants) 
        private Set AllPossibleValuesInternal {
            get { 
                NegatedCellConstant negatedPossibleValue = new NegatedCellConstant(m_possibleValues);
                return m_possibleValues.Union(new CellConstant[] { negatedPossibleValue });
            }
        } 

        // effects: Returns the number of constants in this (including a negated constant) 
        internal int Count { 
            get { return m_domain.Count;}
        } 

        // effects: Yields all the values in this
        internal IEnumerable Values {
            get { return m_domain;} 
        }
        #endregion 
 
        #region Static Helper Methods to create cell constant sets from metadata
        // effects: Given a member, determines all possible values that can be created from Metadata 
        internal static CellConstantSet CreateDomainSetFromMemberPath(MemberPath memberPath, MetadataWorkspace workspace) {
            CellConstantSet domain = CreateDomainFromType(memberPath.EdmType, workspace);
            if (memberPath.IsNullable) {
                domain.Add(CellConstant.Null); 
            }
            return domain; 
        } 

        // effects: Given a slot, determines all possible values that can be created from Metadata 
        private static CellConstantSet CreateDomainSetFromSlot(JoinTreeSlot slot, MetadataWorkspace workspace) {
            CellConstantSet domain = CreateDomainSetFromMemberPath(slot.MemberPath, workspace);
            return domain;
        } 

        // effects: Given a type, determines all possible values that can be created from Metadata 
        private static CellConstantSet CreateDomainFromType(EdmType type, MetadataWorkspace workspace) { 
            CellConstantSet domain = null;
            PrimitiveType scalarType = type as PrimitiveType; 
            EnumType enumType = type as EnumType;
            if (scalarType != null) {
                // Get the domain for scalars -- for booleans, we special case.
                if (scalarType.PrimitiveTypeKind == PrimitiveTypeKind.Boolean) { 
                    domain = new Set(CreateList(true, false), CellConstant.EqualityComparer);
                } else { 
                    // Unbounded domain -- null is added by caller if the 
                    // property of this type is nullable
                    domain = new Set(CellConstant.EqualityComparer); 
                    domain.Add(CellConstant.NotNull);
                }
            } else if (enumType != null) {
                // Get all the values for the enumeration 
                domain = GetEnumeratedDomain(enumType);
            } else { 
                Debug.Assert(Helper.IsEntityType(type) || Helper.IsComplexType(type) || Helper.IsRefType(type) || Helper.IsAssociationType(type)); 
                // Treat ref types as their referenced entity types
                if (Helper.IsRefType(type)) { 
                    type = ((RefType)type).ElementType;
                }
                List types = new List();
                foreach (EdmType derivedType in MetadataHelper.GetTypeAndSubtypesOf(type, workspace, false /*includeAbstractTypes*/)) 
                {
                    TypeConstant derivedTypeConstant = new TypeConstant(derivedType); 
                    types.Add(derivedTypeConstant); 
                }
                domain = new Set(types, CellConstant.EqualityComparer); 
            }
            Debug.Assert(domain != null, "Domain not set up for some type");
            return domain;
        } 

        // effect: returns the default value for the member 
        // if the member is nullable and has no default, changes default value to CellConstant.NULL and returns true 
        // if the mebmer is not nullable and has no default, returns false
        // CHANGE_[....]_FEATURE_DEFAULT_VALUES: return the right default once metadata supports it 
        internal static bool TryGetDefaultValueForMemberPath(MemberPath memberPath, out CellConstant defaultConstant) {
            object defaultValue = memberPath.DefaultValue;
            defaultConstant = CellConstant.Null;
            if (defaultValue != null) { 
                defaultConstant = new ScalarConstant(defaultValue);
                return true; 
            } 
            else if (memberPath.IsNullable || memberPath.IsComputed) {
                return true; 
            }
            return false;
        }
 
        internal static CellConstant GetDefaultValueForMemberPath(MemberPath memberPath, IEnumerable wrappersForErrorReporting,
                                                                  ConfigViewGenerator config) 
        { 
            CellConstant defaultValue = null;
            if (!CellConstantDomain.TryGetDefaultValueForMemberPath(memberPath, out defaultValue)) 
            {
                string message = Strings.ViewGen_No_Default_Value_1( memberPath.Extent.Name, memberPath.PathToString(false));
                ErrorLog.Record record = new ErrorLog.Record(true, ViewGenErrorCode.NoDefaultValue, message, wrappersForErrorReporting, String.Empty);
                ExceptionHelpers.ThrowMappingException(record, config); 
            }
            return defaultValue; 
        } 

        // effects: Given an enumeration type, enumType, returns the set of 
        // values that enumType can take
        private static CellConstantSet GetEnumeratedDomain(EnumType enumType) {
            Set values = new Set(CellConstant.EqualityComparer);
            foreach (EnumMember enumerationValue in enumType.EnumMembers) { 
                values.Add(new ScalarConstant(enumerationValue));
            } 
            return values; 
        }
        #endregion 

        #region External methods
        internal int GetHash() {
            int result = 0; 
            foreach (CellConstant constant in m_domain) {
                result ^= CellConstant.EqualityComparer.GetHashCode(constant); 
            } 
            return result;
        } 

        // effects: Returns true iff this domain has the same values as
        // second. Note that this method performs a semantic check not just
        // an element by element check 
        internal bool IsEqualTo(CellConstantDomain second) {
            return m_domain.SetEquals(second.m_domain); 
        } 

        // requires: this is fully done 
        // effects: Returns true iff this contains NOT(NULL OR ....)
        internal bool ContainsNotNull() {
            NegatedCellConstant negated = GetNegatedConstant(m_domain);
            return negated != null && negated.Contains(CellConstant.Null); 
        }
 
        ///  
        /// Returns true if the domain contains the given Cell Constant
        ///  
        internal bool Contains(CellConstant constant)
        {
            return m_domain.Contains(constant);
        } 

        internal override bool CheckRepInvariant() { 
            return CheckRepInvariantLocal(); 
        }
 
        // effects: Given a set of values in domain, "normalizes" it, i.e.,
        // all positive constants are seperated out and any negative constant
        // is changed s.t. it is the negative of all positive values
        // extraValues indicates more constants that domain could take, e.g., 
        // domain could be "1, 2, NOT(1, 2)", extraValues could be "3". In
        // this case, we return "1, 2, NOT(1, 2, 3)" 
        internal static CellConstantSet NormalizeDomain(IEnumerable domain, IEnumerable extraValues) { 
            CellConstantSet possibleValues = DeterminePossibleValues(domain, extraValues);
 
            // For NOT --> Add all constants into d that are present in p but
            // not in the NOT
            // v = 1, NOT(1, 2); p = 1, 2, 3 => d = 1, NOT(1, 2, 3), 3
            // v = 1, 2, NOT(1); p = 1, 2, 4 => d = 1, 2, 4, NOT(1, 2, 4) 
            // v = 1, 2, NOT(1, 2, 4), NOT(1, 2, 4, 5); p = 1, 2, 4, 5, 6 => d = 1, 2, 5, 6, NOT(1, 2, 4, 5, 6)
 
            // NotNull works naturally now. If possibleValues has (1, 2, NULL) 
            // and values has NOT(NULL), add 1, 2 to m_domain
 
            CellConstantSet result = new Set(CellConstant.EqualityComparer);

            foreach (CellConstant constant in domain) {
                NegatedCellConstant negated = constant as NegatedCellConstant; 
                if (negated != null) {
                    result.Add(new NegatedCellConstant(possibleValues)); 
                    // Compute all elements in possibleValues that are not present in negated. E.g., if 
                    // negated is NOT(1, 2, 3) and possibleValues is 1, 2, 3,
                    // 4, we need to add 4 to result 
                    CellConstantSet remainingElements = possibleValues.Difference(negated.Elements);
                    result.AddRange(remainingElements);
                } else {
                    result.Add(constant); 
                }
            } 
            return result; 
        }
        // effects: Given a set of values in domain 
        // Returns all possible values that are present in domain. If
        // addNegatedIfPresent is true, the negated of all possible values is
        // added if a negated value is present
        static CellConstantSet DeterminePossibleValues(IEnumerable domain, bool addNegatedIfPresent) { 

            // E.g., if we have 1, 2, NOT(1) --> Result = 1, 2 
            // 1, NOT(1, 2) --> Result = 1, 2 
            // 1, 2, NOT(NULL) --> Result = 1, 2, NULL
            // 1, 2, NOT(2), NOT(3, 4) --> Result = 1, 2, 3, 4 

            CellConstantSet result = new CellConstantRWSet(CellConstant.EqualityComparer);
            bool hasNegated = false;
 
            foreach (CellConstant constant in domain) {
                NegatedCellConstant negated = constant as NegatedCellConstant; 
                if (negated != null) { 
                    hasNegated = true;
                    // Go through all the constants in negated and add them to domain 
                    // We add them to possible values also even if (say) Null is not
                    // allowed because we want the complete partitioning of
                    // the space, e.g., if the values specified by the
                    // caller are 1, NotNull -> we want 1, Null, Not(1, 
                    // Null) we can represent Not-Null as 1 + Not(1, NotNull)
                    foreach (CellConstant constElement in negated.Elements) { 
                        Debug.Assert(constElement as NegatedCellConstant == null, 
                                     "Negated cell constant inside NegatedCellConstant");
                        result.Add(constElement); 
                    }
                } else {
                    result.Add(constant);
                } 
            }
 
            if (hasNegated && addNegatedIfPresent) { 
                NegatedCellConstant negatedValue = new NegatedCellConstant(result);
                result.Add(negatedValue); 
            }

            return result;
        } 
        #endregion
 
        #region Helper methods for determining domains from cells 
        // effects: Given a set of cells, returns all the different values
        // that each memberPath in cells can take 
        internal static Dictionary
            ComputeConstantDomainSetsForSlotsInQueryViews(IEnumerable cells, MetadataWorkspace workspace) {

            Dictionary cDomainMap = 
                new Dictionary(MemberPath.EqualityComparer);
 
            foreach (Cell cell in cells) { 
                CellQuery cQuery = cell.CQuery;
                // Go through the conjuncts to get the constants (e.g., we 
                // just don't want to NULL, NOT(NULL). We want to say that
                // the possible values are NULL, 4, NOT(NULL, 4)
                foreach (OneOfConst oneOfConst in cQuery.GetConjunctsFromWhereClause()) {
                    JoinTreeSlot slot = oneOfConst.Slot; 
                    CellConstantSet cDomain = CreateDomainSetFromSlot(slot, workspace);
                    // Now we add the domain of oneConst into this 
                        //Isnull=true and Isnull=false conditions should not contribute to a member's domain 
                    cDomain.AddRange(oneOfConst.Values.Values.Where(c=> !(c.Equals(CellConstant.Null)||c.Equals(CellConstant.NotNull))));
                    CellConstantSet values; 
                    bool found = cDomainMap.TryGetValue(slot.MemberPath, out values);
                    if (!found) {
                        cDomainMap[slot.MemberPath] = cDomain;
                    } else { 
                        values.AddRange(cDomain);
                    } 
                } 
            }
            return cDomainMap; 
        }

        // effects: returns a dictionary that maps each S-side slot whose domain can be restricted to such an enumerated domain
        // The resulting domain is a union of 
        // (a) constants appearing in conditions on that slot on S-side
        // (b) constants appearing in conditions on the respective slot on C-side, if the given slot 
        //     is projected (on the C-side) and no conditions are placed on it on S-side 
        // (c) default value of the slot based on metadata
        internal static Dictionary 
            ComputeConstantDomainSetsForSlotsInUpdateViews(IEnumerable cells, MetadataWorkspace workspace) {

            Dictionary sDomainMap =
                new Dictionary(MemberPath.EqualityComparer); 

            foreach (Cell cell in cells) { 
                CellQuery cQuery = cell.CQuery; 
                CellQuery sQuery = cell.SQuery;
                foreach (JoinTreeSlot sSlot in sQuery.GetAllQuerySlots()) { 
                    // obtain initial slot domain and restrict it if the slot has conditions
                    CellConstantSet sDomain = CreateDomainSetFromSlot(sSlot, workspace);
                    CellConstantSet restrictedDomain = RestrictDomainByWhereClause(sDomain, sSlot, sQuery);
 
                    // Suppose that we have a cell: Proj(ID, A) Select(A=5)(E) == T(ID, B)
                    // In the above cell, B on the S-side is 5 and we add 
                    // that to its range. But if B had a restriction, we do 
                    // not add 5. Note that do we not have a problem
                    // w.r.t. possibleValues since if A=5 and B=1, we have an 
                    // empty cell -- we should catch that as an error. If A = 5
                    // and B = 5 is present then restrictedDomain and sDomain
                    // are the same
 
                    // if no restriction on the S-side and the slot is projected then take the domain from the C-side
                    if (restrictedDomain.SetEquals(sDomain)) { 
                        int projectedPosition = sQuery.GetProjectedPosition(sSlot); 
                        if (projectedPosition >= 0) {
                            // get the domain of the respective C-side slot 
                            JoinTreeSlot cSlot = cQuery.ProjectedSlotAt(projectedPosition) as JoinTreeSlot;
                            Debug.Assert(cSlot != null, "Assuming constants are not projected");
                            CellConstantSet cDomain = CreateDomainSetFromSlot(cSlot, workspace);
                            restrictedDomain = RestrictDomainByWhereClause(cDomain, cSlot, cQuery); 
                        }
                    } 
                    // Add the default value to the domain 
                    MemberPath sSlotMemberPath = sSlot.MemberPath;
                    CellConstant defaultValue; 
                    if (TryGetDefaultValueForMemberPath(sSlotMemberPath, out defaultValue)) {
                        restrictedDomain.Add(defaultValue);
                    }
 
                    // add all constants appearing in the domain to sDomainMap
                    CellConstantSet sSlotDomain; 
                    if (false == sDomainMap.TryGetValue(sSlotMemberPath, out sSlotDomain)) { 
                        sDomainMap[sSlotMemberPath] = restrictedDomain;
                    } else { 
                        sSlotDomain.AddRange(restrictedDomain);
                    }
                }
            } 
            return sDomainMap;
        } 
 
        // requires: domain not have any Negated constants other than NotNull
        // Also, cellQuery contains all final oneOfConsts or all partial oneOfConsts 
        // cellquery must contain a whereclause of the form "True", "OneOfConst" or "
        // "OneOfConst AND ... AND OneOfConst"
        // slot must present in cellQuery and incomingDomain is the domain for it
        // effects: Returns the set of values that slot can take as restricted by cellQuery's whereClause 
        private static CellConstantSet RestrictDomainByWhereClause(IEnumerable domain, JoinTreeSlot slot,
                                                                   CellQuery cellQuery) { 
            // Keep track of different values 
            CellConstantSet allValuesInQuery = null;
 
            foreach (OneOfConst oneOfConst in cellQuery.GetConjunctsFromWhereClause()) {
                // Determine the clause that is relevant for this slot
                if (false == MemberPath.EqualityComparer.Equals(oneOfConst.Slot.MemberPath,
                                                                slot.MemberPath)) { 
                    continue;
                } 
                Debug.Assert(allValuesInQuery == null, "More than one Clause with the same path"); 
                allValuesInQuery = new CellConstantRWSet(oneOfConst.Values.Values, CellConstant.EqualityComparer);
            } 

            // If the slot was not mentioned in the query, do nothing
            if (allValuesInQuery == null) {
                return new CellConstantRWSet(domain, CellConstant.EqualityComparer); 
            }
 
            // Now get all the possible values from domain and allValuesInQuery 
            CellConstantSet combined = DeterminePossibleValues(allValuesInQuery, domain);
 
            // We can now create CellConstantDomains!
            CellConstantDomain restrictedDomain = new CellConstantDomain(domain, combined);
            CellConstantDomain allDomainsInQuery = new CellConstantDomain(allValuesInQuery, combined);
 
            // Intersect the two domains to get the result
            restrictedDomain = restrictedDomain.Intersect(allDomainsInQuery); 
                                                                       CellConstantSet result = new CellConstantRWSet(restrictedDomain.Values, CellConstant.EqualityComparer); 
            return result;
        } 
        #endregion

        #region Private helper methods
        // effects: Intersects the values in second with this domain and 
        // returns the result
        private CellConstantDomain Intersect(CellConstantDomain second) { 
            CheckTwoDomainInvariants(this, second); 
            CellConstantDomain result = new CellConstantDomain(this);
            result.m_domain.Intersect(second.m_domain); 
            return result;
        }

        // requires: constants has at most one NegatedCellConstant 
        // effects: Returns the NegatedCellConstant in this if any. Else
        // returns null 
        private static NegatedCellConstant GetNegatedConstant(IEnumerable constants) { 
            NegatedCellConstant result = null;
            foreach (CellConstant constant in constants) { 
                NegatedCellConstant negated = constant as NegatedCellConstant;
                if (negated != null) {
                    Debug.Assert(result == null, "Multiple negated cell constants?");
                    result = negated; 
                }
            } 
            return result; 
        }
 
        // effects: Given a set of values in domain1 and domain2,
        // Returns all possible positive values that are present in domain1 and domain2
        private static CellConstantSet DeterminePossibleValues(IEnumerable domain1,
                                                               IEnumerable domain2) { 

            // E.g., if we have 1, 2, NOT(1) --> Result = 1, 2 
            // 1, NOT(1, 2) --> Result = 1, 2 
            // 1, 2, NOT(NULL) --> Result = 1, 2, NULL
            // 1, 2, NOT(2), NOT(3, 4) --> Result = 1, 2, 3, 4 

                                                                   CellConstantSet union = new CellConstantRWSet(domain1, CellConstant.EqualityComparer);
            union.Unite(domain2);
            CellConstantSet result = DeterminePossibleValues(union, false); 
            return result;
        } 
 
        // effects: Checks that two domains, domain1 and domain2, that are being compared/unioned/intersected, etc
        // are compatible with each other 
        [Conditional("DEBUG")]
        private static void CheckTwoDomainInvariants(CellConstantDomain domain1, CellConstantDomain domain2) {
            domain1.CheckRepInvariant();
            domain2.CheckRepInvariant(); 

            // The possible values must match 
            Debug.Assert(domain1.m_possibleValues.SetEquals(domain2.m_possibleValues), "domains must be compatible"); 
        }
 
        // effects: A helper method. Given two
        // values, yields a list of CellConstants in the order of values
        private static IEnumerable CreateList(object value1, object value2) {
            yield return new ScalarConstant(value1); 
            yield return new ScalarConstant(value2);
        } 
 
        // effects: Checks the invariants in "this"
        private bool CheckRepInvariantLocal() { 
            // Make sure m_domain has at most one negatedCellConstant
            //  m_possibleValues has none
            NegatedCellConstant negated = GetNegatedConstant(m_domain); // Can be null or not-null
            Debug.Assert(negated == null || negated.CheckRepInvariantLocal(), 
                         "Negated cell constant's invariant broken");
            negated = GetNegatedConstant(m_possibleValues); 
            Debug.Assert(negated == null, "m_possibleValues cannot contain negated constant"); 

            Debug.Assert(m_domain.IsSubsetOf(AllPossibleValuesInternal), 
                         "All domain values must be contained in possibleValues");
            return true;
        }
        #endregion 

        #region String methods 
        // effects: Returns a user-friendly string that can be reported to an end-user 
        internal string ToUserString() {
            StringBuilder builder = new StringBuilder(); 
            bool isFirst = true;
            foreach (CellConstant constant in m_domain) {
                if (isFirst == false) {
                    builder.Append(", "); 
                }
                builder.Append(constant.ToUserString()); 
                isFirst = false; 
            }
            return builder.ToString(); 
        }

        internal override void ToCompactString(StringBuilder builder) {
            builder.Append(ToUserString()); 
        }
        #endregion 
 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.

                        

Link Menu

Network programming in C#, Network Programming in VB.NET, Network Programming in .NET
This book is available now!
Buy at Amazon US or
Buy at Amazon UK