File: System\Data\Mapping\ViewGeneration\Structures\MemberProjectedSlot.cs
Project: ndp\fx\src\DataEntity\System.Data.Entity.csproj (System.Data.Entity)
//---------------------------------------------------------------------
// <copyright file="MemberProjectedSlot.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
 
using System.Collections.Generic;
using System.Text;
using System.Diagnostics;
using System.Data.Common.CommandTrees;
using System.Data.Common.CommandTrees.ExpressionBuilder;
using System.Data.Common.Utils;
using System.Data.Metadata.Edm;
using System.Data.Mapping.ViewGeneration.CqlGeneration;
using System.Data.Mapping.ViewGeneration.Utils;
 
namespace System.Data.Mapping.ViewGeneration.Structures
{
    /// <summary>
    /// A wrapper around MemberPath that allows members to be marked as ProjectedSlots.
    /// </summary>
    internal sealed class MemberProjectedSlot : ProjectedSlot
    {
        #region Constructor
        /// <summary>
        /// Creates a projected slot that references the relevant celltree node.
        /// </summary>
        internal MemberProjectedSlot(MemberPath node)
        {
            m_memberPath = node;
        }
        #endregion
 
        #region Fields
        private readonly MemberPath m_memberPath;
        #endregion
 
        #region Properties
        /// <summary>
        /// Returns the full metadata path from the root extent to this node, e.g., Person.Adrs.zip
        /// </summary>
        internal MemberPath MemberPath
        {
            get { return m_memberPath; }
        }
        #endregion
 
        #region Methods
        internal override StringBuilder AsEsql(StringBuilder builder, MemberPath outputMember, string blockAlias, int indentLevel)
        {
            TypeUsage outputMemberStoreTypeUsage;
            if (NeedToCastCqlValue(outputMember, out outputMemberStoreTypeUsage))
            {
                builder.Append("CAST(");
                m_memberPath.AsEsql(builder, blockAlias);
                builder.Append(" AS ");
                CqlWriter.AppendEscapedTypeName(builder, outputMemberStoreTypeUsage.EdmType);
                builder.Append(')');
            }
            else
            {
                m_memberPath.AsEsql(builder, blockAlias);
            }
            return builder;
        }
 
        internal override DbExpression AsCqt(DbExpression row, MemberPath outputMember)
        {
            DbExpression cqt = m_memberPath.AsCqt(row);
 
            TypeUsage outputMemberTypeUsage;
            if (NeedToCastCqlValue(outputMember, out outputMemberTypeUsage))
            {
                cqt = cqt.CastTo(outputMemberTypeUsage);
            }
 
            return cqt;
        }
 
        /// <summary>
        /// True iff <see cref=" m_memberPath"/> and <paramref name="outputMember"/> types do not match,
        /// We assume that the mapping loader has already checked that the casts are ok and emitted warnings.
        /// </summary>
        private bool NeedToCastCqlValue(MemberPath outputMember, out TypeUsage outputMemberTypeUsage)
        {
            TypeUsage memberPathTypeUsage = Helper.GetModelTypeUsage(m_memberPath.LeafEdmMember);
            outputMemberTypeUsage = Helper.GetModelTypeUsage(outputMember.LeafEdmMember);
            return !memberPathTypeUsage.EdmType.Equals(outputMemberTypeUsage.EdmType);
        }
 
        internal override void ToCompactString(StringBuilder builder)
        {
            m_memberPath.ToCompactString(builder);
        }
 
        internal string ToUserString()
        {
            return m_memberPath.PathToString(false);
        }
 
        protected override bool IsEqualTo(ProjectedSlot right)
        {
            MemberProjectedSlot rightSlot = right as MemberProjectedSlot;
            if (rightSlot == null)
            {
                return false;
            }
            // We want equality of the paths
            return MemberPath.EqualityComparer.Equals(m_memberPath, rightSlot.m_memberPath);
        }
 
        protected override int GetHash()
        {
            return MemberPath.EqualityComparer.GetHashCode(m_memberPath);
        }
 
        /// <summary>
        /// Given a slot and the new mapping, returns the corresponding new slot.
        /// </summary>
        internal MemberProjectedSlot RemapSlot(Dictionary<MemberPath, MemberPath> remap)
        {
            MemberPath remappedNode = null;
            if (remap.TryGetValue(MemberPath, out remappedNode))
            {
                return new MemberProjectedSlot(remappedNode);
            }
            else
            {
                return new MemberProjectedSlot(MemberPath);
            }
        }
        #endregion
 
        #region Helper methods
        /// <summary>
        /// Given the <paramref name="prefix"/>, determines the slots in <paramref name="slots"/> that correspond to the entity key for the entity set or the
        /// association set end. Returns the list of slots.  Returns null if even one of the key slots is not present in slots.
        /// </summary>
        /// <param name="prefix">corresponds to an entity set or an association end</param>
        internal static List<MemberProjectedSlot> GetKeySlots(IEnumerable<MemberProjectedSlot> slots, MemberPath prefix)
        {
            // Get the entity type of the hosted end or entity set
            EntitySet entitySet = prefix.EntitySet;
            Debug.Assert(entitySet != null, "Prefix must have associated entity set");
 
            List<ExtentKey> keys = ExtentKey.GetKeysForEntityType(prefix, entitySet.ElementType);
            Debug.Assert(keys.Count > 0, "No keys for entity?");
            Debug.Assert(keys.Count == 1, "Currently, we only support primary keys");
            // Get the slots for the key
            List<MemberProjectedSlot> keySlots = GetSlots(slots, keys[0].KeyFields);
            return keySlots;
        }
 
        /// <summary>
        /// Searches for members in <paramref name="slots"/> and returns the corresponding slots in the same order as present in
        /// <paramref name="members"/>. Returns null if even one member is not present in slots.
        /// </summary>
        internal static List<MemberProjectedSlot> GetSlots(IEnumerable<MemberProjectedSlot> slots, IEnumerable<MemberPath> members)
        {
            List<MemberProjectedSlot> result = new List<MemberProjectedSlot>();
            foreach (MemberPath member in members)
            {
                MemberProjectedSlot slot = GetSlotForMember(Helpers.AsSuperTypeList<MemberProjectedSlot, ProjectedSlot>(slots), member);
                if (slot == null)
                {
                    return null;
                }
                result.Add(slot);
            }
            return result;
        }
 
        /// <summary>
        /// Searches for <paramref name="member"/> in <paramref name="slots"/> and returns the corresponding slot. If none is found, returns null.
        /// </summary>
        internal static MemberProjectedSlot GetSlotForMember(IEnumerable<ProjectedSlot> slots, MemberPath member)
        {
            foreach (MemberProjectedSlot slot in slots)
            {
                if (MemberPath.EqualityComparer.Equals(slot.MemberPath, member))
                {
                    return slot;
                }
            }
            return null;
        }
        #endregion
    }
}