File: System\Data\Mapping\ViewGeneration\Structures\MemberProjectionIndex.cs
Project: ndp\fx\src\DataEntity\System.Data.Entity.csproj (System.Data.Entity)
//---------------------------------------------------------------------
// <copyright file="MemberProjectionIndex.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
 
using System.Collections.Generic;
using System.Text;
using System.Data.Common.Utils;
using System.Data.Common;
using System.Data.Mapping.ViewGeneration.Structures;
using System.Diagnostics;
using System.Data.Metadata.Edm;
 
namespace System.Data.Mapping.ViewGeneration.Structures
{
    /// <summary>
    /// Manages <see cref="MemberPath"/>s of the members of the types stored in an extent.
    /// This is a bi-directional dictionary of <see cref="MemberPath"/>s to integer indexes and back.
    /// </summary>
    internal sealed class MemberProjectionIndex : InternalBase
    {
        #region Fields
        private readonly Dictionary<MemberPath, int> m_indexMap;
        private readonly List<MemberPath> m_members;
        #endregion
 
        #region Constructor/Factory
        /// <summary>
        /// Recursively generates <see cref="MemberPath"/>s for the members of the types stored in the <paramref name="extent"/>.
        /// </summary>
        internal static MemberProjectionIndex Create(EntitySetBase extent, EdmItemCollection edmItemCollection)
        {
            // We generate the indices for the projected slots as we traverse the metadata.
            MemberProjectionIndex index = new MemberProjectionIndex();
            GatherPartialSignature(index, edmItemCollection, new MemberPath(extent), false); // need not only keys
            return index;
        }
 
        /// <summary>
        /// Creates an empty index.
        /// </summary>
        private MemberProjectionIndex()
        {
            m_indexMap = new Dictionary<MemberPath, int>(MemberPath.EqualityComparer);
            m_members = new List<MemberPath>();
        }
        #endregion
 
        #region Properties
        internal int Count
        {
            get { return m_members.Count; }
        }
 
        internal MemberPath this[int index]
        {
            get { return m_members[index]; }
        }
 
        /// <summary>
        /// Returns the indexes of the key slots corresponding to fields in this for which IsPartOfKey is true.
        /// </summary>
        internal IEnumerable<int> KeySlots
        {
            get
            {
                List<int> result = new List<int>();
                for (int slotNum = 0; slotNum < Count; slotNum++)
                {
                    // We pass for numboolslots since we know that this is not a
                    // bool slot
                    if (this.IsKeySlot(slotNum, 0))
                    {
                        result.Add(slotNum);
                    }
                }
                return result;
            }
        }
 
        /// <summary>
        /// Returns an enumeration of all members
        /// </summary>
        internal IEnumerable<MemberPath> Members
        {
            get { return m_members; }
        }
        #endregion
 
        #region Methods
        /// <summary>
        /// Returns a non-negative index of the <paramref name="member"/> if found, otherwise -1.
        /// </summary>
        internal int IndexOf(MemberPath member)
        {
            int index;
            if (m_indexMap.TryGetValue(member, out index))
            {
                return index;
            }
            else
            {
                return -1;
            }
        }
 
        /// <summary>
        /// If an index already exists for member, this is a no-op. Else creates the next index available for member and returns it.
        /// </summary>
        internal int CreateIndex(MemberPath member)
        {
            int index;
            if (false == m_indexMap.TryGetValue(member, out index))
            {
                index = m_indexMap.Count;
                m_indexMap[member] = index;
                m_members.Add(member);
            }
            return index;
        }
 
        /// <summary>
        /// Given the <paramref name="slotNum"/>, returns the output member path that this slot contributes/corresponds to in the extent view.
        /// If the slot corresponds to one of the boolean variables, returns null.
        /// </summary>
        internal MemberPath GetMemberPath(int slotNum, int numBoolSlots)
        {
            MemberPath result = this.IsBoolSlot(slotNum, numBoolSlots) ? null : this[slotNum];
            return result;
        }
 
        /// <summary>
        /// Given the index of a boolean variable (e.g., of from1), returns the slot number for that boolean in this.
        /// </summary>
        internal int BoolIndexToSlot(int boolIndex, int numBoolSlots)
        {
            // Booleans appear after the regular slots
            Debug.Assert(boolIndex >= 0 && boolIndex < numBoolSlots, "No such boolean in this node");
            return this.Count + boolIndex;
        }
 
        /// <summary>
        /// Given the <paramref name="slotNum"/> corresponding to a boolean slot, returns the cell number that the cell corresponds to.
        /// </summary>
        internal int SlotToBoolIndex(int slotNum, int numBoolSlots)
        {
            Debug.Assert(slotNum < this.Count + numBoolSlots && slotNum >= this.Count, "No such boolean slot");
            return slotNum - this.Count;
        }
 
        /// <summary>
        /// Returns true if <paramref name="slotNum"/> corresponds to a key slot in the output extent view.
        /// </summary>
        internal bool IsKeySlot(int slotNum, int numBoolSlots)
        {
            Debug.Assert(slotNum < this.Count + numBoolSlots, "No such slot in tree");
            return slotNum < this.Count && this[slotNum].IsPartOfKey;
        }
 
        /// <summary>
        /// Returns true if <paramref name="slotNum"/> corresponds to a bool slot and not a regular field.
        /// </summary>
        internal bool IsBoolSlot(int slotNum, int numBoolSlots)
        {
            Debug.Assert(slotNum < this.Count + numBoolSlots, "Boolean slot does not exist in tree");
            return slotNum >= this.Count;
        }
 
        internal override void ToCompactString(StringBuilder builder)
        {
            builder.Append('<');
            StringUtil.ToCommaSeparatedString(builder, m_members);
            builder.Append('>');
        }
        #endregion
 
        #region Signature construction
        /// <summary>
        /// Starting at the <paramref name="member"/>, recursively generates <see cref="MemberPath"/>s for the fields embedded in it.
        /// </summary>
        /// <param name="member">corresponds to a value of an Entity or Complex or Association type</param>
        /// <param name="needKeysOnly">indicates whether we need to only collect members that are keys</param>
        private static void GatherPartialSignature(MemberProjectionIndex index, EdmItemCollection edmItemCollection, MemberPath member, bool needKeysOnly)
        {
            EdmType memberType = member.EdmType;
            ComplexType complexTypemember = memberType as ComplexType;
            Debug.Assert(complexTypemember != null ||
                         memberType is EntityType || // for entity sets
                         memberType is AssociationType || // For association sets
                         memberType is RefType, // for association ends
                         "GatherPartialSignature can be called only for complex types, entity sets, association ends");
 
            if (memberType is ComplexType && needKeysOnly)
            {
                // Check if the complex type needs to be traversed or not. If not, just return 
                // from here. Else we need to continue to the code below. Right now, we do not
                // allow keys inside complex types
                return;
            }
 
            // Make sure that this member is in the slot map before any of its embedded objects.
            index.CreateIndex(member);
 
            // Consider each possible type value -- each type value conributes to a tuple in the result.
            // For that possible type, add all the type members into the signature.
            foreach (EdmType possibleType in MetadataHelper.GetTypeAndSubtypesOf(memberType, edmItemCollection, false /*includeAbstractTypes*/))
            {
                StructuralType possibleStructuralType = possibleType as StructuralType;
                Debug.Assert(possibleStructuralType != null, "Non-structural subtype?");
 
                GatherSignatureFromTypeStructuralMembers(index, edmItemCollection, member, possibleStructuralType, needKeysOnly);
            }
        }
 
        /// <summary>
        /// Given the <paramref name="member"/> and one of its <paramref name="possibleType"/>s, determine the attributes that are relevant
        /// for this <paramref name="possibleType"/> and return a <see cref="MemberPath"/> signature corresponding to the <paramref name="possibleType"/> and the attributes.
        /// If <paramref name="needKeysOnly"/>=true, collect the key fields only.
        /// </summary>
        /// <param name="possibleType">the <paramref name="member"/>'s type or one of its subtypes</param>
        private static void GatherSignatureFromTypeStructuralMembers(MemberProjectionIndex index,
                                                                     EdmItemCollection edmItemCollection,
                                                                     MemberPath member, 
                                                                     StructuralType possibleType, 
                                                                     bool needKeysOnly)
        {
            // For each child member of this type, collect all the relevant scalar fields
            foreach (EdmMember structuralMember in Helper.GetAllStructuralMembers(possibleType))
            {
                if (MetadataHelper.IsNonRefSimpleMember(structuralMember))
                {
                    if (!needKeysOnly || MetadataHelper.IsPartOfEntityTypeKey(structuralMember))
                    {
                        MemberPath nonStructuredMember = new MemberPath(member, structuralMember);
                        // Note: scalarMember's parent has already been added to the projectedSlotMap
                        index.CreateIndex(nonStructuredMember);
                    }
                }
                else
                {
                    Debug.Assert(structuralMember.TypeUsage.EdmType is ComplexType ||
                                 structuralMember.TypeUsage.EdmType is RefType, // for association ends
                                 "Only non-scalars expected - complex types, association ends");
 
                    
 
                    MemberPath structuredMember = new MemberPath(member, structuralMember);
                    GatherPartialSignature(index, 
                                           edmItemCollection, 
                                           structuredMember,
                                           // Only keys are required for entities referenced by association ends of an association.
                                           needKeysOnly || Helper.IsAssociationEndMember(structuralMember));
                }
            }
        }
        #endregion
    }
}