StructuredTypeInfo.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 / Query / PlanCompiler / StructuredTypeInfo.cs / 1 / StructuredTypeInfo.cs

                            //---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner  [....], [....]
//--------------------------------------------------------------------- 
 
using System;
using System.Collections; 
using System.Collections.Generic;
//using System.Diagnostics; // Please use PlanCompiler.Assert instead of Debug.Assert in this class...
using System.Globalization;
using System.Linq; 
using System.Data.Common;
using md = System.Data.Metadata.Edm; 
using System.Data.Query.InternalTrees; 

namespace System.Data.Query.PlanCompiler { 

    /// 
    /// The type flattener module is part of the structured type elimination phase,
    /// and is largely responsible for "flattening" record and nominal types into 
    /// flat record types. Additionally, for nominal types, this module produces typeid
    /// values that can be used later to interpret the input data stream. 
    /// 
    /// The goal of this module is to load up information about type and entityset metadata
    /// used in the ITree. This module is part of the "StructuredTypeElimination" phase, 
    /// and provides information to help in this process.
    ///
    /// This module itself is broken down into multiple parts.
    /// 
    /// (*) Loading type information: We walk the query tree to identify all references
    ///     to structured types and entity sets 
    /// 
    /// (*) Processing entitysets: We walk the list of entitysets, and assign ids to each
    ///     entityset. We also create a map of id->entityset metadata in this phase. 
    ///
    /// (*) Processing types: We then walk the list of types, and process each type. This,
    ///     in turn, is also broken into multiple parts:
    /// 
    ///     * Populating the Type Map: we walk the list of reference types and add each of
    ///       them to our typeMap, along with their base types. 
    /// 
    ///     * TypeId assignment: We assign typeids to each nominal (complextype/entitytype).
    ///       This typeid is based on a dewey encoding. The typeid of a type is typically 
    ///       the typeid of its supertype suffixed by the subtype number of this type within
    ///       its supertype. This encoding is intended to support easy type matching
    ///       later on in the query - both for exact (IS OF ONLY) and inexact (IS OF) matches.
    /// 
    ///     * Type flattening: We then "explode"/"flatten" each structured type - refs,
    ///       entity types, complex types and record types. The result is a flattened type 
    ///       where every single property of the resulting type is a primitive/scalar type 
    ///       (Note: UDTs are considered to be scalar types). Additional information may also
    ///       be encoded as a type property. For example, a typeid property is added (if 
    ///       necessary) to complex/entity types to help discriminate polymorphic instances.
    ///       An EntitySetId property is added to ref and entity type attributes to help
    ///       determine the entity set that a given entity instance comes from.
    ///       As part of type flattening, we keep track of additional information that allows 
    ///       us to map easily from the original property to the properties in the new type
    /// 
    /// The final result of this processing is an object that contains: 
    ///
    ///  * a TypeInfo (extra type information) for each structured type in the query 
    ///  * a map from typeid value to type. To be used later by result assembly
    ///  * a map between entitysetid value and entityset. To be used later by result assembly
    ///
    /// NOTE: StructuredTypeInfo is probably not the best name for this class, since 
    ///       it doesn't derive from TypeInfo but rather manages a collection of them.
    ///       I don't have a better name, but if you come up with one change this. 
    /// 
    /// 
    internal class StructuredTypeInfo { 

        #region private state

        private md.TypeUsage m_stringType; 
        private md.TypeUsage m_intType;
        private Dictionary m_typeInfoMap; 
        private bool m_typeInfoMapPopulated; 
        private md.EntitySet[] m_entitySetIdToEntitySetMap; //used as a Dictionary with the index as key
        private Dictionary m_entitySetToEntitySetIdMap; 
        // A mapping from entity types to the "single" entityset (in the query) that can
        // produce instances of that entity. If there are multiple entitysets of the
        // same type, or "free-floating" entity constructors in the query, then
        // the corresponding entry is null 
        private Dictionary m_entityTypeToEntitySetMap;
        private Dictionary m_discriminatorMaps; 
        private RelPropertyHelper m_relPropertyHelper; 
        private HashSet m_typesNeedingNullSentinel;
        #endregion 

        #region constructor

        private StructuredTypeInfo(HashSet typesNeedingNullSentinel) { 

            // Bug 428351: Make the type->typeInfo dictionary use ref equality for 
            // types. The problem is that records (and other transient types) can 
            // compare equal, even if they are not reference-equal, and this causes
            // us trouble down the road when we try to compare properties. 
            // Type unification is a good thing, but it needs to happen earlier somewhere
            m_typeInfoMap = new Dictionary(TypeUsageEqualityComparer.Instance);
            m_typeInfoMapPopulated = false;
            m_typesNeedingNullSentinel = typesNeedingNullSentinel; 
        }
 
        #endregion 

        #region Process driver 

        /// 
        /// Process Driver
        ///  
        /// 
        /// list of structured types referenced in the query 
        /// list of entitysets referenced in the query 
        /// list of entity types that have "free-floating" entity constructors
        /// information on optimized discriminator patterns for entity sets 
        /// helper for rel properties
        /// which types need a null sentinel
        /// 
        internal static void Process(Command itree, 
            List referencedTypes,
            List referencedEntitySets, 
            List freeFloatingEntityConstructorTypes, 
            Dictionary discriminatorMaps,
            RelPropertyHelper relPropertyHelper, 
            HashSet typesNeedingNullSentinel,
            out StructuredTypeInfo structuredTypeInfo) {
            structuredTypeInfo = new StructuredTypeInfo(typesNeedingNullSentinel);
            structuredTypeInfo.Process(itree, referencedTypes, referencedEntitySets, freeFloatingEntityConstructorTypes, discriminatorMaps, relPropertyHelper); 
        }
 
        ///  
        /// Fills the StructuredTypeInfo instance from the itree provided.
        ///  
        /// 
        /// list of referenced structured types
        /// list of referenced entitysets
        /// list of free-floating entityConstructor types 
        /// discriminator information for entity sets mapped using TPH pattern
        /// helper for rel properties 
        private void Process(Command itree, 
            List referencedTypes,
            List referencedEntitySets, 
            List freeFloatingEntityConstructorTypes,
            Dictionary discriminatorMaps,
            RelPropertyHelper relPropertyHelper) {
            PlanCompiler.Assert(null != itree, "null itree?"); 

            m_stringType = itree.StringType; 
            m_intType = itree.IntegerType; 
            m_relPropertyHelper = relPropertyHelper;
 
            ProcessEntitySets(referencedEntitySets, freeFloatingEntityConstructorTypes);
            ProcessDiscriminatorMaps(discriminatorMaps);
            ProcessTypes(referencedTypes);
        } 

        #endregion 
 
        #region "public" properties
 
        /// 
        /// Mapping from entitysetid-s to entitysets
        /// 
        internal md.EntitySet[] EntitySetIdToEntitySetMap { 
            get {
                return m_entitySetIdToEntitySetMap; 
            } 
        }
 
        #endregion

        #region "public" methods
        ///  
        /// Get a helper for rel properties
        ///  
        internal RelPropertyHelper RelPropertyHelper { 
            get { return m_relPropertyHelper; }
        } 
        /// 
        /// Gets the "single" entityset that stores instances of this type
        /// 
        ///  
        /// 
        internal md.EntitySet GetEntitySet(md.EntityTypeBase type) { 
            md.EntitySet set; 
            md.EntityTypeBase rootType = GetRootType(type);
            if (!m_entityTypeToEntitySetMap.TryGetValue(rootType, out set)) { 
                return null;
            }
            return set;
        } 

        ///  
        /// Get the entitysetid value for a given entityset 
        /// 
        /// the entityset 
        /// entitysetid value
        internal int GetEntitySetId(md.EntitySet e) {
            int result = 0;
 
            if (!m_entitySetToEntitySetIdMap.TryGetValue(e, out result)) {
                PlanCompiler.Assert(false, "no such entity set?"); 
            } 
            return result;
        } 

        /// 
        /// Gets entity sets referenced by the query.
        ///  
        /// entity sets
        internal Common.Utils.Set GetEntitySets() { 
            return new Common.Utils.Set(m_entitySetIdToEntitySetMap).MakeReadOnly(); 
        }
 
        /// 
        /// Find the TypeInfo entry for a type. For non-structured types, we always
        /// return null. For structured types, we return the entry in the typeInfoMap.
        /// If we don't find one, and the typeInfoMap has already been populated, then we 
        /// assert
        ///  
        /// the type to look up 
        /// the typeinfo for the type (null if we couldn't find one)
        internal TypeInfo GetTypeInfo(md.TypeUsage type) { 
            if (!TypeUtils.IsStructuredType(type)) {
                return null;
            }
            TypeInfo typeInfo = null; 
            if (!m_typeInfoMap.TryGetValue(type, out typeInfo)) {
                PlanCompiler.Assert(!TypeUtils.IsStructuredType(type) || !m_typeInfoMapPopulated, 
                    "cannot find typeInfo for type " + type); 
            }
            return typeInfo; 
        }

        #endregion
 
        #region private methods
 
        #region EntitySet processing methods 

        ///  
        /// Add a new entry to the entityTypeToSet map
        /// 
        /// entity type
        /// entityset producing this type 
        private void AddEntityTypeToSetEntry(md.EntityType entityType, md.EntitySet entitySet) {
            md.EntitySet other; 
            md.EntityTypeBase rootType = GetRootType(entityType); 
            bool hasSingleEntitySet = true;
 
            if (entitySet == null) {
                hasSingleEntitySet = false;
            }
            else if (m_entityTypeToEntitySetMap.TryGetValue(rootType, out other)) { 
                if (other != entitySet) {
                    hasSingleEntitySet = false; 
                } 
            }
 
            if (hasSingleEntitySet) {
                m_entityTypeToEntitySetMap[rootType] = entitySet;
            }
            else { 
                m_entityTypeToEntitySetMap[rootType] = null;
            } 
        } 

        ///  
        /// Handle any relevant processing for entity sets
        /// list of referenced entitysets
        /// list of free-floating entity constructor types
        ///  
        private void ProcessEntitySets(List referencedEntitySets, List freeFloatingEntityConstructorTypes) {
            AssignEntitySetIds(referencedEntitySets); 
 
            //
            // set up the entity-type to set map 
            //
            m_entityTypeToEntitySetMap = new Dictionary();
            foreach (md.EntitySet e in referencedEntitySets) {
                AddEntityTypeToSetEntry(e.ElementType, e); 
            }
            foreach (md.EntityType t in freeFloatingEntityConstructorTypes) { 
                AddEntityTypeToSetEntry(t, null); 
            }
        } 

        /// 
        /// Handle discriminator maps (determine which can safely be used in the query)
        ///  
        private void ProcessDiscriminatorMaps(Dictionary discriminatorMaps)
        { 
            // Only use custom type discrimination where a type has a single entity set. Where 
            // there are multiple sets, discriminator properties and flattened representations
            // may be incompatible. 
            Dictionary filteredMaps = null;
            if (null != discriminatorMaps)
            {
                filteredMaps = new Dictionary(discriminatorMaps.Count, discriminatorMaps.Comparer); 
                foreach (KeyValuePair setMapPair in discriminatorMaps)
                { 
                    md.EntitySetBase set = setMapPair.Key; 
                    ExplicitDiscriminatorMap map = setMapPair.Value.DiscriminatorMap;
                    if (null != map) { 
                        md.EntityTypeBase rootType = GetRootType(set.ElementType);
                        bool hasOneSet = GetEntitySet(rootType) != null;
                        if (hasOneSet)
                        { 
                            filteredMaps.Add(set, map);
                        } 
                    } 
                }
                if (filteredMaps.Count == 0) 
                {
                    // don't bother keeping the dictionary if it's empty
                    filteredMaps = null;
                } 
            }
            m_discriminatorMaps = filteredMaps; 
        } 

        ///  
        /// Assign ids to each entityset in the query
        /// list of referenced entitysets
        /// 
        private void AssignEntitySetIds(List referencedEntitySets) { 
            m_entitySetIdToEntitySetMap = new md.EntitySet[referencedEntitySets.Count];
            m_entitySetToEntitySetIdMap = new Dictionary(); 
 
            int id = 0;
            foreach (md.EntitySet e in referencedEntitySets) { 
                if (m_entitySetToEntitySetIdMap.ContainsKey(e)) {
                    continue;
                }
                m_entitySetIdToEntitySetMap[id] = e; 
                m_entitySetToEntitySetIdMap[e] = id;
                id++; 
            } 
        }
 
        #endregion

        #region Type processing methods
 
        /// 
        /// Process all types in the query 
        ///  
        private void ProcessTypes(List referencedTypes) {
            // Build up auxilliary information for each type 
            PopulateTypeInfoMap(referencedTypes);
            // Assign typeids to all nominal types
            AssignTypeIds();
            // Then "explode" all types 
            ExplodeTypes();
        } 
 
        #region Populating TypeInfo Map
 
        /// 
        /// Build up auxilliary information for each referenced type in the query
        /// 
        ///  
        private void PopulateTypeInfoMap(List referencedTypes) {
            foreach (md.TypeUsage t in referencedTypes) { 
                CreateTypeInfoForType(t); 
            }
            m_typeInfoMapPopulated = true; 
        }

        /// 
        /// Tries to lookup custom discriminator map for the given type (applies to EntitySets with 
        /// TPH discrimination pattern)
        ///  
        private bool TryGetDiscriminatorMap(md.EdmType type, out ExplicitDiscriminatorMap discriminatorMap) { 
            discriminatorMap = null;
 
            // check that there are actually discriminator maps available
            if (null == m_discriminatorMaps) {
                return false;
            } 

            // must be an entity type... 
            if (type.BuiltInTypeKind != md.BuiltInTypeKind.EntityType) { 
                return false;
            } 

            // get root entity type (discriminator maps are mapped from the root)
            md.EntityTypeBase rootEntityType = GetRootType((md.EntityType)type);
 
            // find entity set
            md.EntitySet entitySet; 
            if (!m_entityTypeToEntitySetMap.TryGetValue(rootEntityType, out entitySet)) { 
                return false;
            } 

            // free floating entity constructors are stored with a null EntitySet
            if (entitySet == null) {
                return false; 
            }
 
            // look for discriminator map 
            return m_discriminatorMaps.TryGetValue(entitySet, out discriminatorMap);
        } 

        /// 
        /// Create a TypeInfo (if necessary) for the type, and add it to the TypeInfo map
        ///  
        /// the type to process
        private void CreateTypeInfoForType(md.TypeUsage type) { 
            // 
            // peel off all collection wrappers
            // 
            while (TypeUtils.IsCollectionType(type)) {
                type = TypeHelpers.GetEdmType(type).TypeUsage;
            }
 
            // Only add "structured" types
            if (TypeUtils.IsStructuredType(type)) { 
                // check for discriminator map... 
                ExplicitDiscriminatorMap discriminatorMap;
                TryGetDiscriminatorMap(type.EdmType, out discriminatorMap); 

                CreateTypeInfoForStructuredType(type, discriminatorMap);
            }
        } 

        ///  
        /// Add a new entry to the map. If an entry already exists, then this function 
        /// simply returns the existing entry. Otherwise a new entry is created. If
        /// the type has a supertype, then we ensure that the supertype also exists in 
        /// the map, and we add our info to the supertype's list of subtypes
        /// 
        /// New type to add
        /// type discriminator map 
        /// The TypeInfo for this type
        private TypeInfo CreateTypeInfoForStructuredType(md.TypeUsage type, ExplicitDiscriminatorMap discriminatorMap) { 
            TypeInfo typeInfo; 

            PlanCompiler.Assert(TypeUtils.IsStructuredType(type), "expected structured type. Found " + type); 

            // Return existing entry, if one is available
            typeInfo = GetTypeInfo(type);
            if (typeInfo != null) { 
                return typeInfo;
            } 
 
            // Ensure that my supertype has been added to the map.
            TypeInfo superTypeInfo = null; 
            md.RefType refType;
            if (type.EdmType.BaseType != null) {

                superTypeInfo = CreateTypeInfoForStructuredType(md.TypeUsage.Create(type.EdmType.BaseType), discriminatorMap); 
            }
            // 
            // Handle Ref types also in a similar fashion 
            //
            else if (TypeHelpers.TryGetEdmType(type, out refType)) { 
                md.EntityType entityType = refType.ElementType as md.EntityType;
                if (entityType != null && entityType.BaseType != null) {
                    md.TypeUsage baseRefType = TypeHelpers.CreateReferenceTypeUsage(entityType.BaseType as md.EntityType);
                    superTypeInfo = CreateTypeInfoForStructuredType(baseRefType, discriminatorMap); 
                }
            } 
 
            //
            // Add the types of my properties to the TypeInfo map 
            //
            foreach (md.EdmMember m in TypeHelpers.GetDeclaredStructuralMembers(type)) {
                CreateTypeInfoForType(m.TypeUsage);
            } 

            // 
            // Get the types of the rel properties also 
            //
            { 
                md.EntityTypeBase entityType;
                if (TypeHelpers.TryGetEdmType(type, out entityType)) {
                    foreach (RelProperty p in m_relPropertyHelper.GetDeclaredOnlyRelProperties(entityType)) {
                        CreateTypeInfoForType(p.ToEnd.TypeUsage); 
                    }
                } 
            } 

 
            // Now add myself to the map
            typeInfo = TypeInfo.Create(type, superTypeInfo, discriminatorMap);
            m_typeInfoMap.Add(type, typeInfo);
 
            return typeInfo;
        } 
 
        #endregion
 
        #region Assigning TypeIds

        /// 
        /// Assigns typeids to each type in the map. 
        /// We walk the map looking only for "root" types, and call the function
        /// above to process root types. All other types will be handled in that 
        /// function 
        /// 
        private void AssignTypeIds() { 
            int typeNum = 0;

            foreach (KeyValuePair kv in m_typeInfoMap) {
                // See if there is a declared discriminator value for this column 
                if (kv.Value.RootType.DiscriminatorMap != null) {
                    // find discriminator value for type 
                    var entityType = (md.EntityType)kv.Key.EdmType; 
                    kv.Value.TypeId = kv.Value.RootType.DiscriminatorMap.GetTypeId(entityType);
                } 

                // Only handle root types. The call below will ensure that all the
                // subtypes are appropriately tagged
                else if (kv.Value.IsRootType && (md.TypeSemantics.IsEntityType(kv.Key) || md.TypeSemantics.IsComplexType(kv.Key))) { 
                    AssignRootTypeId(kv.Value, String.Format(CultureInfo.InvariantCulture, "{0}X", typeNum));
                    typeNum++; 
                } 
            }
        } 

        /// 
        /// Assign a typeid to a root type
        ///  
        /// 
        ///  
        private void AssignRootTypeId(TypeInfo typeInfo, string typeId) { 
            typeInfo.TypeId = typeId;
            AssignTypeIdsToSubTypes(typeInfo); 
        }

        /// 
        /// Assigns typeids to each subtype of the current type. 
        /// Assertion: the current type has already had a typeid assigned to it.
        ///  
        /// The current type 
        private void AssignTypeIdsToSubTypes(TypeInfo typeInfo) {
            // Now walk through all my subtypes, and assign their typeids 
            int mySubTypeNum = 0;
            foreach (TypeInfo subtype in typeInfo.ImmediateSubTypes) {
                AssignTypeId(subtype, mySubTypeNum);
                mySubTypeNum++; 
            }
        } 
 
        /// 
        /// Assign a typeid to a non-root type. 
        /// Assigns typeids to a non-root type based on a dewey encoding scheme.
        /// The typeid will be the typeId of the supertype suffixed by a
        /// local identifier for the type.
        ///  
        /// the non-root type
        /// position in the subtype list 
        private void AssignTypeId(TypeInfo typeInfo, int subtypeNum) { 
            typeInfo.TypeId = String.Format(CultureInfo.InvariantCulture, "{0}{1}X", typeInfo.SuperType.TypeId, subtypeNum);
            AssignTypeIdsToSubTypes(typeInfo); 
        }

        #endregion
 
        #region Flattening/Exploding types
        ///  
        /// A type needs a type-id property if it is an entity type or a complex tpe that 
        /// has subtypes.
        /// Coming soon: relax the "need subtype" requirement (ie) any entity/complex type will 
        /// have a typeid
        /// 
        /// 
        ///  
        private bool NeedsTypeIdProperty(TypeInfo typeInfo) {
            return typeInfo.ImmediateSubTypes.Count > 0 && !md.TypeSemantics.IsReferenceType(typeInfo.Type); 
        } 

        ///  
        /// A type needs a null-sentinel property if it is an row type that was projected
        /// at the top level of the query; we capture that information in the preprocessor
        /// and pass it in here.
        ///  
        /// 
        ///  
        private bool NeedsNullSentinelProperty(TypeInfo typeInfo) { 
            return m_typesNeedingNullSentinel.Contains(typeInfo.Type.EdmType.Identity);
        } 

        /// 
        /// The type needs an entitysetidproperty, if it is either an entity type
        /// or a reference type, AND we cannot determine that there is only entityset 
        /// in the query that could be producing instances of this entity
        ///  
        ///  
        /// 
        private bool NeedsEntitySetIdProperty(TypeInfo typeInfo) { 
            md.EntityType entityType;
            md.RefType refType = typeInfo.Type.EdmType as md.RefType;
            if (refType != null) {
                entityType = refType.ElementType as md.EntityType; 
            }
            else { 
                entityType = typeInfo.Type.EdmType as md.EntityType; 
            }
            bool result = ((entityType != null) && (GetEntitySet(entityType) == null)); 
            return result;
        }

        ///  
        /// "Explode" each type in the dictionary. (ie) for each type, get a flattened
        /// list of all its members (including special cases for the typeid) 
        ///  
        private void ExplodeTypes() {
            // Walk through the list of types, and only process the supertypes, since 
            // The ExplodeType method will ensure that all the subtypes are appropriately
            // tagged
            foreach (KeyValuePair kv in m_typeInfoMap) {
                if (kv.Value.IsRootType) { 
                    ExplodeType(kv.Value);
                } 
            } 
        }
 
        /// 
        /// "Explode" a type.  (ie) produce a flat record type with one property for each
        /// scalar property (top-level or nested) of the original type.
        /// Really deals with structured types, but also 
        /// peels off collection wrappers
        ///  
        /// the type to explode 
        /// the typeinfo for this type (with the explosion)
        private TypeInfo ExplodeType(md.TypeUsage type) { 
            if (TypeUtils.IsStructuredType(type)) {
                TypeInfo typeInfo = GetTypeInfo(type);
                ExplodeType(typeInfo);
                return typeInfo; 
            }
 
            if (TypeUtils.IsCollectionType(type)) { 
                md.TypeUsage elementType = TypeHelpers.GetEdmType(type).TypeUsage;
                ExplodeType(elementType); 
                return null;
            }
            return null;
        } 

        ///  
        /// Type Explosion - simply delegates to the root type 
        /// 
        /// type info 
        private void ExplodeType(TypeInfo typeInfo) {
            ExplodeRootStructuredType(typeInfo.RootType);
        }
 
        /// 
        /// "Explode" a root type. (ie) add each member of the type to a flat list of 
        /// members for the supertype. 
        ///
        /// Type explosion works in a DFS style model. We first walk through the 
        /// list of properties for the current type, and "flatten" out the properties
        /// that are themselves "structured". We then target each subtype (recursively)
        /// and perform the same kind of processing.
        /// 
        /// Consider a very simple case:
        /// 
        ///   Q = (z1 int, z2 date) 
        ///   Q2: Q = (z3 string)  -- Q2 is a subtype of Q
        ///   T = (a int, b Q, c date) 
        ///   S: T = (d int)  -- read as S is a subtype of T
        ///
        /// The result of flattening T (and S) will be
        /// 
        ///   (a int, b.z1 int, b.z2 date, b.z3 string, c date, d int)
        ///  
        /// the root type to explode 
        private void ExplodeRootStructuredType(RootTypeInfo rootType) {
            // Already done?? 
            if (rootType.FlattenedType != null) {
                return;
            }
 
            //
            // Special handling for root types. Add any special 
            // properties that are needed - TypeId, EntitySetId, etc 
            //
            if (NeedsTypeIdProperty(rootType)) { 
                rootType.AddPropertyRef(TypeIdPropertyRef.Instance);
                // check for discriminator map; if one exists, use custom discriminator member; otherwise, use default
                if (null != rootType.DiscriminatorMap) {
                    rootType.TypeIdKind = TypeIdKind.UserSpecified; 
                    rootType.TypeIdType = md.Helper.GetModelTypeUsage(rootType.DiscriminatorMap.DiscriminatorProperty);
                } 
                else { 
                    rootType.TypeIdKind = TypeIdKind.Generated;
                    rootType.TypeIdType = m_stringType; 
                }
            }
            if (NeedsEntitySetIdProperty(rootType)) {
                rootType.AddPropertyRef(EntitySetIdPropertyRef.Instance); 
            }
            if (NeedsNullSentinelProperty(rootType)) { 
                rootType.AddPropertyRef(NullSentinelPropertyRef.Instance); 
            }
 
            //
            // Then add members from each type in the hierarchy (including
            // the root type)
            // 
            ExplodeRootStructuredTypeHelper(rootType);
 
            // 
            // For entity types, add all the rel-properties now. Note that rel-properties
            // are added after the regular properties of all subtypes 
            //
            if (md.TypeSemantics.IsEntityType(rootType.Type)) {
                AddRelProperties(rootType);
            } 

            // 
            // We've now gotten all the relevant properties 
            // Now let's create a new record type
            // 
            CreateFlattenedRecordType(rootType);
        }

        ///  
        /// Helper for ExplodeType.
        /// Walks through each member introduced by the current type, and 
        /// adds it onto the "flat" record type being constructed. 
        /// We then walk through all subtypes of this type, and process those as
        /// well. 
        /// Special handling for Refs: we only add the keys; there is no
        /// need to handle subtypes (since they won't be introducing anything
        /// different)
        ///  
        /// type in the type hierarchy
        private void ExplodeRootStructuredTypeHelper(TypeInfo typeInfo) { 
            RootTypeInfo rootType = typeInfo.RootType; 

            // Identify the members of this type. For Refs, use the key properties 
            // of the target entity type. For all other types, simply use the type
            // members
            IEnumerable typeMembers = null;
            md.RefType refType; 
            if (TypeHelpers.TryGetEdmType(typeInfo.Type, out refType)) {
                // 
                // If this is not the root type, then don't bother adding the keys. 
                // the root type has already done this
                // 
                if (!typeInfo.IsRootType) {
                    return;
                }
                typeMembers = refType.ElementType.KeyMembers; 
            }
            else { 
                typeMembers = TypeHelpers.GetDeclaredStructuralMembers(typeInfo.Type); 
            }
 
            // Walk through all the members of the type
            foreach (md.EdmMember p in typeMembers) {
                TypeInfo propertyType = ExplodeType(p.TypeUsage);
 
                //
                // If we can't find a TypeInfo for this property's type, then it must 
                // be a scalar type or a collection type. In either case, we'll 
                // build up a SimplePropertyRef
                // 
                if (propertyType == null) {
                    rootType.AddPropertyRef(new SimplePropertyRef(p));
                }
                else { 
                    //
                    // We're dealing with a structured type again. Create NestedPropertyRef 
                    // for each property of the nested type 
                    //
                    foreach (PropertyRef nestedPropInfo in propertyType.PropertyRefList) { 
                        rootType.AddPropertyRef(nestedPropInfo.CreateNestedPropertyRef(p));
                    }
                }
            } 

            // 
            // Process all subtypes now 
            //
            foreach (TypeInfo subTypeInfo in typeInfo.ImmediateSubTypes) { 
                ExplodeRootStructuredTypeHelper(subTypeInfo);
            }
        }
 
        /// 
        /// Add the list of rel-properties for this type 
        ///  
        /// the type to process
        private void AddRelProperties(TypeInfo typeInfo) { 

            md.EntityTypeBase entityType = (md.EntityTypeBase)typeInfo.Type.EdmType;

            // 
            // Walk through each rel-property defined for this specific type,
            // and add a corresponding property-ref 
            // 
            foreach (RelProperty p in m_relPropertyHelper.GetDeclaredOnlyRelProperties(entityType)) {
                md.EdmType refType = p.ToEnd.TypeUsage.EdmType; 
                TypeInfo refTypeInfo = GetTypeInfo(p.ToEnd.TypeUsage);

                //
                // We're dealing with a structured type again - flatten this out 
                // as well
                // 
                ExplodeType(refTypeInfo); 

                foreach (PropertyRef nestedPropInfo in refTypeInfo.PropertyRefList) { 
                    typeInfo.RootType.AddPropertyRef(nestedPropInfo.CreateNestedPropertyRef(p));
                }
            }
 
            //
            // Process all subtypes now 
            // 
            foreach (TypeInfo subTypeInfo in typeInfo.ImmediateSubTypes) {
                AddRelProperties(subTypeInfo); 
            }
        }

        ///  
        /// Create the flattened record type for the type.
        /// Walk through the list of property refs, and creates a new field 
        /// (which we name as "F1", "F2" etc.) with the required property type. 
        ///
        /// We then produce a mapping from the original property (propertyRef really) 
        /// to the new property for use in later modules.
        ///
        /// Finally, we identify the TypeId and EntitySetId property if they exist
        ///  
        /// 
        private void CreateFlattenedRecordType(RootTypeInfo type) { 
            // 
            // If this type corresponds to an entity type, and that entity type
            // has no subtypes, and that that entity type has no complex properties 
            // then simply use the name from that property
            //
            bool usePropertyNamesFromUnderlyingType;
            if (md.TypeSemantics.IsEntityType(type.Type) && 
                type.ImmediateSubTypes.Count == 0) {
                usePropertyNamesFromUnderlyingType = true; 
            } 
            else {
                usePropertyNamesFromUnderlyingType = false; 
            }


            // Build the record type 
            List> fieldList = new List>();
            int fieldId = 0; 
            foreach (PropertyRef p in type.PropertyRefList) { 
                string fieldName = null;
                if (usePropertyNamesFromUnderlyingType) { 
                    SimplePropertyRef simpleP = p as SimplePropertyRef;
                    if (simpleP != null) {
                        fieldName = simpleP.Property.Name;
                    } 
                }
                md.TypeUsage propertyType = GetPropertyType(type, p); 
                if (fieldName == null) { 
                    //
                    // Deal with collisions? 
                    //
                    fieldName = "F" + fieldId.ToString(CultureInfo.InvariantCulture);
                }
                fieldList.Add(new KeyValuePair(fieldName, propertyType)); 
                fieldId++;
            } 
 
            type.FlattenedType = TypeHelpers.CreateRowType(fieldList);
 
            // Now build up the property map
            IEnumerator origProps = type.PropertyRefList.GetEnumerator();
            foreach (md.EdmProperty p in type.FlattenedType.Properties) {
                if (!origProps.MoveNext()) { 
                    PlanCompiler.Assert(false, "property refs count and flattened type member count mismatch?");
                } 
                type.AddPropertyMapping(origProps.Current, p); 
            }
        } 

        /// 
        /// Get the "new" type corresponding to the input type. For structured types,
        /// we return the flattened record type. 
        /// For collections of structured type, we return a new collection type of the corresponding flattened
        /// type. 
        /// For everything else, we return the input type 
        /// 
        /// the original type 
        /// the new type (if any)
        private md.TypeUsage GetNewType(md.TypeUsage type) {
            if (TypeUtils.IsStructuredType(type)) {
                TypeInfo typeInfo = GetTypeInfo(type); 
                return typeInfo.FlattenedTypeUsage;
            } 
            md.TypeUsage elementType; 
            if (TypeHelpers.TryGetCollectionElementType(type, out elementType)) {
                md.TypeUsage newElementType = GetNewType(elementType); 
                if (newElementType.EdmEquals(elementType)) {
                    return type;
                }
                else { 
                    return TypeHelpers.CreateCollectionTypeUsage(newElementType);
                } 
 
            }
 
            // simple scalar
            return type;
        }
 
        /// 
        /// Get the datatype for a propertyRef. The only concrete classes that we 
        /// handle are TypeIdPropertyRef, and BasicPropertyRef. 
        /// AllPropertyRef is illegal here.
        /// For BasicPropertyRef, we simply pick up the type from the corresponding 
        /// property. For TypeIdPropertyRef, we use "string" as the default type
        /// or the discriminator property type where one is available.
        /// 
        /// typeinfo of the current type 
        /// current property ref
        /// the datatype of the property 
        private md.TypeUsage GetPropertyType(RootTypeInfo typeInfo, PropertyRef p) { 
            md.TypeUsage result = null;
 
            PropertyRef innerProperty = null;
            // Get the "leaf" property first
            while (p is NestedPropertyRef) {
                NestedPropertyRef npr = (NestedPropertyRef)p; 
                p = npr.OuterProperty;
                innerProperty = npr.InnerProperty; 
            } 

            if (p is TypeIdPropertyRef) { 
                //
                // Get to the innermost type that specifies this typeid (the entity type),
                // get the datatype for the typeid column from that type
                // 
                if (innerProperty != null && innerProperty is SimplePropertyRef) {
                    md.TypeUsage innerType = ((SimplePropertyRef)innerProperty).Property.TypeUsage; 
                    TypeInfo innerTypeInfo = GetTypeInfo(innerType); 
                    result = innerTypeInfo.RootType.TypeIdType;
                } 
                else {
                    result = typeInfo.TypeIdType;
                }
            } 
            else if (p is EntitySetIdPropertyRef || p is NullSentinelPropertyRef) {
                result = m_intType; 
            } 
            else if (p is RelPropertyRef) {
                result = (p as RelPropertyRef).Property.ToEnd.TypeUsage; 
            }
            else {
                SimplePropertyRef simpleP = p as SimplePropertyRef;
                if (simpleP != null) { 
                    result = md.Helper.GetModelTypeUsage(simpleP.Property);
                } 
            } 

            result = GetNewType(result); 
            PlanCompiler.Assert(null != result, "unrecognized property type?");
            return result;
        }
 
        #endregion
 
        #endregion 

        #region utils 
        /// 
        /// Get the root entity type for a type
        /// 
        /// entity type 
        /// 
        private static md.EntityTypeBase GetRootType(md.EntityTypeBase type) { 
            while (type.BaseType != null) { 
                type = (md.EntityTypeBase)type.BaseType;
            } 
            return type;
        }
        #endregion
        #endregion 
    }
} 

// File provided for Reference Use Only by Microsoft Corporation (c) 2007.
//---------------------------------------------------------------------- 
// 
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// 
// 
// @owner  [....], [....]
//--------------------------------------------------------------------- 
 
using System;
using System.Collections; 
using System.Collections.Generic;
//using System.Diagnostics; // Please use PlanCompiler.Assert instead of Debug.Assert in this class...
using System.Globalization;
using System.Linq; 
using System.Data.Common;
using md = System.Data.Metadata.Edm; 
using System.Data.Query.InternalTrees; 

namespace System.Data.Query.PlanCompiler { 

    /// 
    /// The type flattener module is part of the structured type elimination phase,
    /// and is largely responsible for "flattening" record and nominal types into 
    /// flat record types. Additionally, for nominal types, this module produces typeid
    /// values that can be used later to interpret the input data stream. 
    /// 
    /// The goal of this module is to load up information about type and entityset metadata
    /// used in the ITree. This module is part of the "StructuredTypeElimination" phase, 
    /// and provides information to help in this process.
    ///
    /// This module itself is broken down into multiple parts.
    /// 
    /// (*) Loading type information: We walk the query tree to identify all references
    ///     to structured types and entity sets 
    /// 
    /// (*) Processing entitysets: We walk the list of entitysets, and assign ids to each
    ///     entityset. We also create a map of id->entityset metadata in this phase. 
    ///
    /// (*) Processing types: We then walk the list of types, and process each type. This,
    ///     in turn, is also broken into multiple parts:
    /// 
    ///     * Populating the Type Map: we walk the list of reference types and add each of
    ///       them to our typeMap, along with their base types. 
    /// 
    ///     * TypeId assignment: We assign typeids to each nominal (complextype/entitytype).
    ///       This typeid is based on a dewey encoding. The typeid of a type is typically 
    ///       the typeid of its supertype suffixed by the subtype number of this type within
    ///       its supertype. This encoding is intended to support easy type matching
    ///       later on in the query - both for exact (IS OF ONLY) and inexact (IS OF) matches.
    /// 
    ///     * Type flattening: We then "explode"/"flatten" each structured type - refs,
    ///       entity types, complex types and record types. The result is a flattened type 
    ///       where every single property of the resulting type is a primitive/scalar type 
    ///       (Note: UDTs are considered to be scalar types). Additional information may also
    ///       be encoded as a type property. For example, a typeid property is added (if 
    ///       necessary) to complex/entity types to help discriminate polymorphic instances.
    ///       An EntitySetId property is added to ref and entity type attributes to help
    ///       determine the entity set that a given entity instance comes from.
    ///       As part of type flattening, we keep track of additional information that allows 
    ///       us to map easily from the original property to the properties in the new type
    /// 
    /// The final result of this processing is an object that contains: 
    ///
    ///  * a TypeInfo (extra type information) for each structured type in the query 
    ///  * a map from typeid value to type. To be used later by result assembly
    ///  * a map between entitysetid value and entityset. To be used later by result assembly
    ///
    /// NOTE: StructuredTypeInfo is probably not the best name for this class, since 
    ///       it doesn't derive from TypeInfo but rather manages a collection of them.
    ///       I don't have a better name, but if you come up with one change this. 
    /// 
    /// 
    internal class StructuredTypeInfo { 

        #region private state

        private md.TypeUsage m_stringType; 
        private md.TypeUsage m_intType;
        private Dictionary m_typeInfoMap; 
        private bool m_typeInfoMapPopulated; 
        private md.EntitySet[] m_entitySetIdToEntitySetMap; //used as a Dictionary with the index as key
        private Dictionary m_entitySetToEntitySetIdMap; 
        // A mapping from entity types to the "single" entityset (in the query) that can
        // produce instances of that entity. If there are multiple entitysets of the
        // same type, or "free-floating" entity constructors in the query, then
        // the corresponding entry is null 
        private Dictionary m_entityTypeToEntitySetMap;
        private Dictionary m_discriminatorMaps; 
        private RelPropertyHelper m_relPropertyHelper; 
        private HashSet m_typesNeedingNullSentinel;
        #endregion 

        #region constructor

        private StructuredTypeInfo(HashSet typesNeedingNullSentinel) { 

            // Bug 428351: Make the type->typeInfo dictionary use ref equality for 
            // types. The problem is that records (and other transient types) can 
            // compare equal, even if they are not reference-equal, and this causes
            // us trouble down the road when we try to compare properties. 
            // Type unification is a good thing, but it needs to happen earlier somewhere
            m_typeInfoMap = new Dictionary(TypeUsageEqualityComparer.Instance);
            m_typeInfoMapPopulated = false;
            m_typesNeedingNullSentinel = typesNeedingNullSentinel; 
        }
 
        #endregion 

        #region Process driver 

        /// 
        /// Process Driver
        ///  
        /// 
        /// list of structured types referenced in the query 
        /// list of entitysets referenced in the query 
        /// list of entity types that have "free-floating" entity constructors
        /// information on optimized discriminator patterns for entity sets 
        /// helper for rel properties
        /// which types need a null sentinel
        /// 
        internal static void Process(Command itree, 
            List referencedTypes,
            List referencedEntitySets, 
            List freeFloatingEntityConstructorTypes, 
            Dictionary discriminatorMaps,
            RelPropertyHelper relPropertyHelper, 
            HashSet typesNeedingNullSentinel,
            out StructuredTypeInfo structuredTypeInfo) {
            structuredTypeInfo = new StructuredTypeInfo(typesNeedingNullSentinel);
            structuredTypeInfo.Process(itree, referencedTypes, referencedEntitySets, freeFloatingEntityConstructorTypes, discriminatorMaps, relPropertyHelper); 
        }
 
        ///  
        /// Fills the StructuredTypeInfo instance from the itree provided.
        ///  
        /// 
        /// list of referenced structured types
        /// list of referenced entitysets
        /// list of free-floating entityConstructor types 
        /// discriminator information for entity sets mapped using TPH pattern
        /// helper for rel properties 
        private void Process(Command itree, 
            List referencedTypes,
            List referencedEntitySets, 
            List freeFloatingEntityConstructorTypes,
            Dictionary discriminatorMaps,
            RelPropertyHelper relPropertyHelper) {
            PlanCompiler.Assert(null != itree, "null itree?"); 

            m_stringType = itree.StringType; 
            m_intType = itree.IntegerType; 
            m_relPropertyHelper = relPropertyHelper;
 
            ProcessEntitySets(referencedEntitySets, freeFloatingEntityConstructorTypes);
            ProcessDiscriminatorMaps(discriminatorMaps);
            ProcessTypes(referencedTypes);
        } 

        #endregion 
 
        #region "public" properties
 
        /// 
        /// Mapping from entitysetid-s to entitysets
        /// 
        internal md.EntitySet[] EntitySetIdToEntitySetMap { 
            get {
                return m_entitySetIdToEntitySetMap; 
            } 
        }
 
        #endregion

        #region "public" methods
        ///  
        /// Get a helper for rel properties
        ///  
        internal RelPropertyHelper RelPropertyHelper { 
            get { return m_relPropertyHelper; }
        } 
        /// 
        /// Gets the "single" entityset that stores instances of this type
        /// 
        ///  
        /// 
        internal md.EntitySet GetEntitySet(md.EntityTypeBase type) { 
            md.EntitySet set; 
            md.EntityTypeBase rootType = GetRootType(type);
            if (!m_entityTypeToEntitySetMap.TryGetValue(rootType, out set)) { 
                return null;
            }
            return set;
        } 

        ///  
        /// Get the entitysetid value for a given entityset 
        /// 
        /// the entityset 
        /// entitysetid value
        internal int GetEntitySetId(md.EntitySet e) {
            int result = 0;
 
            if (!m_entitySetToEntitySetIdMap.TryGetValue(e, out result)) {
                PlanCompiler.Assert(false, "no such entity set?"); 
            } 
            return result;
        } 

        /// 
        /// Gets entity sets referenced by the query.
        ///  
        /// entity sets
        internal Common.Utils.Set GetEntitySets() { 
            return new Common.Utils.Set(m_entitySetIdToEntitySetMap).MakeReadOnly(); 
        }
 
        /// 
        /// Find the TypeInfo entry for a type. For non-structured types, we always
        /// return null. For structured types, we return the entry in the typeInfoMap.
        /// If we don't find one, and the typeInfoMap has already been populated, then we 
        /// assert
        ///  
        /// the type to look up 
        /// the typeinfo for the type (null if we couldn't find one)
        internal TypeInfo GetTypeInfo(md.TypeUsage type) { 
            if (!TypeUtils.IsStructuredType(type)) {
                return null;
            }
            TypeInfo typeInfo = null; 
            if (!m_typeInfoMap.TryGetValue(type, out typeInfo)) {
                PlanCompiler.Assert(!TypeUtils.IsStructuredType(type) || !m_typeInfoMapPopulated, 
                    "cannot find typeInfo for type " + type); 
            }
            return typeInfo; 
        }

        #endregion
 
        #region private methods
 
        #region EntitySet processing methods 

        ///  
        /// Add a new entry to the entityTypeToSet map
        /// 
        /// entity type
        /// entityset producing this type 
        private void AddEntityTypeToSetEntry(md.EntityType entityType, md.EntitySet entitySet) {
            md.EntitySet other; 
            md.EntityTypeBase rootType = GetRootType(entityType); 
            bool hasSingleEntitySet = true;
 
            if (entitySet == null) {
                hasSingleEntitySet = false;
            }
            else if (m_entityTypeToEntitySetMap.TryGetValue(rootType, out other)) { 
                if (other != entitySet) {
                    hasSingleEntitySet = false; 
                } 
            }
 
            if (hasSingleEntitySet) {
                m_entityTypeToEntitySetMap[rootType] = entitySet;
            }
            else { 
                m_entityTypeToEntitySetMap[rootType] = null;
            } 
        } 

        ///  
        /// Handle any relevant processing for entity sets
        /// list of referenced entitysets
        /// list of free-floating entity constructor types
        ///  
        private void ProcessEntitySets(List referencedEntitySets, List freeFloatingEntityConstructorTypes) {
            AssignEntitySetIds(referencedEntitySets); 
 
            //
            // set up the entity-type to set map 
            //
            m_entityTypeToEntitySetMap = new Dictionary();
            foreach (md.EntitySet e in referencedEntitySets) {
                AddEntityTypeToSetEntry(e.ElementType, e); 
            }
            foreach (md.EntityType t in freeFloatingEntityConstructorTypes) { 
                AddEntityTypeToSetEntry(t, null); 
            }
        } 

        /// 
        /// Handle discriminator maps (determine which can safely be used in the query)
        ///  
        private void ProcessDiscriminatorMaps(Dictionary discriminatorMaps)
        { 
            // Only use custom type discrimination where a type has a single entity set. Where 
            // there are multiple sets, discriminator properties and flattened representations
            // may be incompatible. 
            Dictionary filteredMaps = null;
            if (null != discriminatorMaps)
            {
                filteredMaps = new Dictionary(discriminatorMaps.Count, discriminatorMaps.Comparer); 
                foreach (KeyValuePair setMapPair in discriminatorMaps)
                { 
                    md.EntitySetBase set = setMapPair.Key; 
                    ExplicitDiscriminatorMap map = setMapPair.Value.DiscriminatorMap;
                    if (null != map) { 
                        md.EntityTypeBase rootType = GetRootType(set.ElementType);
                        bool hasOneSet = GetEntitySet(rootType) != null;
                        if (hasOneSet)
                        { 
                            filteredMaps.Add(set, map);
                        } 
                    } 
                }
                if (filteredMaps.Count == 0) 
                {
                    // don't bother keeping the dictionary if it's empty
                    filteredMaps = null;
                } 
            }
            m_discriminatorMaps = filteredMaps; 
        } 

        ///  
        /// Assign ids to each entityset in the query
        /// list of referenced entitysets
        /// 
        private void AssignEntitySetIds(List referencedEntitySets) { 
            m_entitySetIdToEntitySetMap = new md.EntitySet[referencedEntitySets.Count];
            m_entitySetToEntitySetIdMap = new Dictionary(); 
 
            int id = 0;
            foreach (md.EntitySet e in referencedEntitySets) { 
                if (m_entitySetToEntitySetIdMap.ContainsKey(e)) {
                    continue;
                }
                m_entitySetIdToEntitySetMap[id] = e; 
                m_entitySetToEntitySetIdMap[e] = id;
                id++; 
            } 
        }
 
        #endregion

        #region Type processing methods
 
        /// 
        /// Process all types in the query 
        ///  
        private void ProcessTypes(List referencedTypes) {
            // Build up auxilliary information for each type 
            PopulateTypeInfoMap(referencedTypes);
            // Assign typeids to all nominal types
            AssignTypeIds();
            // Then "explode" all types 
            ExplodeTypes();
        } 
 
        #region Populating TypeInfo Map
 
        /// 
        /// Build up auxilliary information for each referenced type in the query
        /// 
        ///  
        private void PopulateTypeInfoMap(List referencedTypes) {
            foreach (md.TypeUsage t in referencedTypes) { 
                CreateTypeInfoForType(t); 
            }
            m_typeInfoMapPopulated = true; 
        }

        /// 
        /// Tries to lookup custom discriminator map for the given type (applies to EntitySets with 
        /// TPH discrimination pattern)
        ///  
        private bool TryGetDiscriminatorMap(md.EdmType type, out ExplicitDiscriminatorMap discriminatorMap) { 
            discriminatorMap = null;
 
            // check that there are actually discriminator maps available
            if (null == m_discriminatorMaps) {
                return false;
            } 

            // must be an entity type... 
            if (type.BuiltInTypeKind != md.BuiltInTypeKind.EntityType) { 
                return false;
            } 

            // get root entity type (discriminator maps are mapped from the root)
            md.EntityTypeBase rootEntityType = GetRootType((md.EntityType)type);
 
            // find entity set
            md.EntitySet entitySet; 
            if (!m_entityTypeToEntitySetMap.TryGetValue(rootEntityType, out entitySet)) { 
                return false;
            } 

            // free floating entity constructors are stored with a null EntitySet
            if (entitySet == null) {
                return false; 
            }
 
            // look for discriminator map 
            return m_discriminatorMaps.TryGetValue(entitySet, out discriminatorMap);
        } 

        /// 
        /// Create a TypeInfo (if necessary) for the type, and add it to the TypeInfo map
        ///  
        /// the type to process
        private void CreateTypeInfoForType(md.TypeUsage type) { 
            // 
            // peel off all collection wrappers
            // 
            while (TypeUtils.IsCollectionType(type)) {
                type = TypeHelpers.GetEdmType(type).TypeUsage;
            }
 
            // Only add "structured" types
            if (TypeUtils.IsStructuredType(type)) { 
                // check for discriminator map... 
                ExplicitDiscriminatorMap discriminatorMap;
                TryGetDiscriminatorMap(type.EdmType, out discriminatorMap); 

                CreateTypeInfoForStructuredType(type, discriminatorMap);
            }
        } 

        ///  
        /// Add a new entry to the map. If an entry already exists, then this function 
        /// simply returns the existing entry. Otherwise a new entry is created. If
        /// the type has a supertype, then we ensure that the supertype also exists in 
        /// the map, and we add our info to the supertype's list of subtypes
        /// 
        /// New type to add
        /// type discriminator map 
        /// The TypeInfo for this type
        private TypeInfo CreateTypeInfoForStructuredType(md.TypeUsage type, ExplicitDiscriminatorMap discriminatorMap) { 
            TypeInfo typeInfo; 

            PlanCompiler.Assert(TypeUtils.IsStructuredType(type), "expected structured type. Found " + type); 

            // Return existing entry, if one is available
            typeInfo = GetTypeInfo(type);
            if (typeInfo != null) { 
                return typeInfo;
            } 
 
            // Ensure that my supertype has been added to the map.
            TypeInfo superTypeInfo = null; 
            md.RefType refType;
            if (type.EdmType.BaseType != null) {

                superTypeInfo = CreateTypeInfoForStructuredType(md.TypeUsage.Create(type.EdmType.BaseType), discriminatorMap); 
            }
            // 
            // Handle Ref types also in a similar fashion 
            //
            else if (TypeHelpers.TryGetEdmType(type, out refType)) { 
                md.EntityType entityType = refType.ElementType as md.EntityType;
                if (entityType != null && entityType.BaseType != null) {
                    md.TypeUsage baseRefType = TypeHelpers.CreateReferenceTypeUsage(entityType.BaseType as md.EntityType);
                    superTypeInfo = CreateTypeInfoForStructuredType(baseRefType, discriminatorMap); 
                }
            } 
 
            //
            // Add the types of my properties to the TypeInfo map 
            //
            foreach (md.EdmMember m in TypeHelpers.GetDeclaredStructuralMembers(type)) {
                CreateTypeInfoForType(m.TypeUsage);
            } 

            // 
            // Get the types of the rel properties also 
            //
            { 
                md.EntityTypeBase entityType;
                if (TypeHelpers.TryGetEdmType(type, out entityType)) {
                    foreach (RelProperty p in m_relPropertyHelper.GetDeclaredOnlyRelProperties(entityType)) {
                        CreateTypeInfoForType(p.ToEnd.TypeUsage); 
                    }
                } 
            } 

 
            // Now add myself to the map
            typeInfo = TypeInfo.Create(type, superTypeInfo, discriminatorMap);
            m_typeInfoMap.Add(type, typeInfo);
 
            return typeInfo;
        } 
 
        #endregion
 
        #region Assigning TypeIds

        /// 
        /// Assigns typeids to each type in the map. 
        /// We walk the map looking only for "root" types, and call the function
        /// above to process root types. All other types will be handled in that 
        /// function 
        /// 
        private void AssignTypeIds() { 
            int typeNum = 0;

            foreach (KeyValuePair kv in m_typeInfoMap) {
                // See if there is a declared discriminator value for this column 
                if (kv.Value.RootType.DiscriminatorMap != null) {
                    // find discriminator value for type 
                    var entityType = (md.EntityType)kv.Key.EdmType; 
                    kv.Value.TypeId = kv.Value.RootType.DiscriminatorMap.GetTypeId(entityType);
                } 

                // Only handle root types. The call below will ensure that all the
                // subtypes are appropriately tagged
                else if (kv.Value.IsRootType && (md.TypeSemantics.IsEntityType(kv.Key) || md.TypeSemantics.IsComplexType(kv.Key))) { 
                    AssignRootTypeId(kv.Value, String.Format(CultureInfo.InvariantCulture, "{0}X", typeNum));
                    typeNum++; 
                } 
            }
        } 

        /// 
        /// Assign a typeid to a root type
        ///  
        /// 
        ///  
        private void AssignRootTypeId(TypeInfo typeInfo, string typeId) { 
            typeInfo.TypeId = typeId;
            AssignTypeIdsToSubTypes(typeInfo); 
        }

        /// 
        /// Assigns typeids to each subtype of the current type. 
        /// Assertion: the current type has already had a typeid assigned to it.
        ///  
        /// The current type 
        private void AssignTypeIdsToSubTypes(TypeInfo typeInfo) {
            // Now walk through all my subtypes, and assign their typeids 
            int mySubTypeNum = 0;
            foreach (TypeInfo subtype in typeInfo.ImmediateSubTypes) {
                AssignTypeId(subtype, mySubTypeNum);
                mySubTypeNum++; 
            }
        } 
 
        /// 
        /// Assign a typeid to a non-root type. 
        /// Assigns typeids to a non-root type based on a dewey encoding scheme.
        /// The typeid will be the typeId of the supertype suffixed by a
        /// local identifier for the type.
        ///  
        /// the non-root type
        /// position in the subtype list 
        private void AssignTypeId(TypeInfo typeInfo, int subtypeNum) { 
            typeInfo.TypeId = String.Format(CultureInfo.InvariantCulture, "{0}{1}X", typeInfo.SuperType.TypeId, subtypeNum);
            AssignTypeIdsToSubTypes(typeInfo); 
        }

        #endregion
 
        #region Flattening/Exploding types
        ///  
        /// A type needs a type-id property if it is an entity type or a complex tpe that 
        /// has subtypes.
        /// Coming soon: relax the "need subtype" requirement (ie) any entity/complex type will 
        /// have a typeid
        /// 
        /// 
        ///  
        private bool NeedsTypeIdProperty(TypeInfo typeInfo) {
            return typeInfo.ImmediateSubTypes.Count > 0 && !md.TypeSemantics.IsReferenceType(typeInfo.Type); 
        } 

        ///  
        /// A type needs a null-sentinel property if it is an row type that was projected
        /// at the top level of the query; we capture that information in the preprocessor
        /// and pass it in here.
        ///  
        /// 
        ///  
        private bool NeedsNullSentinelProperty(TypeInfo typeInfo) { 
            return m_typesNeedingNullSentinel.Contains(typeInfo.Type.EdmType.Identity);
        } 

        /// 
        /// The type needs an entitysetidproperty, if it is either an entity type
        /// or a reference type, AND we cannot determine that there is only entityset 
        /// in the query that could be producing instances of this entity
        ///  
        ///  
        /// 
        private bool NeedsEntitySetIdProperty(TypeInfo typeInfo) { 
            md.EntityType entityType;
            md.RefType refType = typeInfo.Type.EdmType as md.RefType;
            if (refType != null) {
                entityType = refType.ElementType as md.EntityType; 
            }
            else { 
                entityType = typeInfo.Type.EdmType as md.EntityType; 
            }
            bool result = ((entityType != null) && (GetEntitySet(entityType) == null)); 
            return result;
        }

        ///  
        /// "Explode" each type in the dictionary. (ie) for each type, get a flattened
        /// list of all its members (including special cases for the typeid) 
        ///  
        private void ExplodeTypes() {
            // Walk through the list of types, and only process the supertypes, since 
            // The ExplodeType method will ensure that all the subtypes are appropriately
            // tagged
            foreach (KeyValuePair kv in m_typeInfoMap) {
                if (kv.Value.IsRootType) { 
                    ExplodeType(kv.Value);
                } 
            } 
        }
 
        /// 
        /// "Explode" a type.  (ie) produce a flat record type with one property for each
        /// scalar property (top-level or nested) of the original type.
        /// Really deals with structured types, but also 
        /// peels off collection wrappers
        ///  
        /// the type to explode 
        /// the typeinfo for this type (with the explosion)
        private TypeInfo ExplodeType(md.TypeUsage type) { 
            if (TypeUtils.IsStructuredType(type)) {
                TypeInfo typeInfo = GetTypeInfo(type);
                ExplodeType(typeInfo);
                return typeInfo; 
            }
 
            if (TypeUtils.IsCollectionType(type)) { 
                md.TypeUsage elementType = TypeHelpers.GetEdmType(type).TypeUsage;
                ExplodeType(elementType); 
                return null;
            }
            return null;
        } 

        ///  
        /// Type Explosion - simply delegates to the root type 
        /// 
        /// type info 
        private void ExplodeType(TypeInfo typeInfo) {
            ExplodeRootStructuredType(typeInfo.RootType);
        }
 
        /// 
        /// "Explode" a root type. (ie) add each member of the type to a flat list of 
        /// members for the supertype. 
        ///
        /// Type explosion works in a DFS style model. We first walk through the 
        /// list of properties for the current type, and "flatten" out the properties
        /// that are themselves "structured". We then target each subtype (recursively)
        /// and perform the same kind of processing.
        /// 
        /// Consider a very simple case:
        /// 
        ///   Q = (z1 int, z2 date) 
        ///   Q2: Q = (z3 string)  -- Q2 is a subtype of Q
        ///   T = (a int, b Q, c date) 
        ///   S: T = (d int)  -- read as S is a subtype of T
        ///
        /// The result of flattening T (and S) will be
        /// 
        ///   (a int, b.z1 int, b.z2 date, b.z3 string, c date, d int)
        ///  
        /// the root type to explode 
        private void ExplodeRootStructuredType(RootTypeInfo rootType) {
            // Already done?? 
            if (rootType.FlattenedType != null) {
                return;
            }
 
            //
            // Special handling for root types. Add any special 
            // properties that are needed - TypeId, EntitySetId, etc 
            //
            if (NeedsTypeIdProperty(rootType)) { 
                rootType.AddPropertyRef(TypeIdPropertyRef.Instance);
                // check for discriminator map; if one exists, use custom discriminator member; otherwise, use default
                if (null != rootType.DiscriminatorMap) {
                    rootType.TypeIdKind = TypeIdKind.UserSpecified; 
                    rootType.TypeIdType = md.Helper.GetModelTypeUsage(rootType.DiscriminatorMap.DiscriminatorProperty);
                } 
                else { 
                    rootType.TypeIdKind = TypeIdKind.Generated;
                    rootType.TypeIdType = m_stringType; 
                }
            }
            if (NeedsEntitySetIdProperty(rootType)) {
                rootType.AddPropertyRef(EntitySetIdPropertyRef.Instance); 
            }
            if (NeedsNullSentinelProperty(rootType)) { 
                rootType.AddPropertyRef(NullSentinelPropertyRef.Instance); 
            }
 
            //
            // Then add members from each type in the hierarchy (including
            // the root type)
            // 
            ExplodeRootStructuredTypeHelper(rootType);
 
            // 
            // For entity types, add all the rel-properties now. Note that rel-properties
            // are added after the regular properties of all subtypes 
            //
            if (md.TypeSemantics.IsEntityType(rootType.Type)) {
                AddRelProperties(rootType);
            } 

            // 
            // We've now gotten all the relevant properties 
            // Now let's create a new record type
            // 
            CreateFlattenedRecordType(rootType);
        }

        ///  
        /// Helper for ExplodeType.
        /// Walks through each member introduced by the current type, and 
        /// adds it onto the "flat" record type being constructed. 
        /// We then walk through all subtypes of this type, and process those as
        /// well. 
        /// Special handling for Refs: we only add the keys; there is no
        /// need to handle subtypes (since they won't be introducing anything
        /// different)
        ///  
        /// type in the type hierarchy
        private void ExplodeRootStructuredTypeHelper(TypeInfo typeInfo) { 
            RootTypeInfo rootType = typeInfo.RootType; 

            // Identify the members of this type. For Refs, use the key properties 
            // of the target entity type. For all other types, simply use the type
            // members
            IEnumerable typeMembers = null;
            md.RefType refType; 
            if (TypeHelpers.TryGetEdmType(typeInfo.Type, out refType)) {
                // 
                // If this is not the root type, then don't bother adding the keys. 
                // the root type has already done this
                // 
                if (!typeInfo.IsRootType) {
                    return;
                }
                typeMembers = refType.ElementType.KeyMembers; 
            }
            else { 
                typeMembers = TypeHelpers.GetDeclaredStructuralMembers(typeInfo.Type); 
            }
 
            // Walk through all the members of the type
            foreach (md.EdmMember p in typeMembers) {
                TypeInfo propertyType = ExplodeType(p.TypeUsage);
 
                //
                // If we can't find a TypeInfo for this property's type, then it must 
                // be a scalar type or a collection type. In either case, we'll 
                // build up a SimplePropertyRef
                // 
                if (propertyType == null) {
                    rootType.AddPropertyRef(new SimplePropertyRef(p));
                }
                else { 
                    //
                    // We're dealing with a structured type again. Create NestedPropertyRef 
                    // for each property of the nested type 
                    //
                    foreach (PropertyRef nestedPropInfo in propertyType.PropertyRefList) { 
                        rootType.AddPropertyRef(nestedPropInfo.CreateNestedPropertyRef(p));
                    }
                }
            } 

            // 
            // Process all subtypes now 
            //
            foreach (TypeInfo subTypeInfo in typeInfo.ImmediateSubTypes) { 
                ExplodeRootStructuredTypeHelper(subTypeInfo);
            }
        }
 
        /// 
        /// Add the list of rel-properties for this type 
        ///  
        /// the type to process
        private void AddRelProperties(TypeInfo typeInfo) { 

            md.EntityTypeBase entityType = (md.EntityTypeBase)typeInfo.Type.EdmType;

            // 
            // Walk through each rel-property defined for this specific type,
            // and add a corresponding property-ref 
            // 
            foreach (RelProperty p in m_relPropertyHelper.GetDeclaredOnlyRelProperties(entityType)) {
                md.EdmType refType = p.ToEnd.TypeUsage.EdmType; 
                TypeInfo refTypeInfo = GetTypeInfo(p.ToEnd.TypeUsage);

                //
                // We're dealing with a structured type again - flatten this out 
                // as well
                // 
                ExplodeType(refTypeInfo); 

                foreach (PropertyRef nestedPropInfo in refTypeInfo.PropertyRefList) { 
                    typeInfo.RootType.AddPropertyRef(nestedPropInfo.CreateNestedPropertyRef(p));
                }
            }
 
            //
            // Process all subtypes now 
            // 
            foreach (TypeInfo subTypeInfo in typeInfo.ImmediateSubTypes) {
                AddRelProperties(subTypeInfo); 
            }
        }

        ///  
        /// Create the flattened record type for the type.
        /// Walk through the list of property refs, and creates a new field 
        /// (which we name as "F1", "F2" etc.) with the required property type. 
        ///
        /// We then produce a mapping from the original property (propertyRef really) 
        /// to the new property for use in later modules.
        ///
        /// Finally, we identify the TypeId and EntitySetId property if they exist
        ///  
        /// 
        private void CreateFlattenedRecordType(RootTypeInfo type) { 
            // 
            // If this type corresponds to an entity type, and that entity type
            // has no subtypes, and that that entity type has no complex properties 
            // then simply use the name from that property
            //
            bool usePropertyNamesFromUnderlyingType;
            if (md.TypeSemantics.IsEntityType(type.Type) && 
                type.ImmediateSubTypes.Count == 0) {
                usePropertyNamesFromUnderlyingType = true; 
            } 
            else {
                usePropertyNamesFromUnderlyingType = false; 
            }


            // Build the record type 
            List> fieldList = new List>();
            int fieldId = 0; 
            foreach (PropertyRef p in type.PropertyRefList) { 
                string fieldName = null;
                if (usePropertyNamesFromUnderlyingType) { 
                    SimplePropertyRef simpleP = p as SimplePropertyRef;
                    if (simpleP != null) {
                        fieldName = simpleP.Property.Name;
                    } 
                }
                md.TypeUsage propertyType = GetPropertyType(type, p); 
                if (fieldName == null) { 
                    //
                    // Deal with collisions? 
                    //
                    fieldName = "F" + fieldId.ToString(CultureInfo.InvariantCulture);
                }
                fieldList.Add(new KeyValuePair(fieldName, propertyType)); 
                fieldId++;
            } 
 
            type.FlattenedType = TypeHelpers.CreateRowType(fieldList);
 
            // Now build up the property map
            IEnumerator origProps = type.PropertyRefList.GetEnumerator();
            foreach (md.EdmProperty p in type.FlattenedType.Properties) {
                if (!origProps.MoveNext()) { 
                    PlanCompiler.Assert(false, "property refs count and flattened type member count mismatch?");
                } 
                type.AddPropertyMapping(origProps.Current, p); 
            }
        } 

        /// 
        /// Get the "new" type corresponding to the input type. For structured types,
        /// we return the flattened record type. 
        /// For collections of structured type, we return a new collection type of the corresponding flattened
        /// type. 
        /// For everything else, we return the input type 
        /// 
        /// the original type 
        /// the new type (if any)
        private md.TypeUsage GetNewType(md.TypeUsage type) {
            if (TypeUtils.IsStructuredType(type)) {
                TypeInfo typeInfo = GetTypeInfo(type); 
                return typeInfo.FlattenedTypeUsage;
            } 
            md.TypeUsage elementType; 
            if (TypeHelpers.TryGetCollectionElementType(type, out elementType)) {
                md.TypeUsage newElementType = GetNewType(elementType); 
                if (newElementType.EdmEquals(elementType)) {
                    return type;
                }
                else { 
                    return TypeHelpers.CreateCollectionTypeUsage(newElementType);
                } 
 
            }
 
            // simple scalar
            return type;
        }
 
        /// 
        /// Get the datatype for a propertyRef. The only concrete classes that we 
        /// handle are TypeIdPropertyRef, and BasicPropertyRef. 
        /// AllPropertyRef is illegal here.
        /// For BasicPropertyRef, we simply pick up the type from the corresponding 
        /// property. For TypeIdPropertyRef, we use "string" as the default type
        /// or the discriminator property type where one is available.
        /// 
        /// typeinfo of the current type 
        /// current property ref
        /// the datatype of the property 
        private md.TypeUsage GetPropertyType(RootTypeInfo typeInfo, PropertyRef p) { 
            md.TypeUsage result = null;
 
            PropertyRef innerProperty = null;
            // Get the "leaf" property first
            while (p is NestedPropertyRef) {
                NestedPropertyRef npr = (NestedPropertyRef)p; 
                p = npr.OuterProperty;
                innerProperty = npr.InnerProperty; 
            } 

            if (p is TypeIdPropertyRef) { 
                //
                // Get to the innermost type that specifies this typeid (the entity type),
                // get the datatype for the typeid column from that type
                // 
                if (innerProperty != null && innerProperty is SimplePropertyRef) {
                    md.TypeUsage innerType = ((SimplePropertyRef)innerProperty).Property.TypeUsage; 
                    TypeInfo innerTypeInfo = GetTypeInfo(innerType); 
                    result = innerTypeInfo.RootType.TypeIdType;
                } 
                else {
                    result = typeInfo.TypeIdType;
                }
            } 
            else if (p is EntitySetIdPropertyRef || p is NullSentinelPropertyRef) {
                result = m_intType; 
            } 
            else if (p is RelPropertyRef) {
                result = (p as RelPropertyRef).Property.ToEnd.TypeUsage; 
            }
            else {
                SimplePropertyRef simpleP = p as SimplePropertyRef;
                if (simpleP != null) { 
                    result = md.Helper.GetModelTypeUsage(simpleP.Property);
                } 
            } 

            result = GetNewType(result); 
            PlanCompiler.Assert(null != result, "unrecognized property type?");
            return result;
        }
 
        #endregion
 
        #endregion 

        #region utils 
        /// 
        /// Get the root entity type for a type
        /// 
        /// entity type 
        /// 
        private static md.EntityTypeBase GetRootType(md.EntityTypeBase type) { 
            while (type.BaseType != null) { 
                type = (md.EntityTypeBase)type.BaseType;
            } 
            return type;
        }
        #endregion
        #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