File: System\Data\Mapping\ViewGeneration\Structures\Cell.cs
Project: ndp\fx\src\DataEntity\System.Data.Entity.csproj (System.Data.Entity)
//---------------------------------------------------------------------
// <copyright file="Cell.cs" company="Microsoft">
//      Copyright (c) Microsoft Corporation.  All rights reserved.
// </copyright>
//
// @owner Microsoft
// @backupOwner Microsoft
//---------------------------------------------------------------------
 
using System.Data.Common.Utils;
using System.Collections.Generic;
using System.Data.Mapping.ViewGeneration.Validation;
using System.Text;
using System.Diagnostics;
using System.Data.Metadata.Edm;
 
namespace System.Data.Mapping.ViewGeneration.Structures
{
 
    /// <summary>
    /// This class contains a pair of cell queries which is essentially a
    /// constraint that they are equal. A cell is initialized with a C or an
    /// S Query which it exposes as properties but it also has the notion of
    /// "Left" and "Right" queries -- left refers to the side for which a
    /// view is being generated 
    /// For example, to
    /// specify a mapping for CPerson to an SPerson table, we have
    ///
    /// [(p type Person) in P : SPerson]
    /// (p.pid, pid)
    /// (p.name, name)
    ///
    /// This really denotes the equality of two queries:
    /// (C) SELECT (p type Person) AS D1, p.pid, p.name FROM p in P WHERE D1 
    /// (S) SELECT True AS D1, pid, name FROM SPerson WHERE D1  
    ///
    /// For more details, see the design doc
    /// </summary>
    internal class Cell : InternalBase
    {
        #region Constructor
        // effects: Creates a cell with the C and S queries 
        private Cell(CellQuery cQuery, CellQuery sQuery, CellLabel label, int cellNumber)
        {
            Debug.Assert(label != null, "Cell lacks label");
            m_cQuery = cQuery;
            m_sQuery = sQuery;
            m_label = label;
            m_cellNumber = cellNumber;
            Debug.Assert(m_sQuery.NumProjectedSlots == m_cQuery.NumProjectedSlots,
                         "Cell queries disagree on the number of projected fields");
        }
        /// <summary>
        /// Copy Constructor
        /// </summary>
        internal Cell(Cell source)
        {
            m_cQuery = new CellQuery(source.m_cQuery);
            m_sQuery = new CellQuery(source.m_sQuery);
            m_label = new CellLabel(source.m_label);
            m_cellNumber = source.m_cellNumber;
        }
 
        #endregion
 
        #region Fields
        private CellQuery m_cQuery;
        private CellQuery m_sQuery;
        private int m_cellNumber; // cell number that identifies this cell
        private CellLabel m_label; // The File and Path Info for the CSMappingFragment
        // that the Cell was constructed over.
        // The view cell relation for all projected slots in this
        private ViewCellRelation m_viewCellRelation;
        #endregion
 
        #region Properties
        // effects: Returns the C query
        internal CellQuery CQuery
        {
            get { return m_cQuery; }
        }
 
        // effects: Returns the S query
        internal CellQuery SQuery
        {
            get { return m_sQuery; }
        }
 
        // effects: Returns the CSMappingFragment (if any)
        // that the Cell was constructed over.
        internal CellLabel CellLabel
        {
            get { return m_label; }
        }
 
        // effects: Returns the cell label (if any)
        internal int CellNumber
        {
            get { return m_cellNumber; }
        }
 
        internal string CellNumberAsString
        {
            get { return StringUtil.FormatInvariant("V{0}", CellNumber); }
        }
        #endregion
 
        #region Methods
        // effects: Determines all the identifiers used in this and adds them to identifiers
        internal void GetIdentifiers(CqlIdentifiers identifiers)
        {
            m_cQuery.GetIdentifiers(identifiers);
            m_sQuery.GetIdentifiers(identifiers);
        }
 
        // effects: Given a cell, determines the paths to which the paths in
        // columns map to in the C-space and returns them. If some columns
        // are not projected in the cell, or if the corresponding properties
        // are not mapped into C-space, returns null
        internal Set<EdmProperty> GetCSlotsForTableColumns(IEnumerable<MemberPath> columns)
        {
            List<int> fieldNums = SQuery.GetProjectedPositions(columns);
            if (fieldNums == null)
            {
                return null;
            }
 
            // The fields are mapped -- see if they are mapped on the
            // cSide and they correspond to the primary key of the
            // entity set
 
            Set<EdmProperty> cSideMembers = new Set<EdmProperty>();
            foreach (int fieldNum in fieldNums)
            {
                ProjectedSlot projectedSlot = CQuery.ProjectedSlotAt(fieldNum);
                MemberProjectedSlot slot = projectedSlot as MemberProjectedSlot;
                if (slot != null)
                {
                    // We can call LastMember since columns do not map to
                    // extents or memberEnds. Can cast to EdmProperty since it
                    // cannot be an association end
                    cSideMembers.Add((EdmProperty)slot.MemberPath.LeafEdmMember);
                }
                else
                {
                    return null;
                }
            }
            return cSideMembers;
        }
 
        // effects: Returns the C query for ViewTarget.QueryView and S query for ViewTarget.UpdateView
        internal CellQuery GetLeftQuery(ViewTarget side)
        {
            return side == ViewTarget.QueryView ? m_cQuery : m_sQuery;
        }
 
        // effects: Returns the S query for ViewTarget.QueryView and C query for ViewTarget.UpdateView
        internal CellQuery GetRightQuery(ViewTarget side)
        {
            return side == ViewTarget.QueryView ? m_sQuery : m_cQuery;
        }
 
        // effects: Returns the relation that contains all the slots being
        // projected in this cell 
        internal ViewCellRelation CreateViewCellRelation(int cellNumber)
        {
            if (m_viewCellRelation != null)
            {
                return m_viewCellRelation;
            }
            GenerateCellRelations(cellNumber);
            return m_viewCellRelation;
        }
 
        private void GenerateCellRelations(int cellNumber)
        {
            // Generate the view cell relation
            List<ViewCellSlot> projectedSlots = new List<ViewCellSlot>();
            // construct a ViewCellSlot for each slot
            Debug.Assert(CQuery.NumProjectedSlots == SQuery.NumProjectedSlots,
                         "Cell queries in cell have a different number of slots");
            for (int i = 0; i < CQuery.NumProjectedSlots; i++)
            {
                ProjectedSlot cSlot = CQuery.ProjectedSlotAt(i);
                ProjectedSlot sSlot = SQuery.ProjectedSlotAt(i);
                Debug.Assert(cSlot != null, "Has cell query been normalized?");
                Debug.Assert(sSlot != null, "Has cell query been normalized?");
 
                // These slots better be MemberProjectedSlots. We do not have constants etc at this point.
                Debug.Assert(cSlot is MemberProjectedSlot, "cSlot is expected to be MemberProjectedSlot");
                Debug.Assert(sSlot is MemberProjectedSlot, "sSlot is expected to be MemberProjectedSlot");
 
                MemberProjectedSlot cJoinSlot = (MemberProjectedSlot)cSlot;
                MemberProjectedSlot sJoinSlot = (MemberProjectedSlot)sSlot;
 
                ViewCellSlot slot = new ViewCellSlot(i, cJoinSlot, sJoinSlot);
                projectedSlots.Add(slot);
            }
            m_viewCellRelation = new ViewCellRelation(this, projectedSlots, cellNumber);
        }
 
        internal override void ToCompactString(StringBuilder builder)
        {
            CQuery.ToCompactString(builder);
            builder.Append(" = ");
            SQuery.ToCompactString(builder);
        }
 
        internal override void ToFullString(StringBuilder builder)
        {
            CQuery.ToFullString(builder);
            builder.Append(" = ");
            SQuery.ToFullString(builder);
        }
 
        public override string ToString()
        {
            return ToFullString();
        }
 
        // effects: Prints the cells in some human-readable form
        internal static void CellsToBuilder(StringBuilder builder, IEnumerable<Cell> cells)
        {
            // Print mapping
            builder.AppendLine();
            builder.AppendLine("=========================================================================");
            foreach (Cell cell in cells)
            {
                builder.AppendLine();
                StringUtil.FormatStringBuilder(builder, "Mapping Cell V{0}:", cell.CellNumber);
                builder.AppendLine();
 
                builder.Append("C: ");
                cell.CQuery.ToFullString(builder);
                builder.AppendLine();
                builder.AppendLine();
 
                builder.Append("S: ");
                cell.SQuery.ToFullString(builder);
                builder.AppendLine();
            }
        }
 
        #endregion
 
        #region Factory methods
        internal static Cell CreateCS(CellQuery cQuery, CellQuery sQuery, CellLabel label, int cellNumber)
        {
            return new Cell(cQuery, sQuery, label, cellNumber);
        }
        #endregion
    }
}