CqlGenerator.cs source code in C# .NET

Source code for the .NET framework in C#

                        

Code:

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

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

using System.Data.Common; 
using System.Data.Common.Utils;
using System.Data.Mapping.ViewGeneration.Structures;
using System.Collections.Generic;
using System.Data.Mapping.ViewGeneration.CqlGeneration; 
using System.Text;
using System.Diagnostics; 
using System.Data.Metadata.Edm; 

namespace System.Data.Mapping.ViewGeneration 
{

    // This class is responsible for generation of CQL after the cell merging
    // process has been done 
    internal class CqlGenerator : InternalBase
    { 
 
        #region Constructor
        // effects: Given the generated "view", the case statements for the 
        // multiconstant fields (caseStatements), a map that maps different
        // paths of the entityset (for which the view is being generated) to
        // slot indexes in the view, creates an object that is capable of
        // generating the Cql for "view" 
        internal CqlGenerator(CellTreeNode view, Dictionary caseStatements,
                              CqlIdentifiers identifiers, MemberPathMapBase projectedSlotMap, 
                              int numCellsInView, BoolExpression topLevelWhereClause, MetadataWorkspace workspace) 
        {
            m_view = view; 
            m_caseStatements = caseStatements;
            m_projectedSlotMap = projectedSlotMap;
            m_numBools = numCellsInView; // We have that many booleans
            m_topLevelWhereClause = topLevelWhereClause; 
            m_identifiers = identifiers;
            m_workspace = workspace; 
        } 
        #endregion
 
        #region Fields
        // The generated view from the cells (e.g., after cell merging)
        private CellTreeNode m_view;
        // Case statements for the multiconstant fields 
        private Dictionary m_caseStatements;
        // Mapping from Memberpaths to slot indexes 
        private MemberPathMapBase m_projectedSlotMap; 
        // No. of booleans in the view, one per cell (from0, from1, etc
        private int m_numBools; // CHANGE_[....]_IMPROVE: Get this from view and cache. Not as a param to constructor 
        // A counter used to generate aliases for blocks
        private int m_currentBlockNum;
        private BoolExpression m_topLevelWhereClause;
        // The prefixes for _from and block aliases (T) 
        private CqlIdentifiers m_identifiers;
        private MetadataWorkspace m_workspace; 
        #endregion 

        #region Properties 
        private int TotalSlots
        {
            get { return m_projectedSlotMap.Count + m_numBools; }
        } 
        #endregion
 
        #region CqlBlock Generation Methods for all node types 
        // effects: Returns a CQL query that represents a query/update
        // mapping view for the view information that was supplied in the constructor 
        internal string GenerateCql()
        {
            // Generate a CqlBlock tree and then convert that to Cql
            CqlBlock blockTree = GenerateCqlBlockTree(); 
            // Create the string builder with 1K so that we don't have to
            // keep growing it 
            StringBuilder builder = new StringBuilder(1024); 
            blockTree.AsCql(builder, true, 1);
            return builder.ToString(); 
        }

        // effects: Generates a CqlBlock tree that is capable of generating
        // the actual Cql strings 
        private CqlBlock GenerateCqlBlockTree()
        { 
 
            // Essentially, we create a block for each CellTreeNode in the
            // tree and then we layer case statements on top of that view -- 
            // one case statement for each multiconstant entry

            // Dertmine the slots that are projected by the whole tree. Tell
            // the children that they need to produce those slots somehow -- 
            // if they don't have it, they can produce null
            bool[] requiredSlots = GetRequiredSlots(); 
            Debug.Assert(requiredSlots.Length == TotalSlots, "Wrong number of requiredSlots"); 

            List withStatements = new List(); 
            CqlBlock viewBlock = m_view.ToCqlBlock(requiredSlots, m_identifiers, ref m_currentBlockNum, ref withStatements);

            // Handle case statements for multiconstant entries
            // Right now, we have a simplication step that removes one of the 
            // entries and adds ELSE instead
            foreach (CaseStatement statement in m_caseStatements.Values) 
            { 
                statement.Simplify();
            } 

            // Generate the case statements and get the top level block which
            // must correspond to the entity set
            CqlBlock finalViewBlock = ConstructCaseBlocks(viewBlock, withStatements); 
            return finalViewBlock;
        } 
 

        private bool[] GetRequiredSlots() 
        {
            bool[] requiredSlots = new bool[TotalSlots];
            // union all slots that are required in case statements
            foreach (CaseStatement caseStatement in m_caseStatements.Values) 
            {
                int slotNum = m_projectedSlotMap.IndexOf(caseStatement.MemberPath); 
                GetRequiredSlotsForCaseMember(slotNum, caseStatement.MemberPath, requiredSlots); 
            }
 
            // For now, make sure that all booleans are required
            // Reason: OUTER JOINs may introduce an extra CASE statement (in OpCellTreeNode.cs/GetJoinSlotInfo)
            // if a member is projected in both inputs to the join.
            // This case statement may use boolean variables that may not be marked as "required" 
            // The problem is that this decision is made _after_ CqlBlocks for children get produced (in OpCellTreeNode.cs/JoinToCqlBlock)
            for (int i = TotalSlots - m_numBools; i < TotalSlots; i++) 
            { 
                requiredSlots[i] = true;
            } 
            // Because of the above we don't need to harvest used booleans from the top-level WHERE clause
            // m_topLevelWhereClause.GetRequiredSlots(m_projectedSlotMap, requiredSlots);

            // slot value is required for case statement? 
            foreach (CaseStatement caseStatement in m_caseStatements.Values)
            { 
                if (!caseStatement.MemberPath.IsScalarType() || 
                    !caseStatement.DependsOnMemberValue)
                { 
                    requiredSlots[m_projectedSlotMap.IndexOf(caseStatement.MemberPath)] = false;
                }
            }
            return requiredSlots; 
        }
 
        #endregion 

        #region CaseStatement Block Methods 
        // effects: Given a CqlBlock tree, generates the case statements
        // blocks on top of it (using m_casestatements) and returns the
        // resulting tree
        private CqlBlock ConstructCaseBlocks(CqlBlock viewBlock, IEnumerable withStatements) 
        {
            // Get the 0th slot only, i.e., the extent 
            bool[] topSlots = new bool[TotalSlots]; 
            topSlots[0] = true;
 
            // all booleans in the top-level WHERE clause are required and get bubbled up
            // this makes some _fromX booleans be marked as 'required by parent'
            m_topLevelWhereClause.GetRequiredSlots(m_projectedSlotMap, topSlots);
            CqlBlock result = ConstructCaseBlocks(viewBlock, 0, topSlots, withStatements); 
            return result;
        } 
 
        // effects: Given a CqlBlock tree generated by the cell merging
        // process (viewBlock) and the required slots by the parent, 
        // generates the casestatement block tree starting from startSlotNum,
        // i.e., only for case statements that are beyond startSlotNum
        private CqlBlock ConstructCaseBlocks(CqlBlock viewBlock, int startSlotNum,
                                             bool[] parentRequiredSlots, IEnumerable withStatements) 
        {
            int numMembers = m_projectedSlotMap.Count; 
            // Find the next slot for which we have a case statement, i.e., 
            // which was in the multiconstants
            int foundSlot = FindNextCaseStatementSlot(startSlotNum, parentRequiredSlots, numMembers); 

            if (foundSlot == -1)
            {
                // We have bottomed out - no more slots to generate cases for 
                // Just get the base view block
                return viewBlock; 
            } 

            // Compute the requiredSlots for this member, i.e., what slots 
            // are needed to produce this member
            MemberPath thisMember = m_projectedSlotMap[foundSlot];
            bool[] thisRequiredSlots = new bool[TotalSlots];
            GetRequiredSlotsForCaseMember(foundSlot, thisMember, thisRequiredSlots); 
            Debug.Assert(thisRequiredSlots.Length == parentRequiredSlots.Length &&
                         thisRequiredSlots.Length == TotalSlots, 
                         "Number of slots in array should not vary across blocks"); 

            // Merge parent's requirements with this requirements 
            for (int i = 0; i < TotalSlots; i++)
            {
                // We do ask the children to generate the slot that we are
                // producing if it is available 
                if (parentRequiredSlots[i])
                { 
                    thisRequiredSlots[i] = true; 
                }
            } 
            // we just filled foundSlot - mark it as non-required if it produces constants only
            CaseStatement thisCaseStatement = m_caseStatements[thisMember];
            thisRequiredSlots[foundSlot] = thisCaseStatement.DependsOnMemberValue;
 
            // Recursively, determine the block tree for slots beyond
            // foundSlot. 
 
            CqlBlock childBlock = ConstructCaseBlocks(viewBlock, foundSlot + 1, thisRequiredSlots, null);
 
            // For each slot, create a SlotInfo object
            SlotInfo[] slotInfos = CreateSlotInfosForCaseStatement(parentRequiredSlots, foundSlot,
                                                                   childBlock, thisCaseStatement, withStatements);
            m_currentBlockNum++; 
            // We have a where clause only at the top level
            BoolExpression whereClause = startSlotNum == 0 ? m_topLevelWhereClause : BoolExpression.True; 
            if (startSlotNum == 0) 
            {
                // only slot #0 is required by parent; reset all 'required by parent' booleans introduced above 
                for (int i = 1; i < slotInfos.Length; i++)
                {
                    slotInfos[i].ResetIsRequiredByParent();
                } 
            }
            CaseCqlBlock result = new CaseCqlBlock(slotInfos, foundSlot, childBlock, whereClause, m_identifiers, m_currentBlockNum); 
            return result; 
        }
 
        // effects: Given the slot (foundSlot) and its corresponding case
        // statements (thisCaseStatement), generates the slotinfos for the
        // case statement block. Uses parentRequiredSlots and childblock to
        // determine if the slot is required and if it is projected by the child 
        private SlotInfo[] CreateSlotInfosForCaseStatement(bool[] parentRequiredSlots, int foundSlot,
                                                           CqlBlock childBlock, CaseStatement thisCaseStatement, 
                                                           IEnumerable withStatements ) 
        {
            int numSlotsAddedByChildBlock = childBlock.Slots.Count - TotalSlots; 
            SlotInfo[] slotInfos = new SlotInfo[TotalSlots + numSlotsAddedByChildBlock];
            for (int slotNum = 0; slotNum < TotalSlots; slotNum++)
            {
                bool isProjected = childBlock.IsProjected(slotNum); 
                bool isRequiredByParent = parentRequiredSlots[slotNum];
                ProjectedSlot slot = childBlock.ProjectedSlot(slotNum); 
                MemberPath memberPath = GetMemberPath(slotNum); 
                if (slotNum == foundSlot)
                { 
                    // We need a case statement instead for this slot that we
                    // are handling right now
                    Debug.Assert(isRequiredByParent, "Case result not needed by parent");
 
                    // Get a case statement with all slots replaced by aliases slots
                    CaseStatement newCaseStatement = thisCaseStatement.MakeCaseWithAliasedSlots(childBlock, memberPath, slotNum); 
                    slot = new CaseStatementSlot(newCaseStatement, withStatements); 
                    isProjected = true; // We are projecting this slot now
                } 
                else if (slot != null && isProjected && isRequiredByParent)
                {
                    // We only alias something that is needed and is being
                    // projected by the child 
                    // It is an aliased slot into the child block
                    slot = new AliasedSlot(childBlock, slot, memberPath, slotNum); 
                } 
                // For slots, if it is not required by the parent, we want to
                // set the isRequiredByParent for this slot to be 
                // false. Furthermore, we do not want to introduce any "NULL
                // AS something" at this stage for slots not being
                // projected. So if the child does not project that slot, we
                // declare it as not being required by the parent (if such a 
                // NULL was needed, it would have been pushed all the way
                // down to a non-case block. 
                // Essentially, from a Case statement's parent perspective, 
                // it is saying "If you can produce a slot either by yourself
                // or your children, please do. Otherwise, do not concoct anything" 
                SlotInfo slotInfo = new SlotInfo(isRequiredByParent && isProjected, isProjected,
                                                 slot, memberPath);
                slotInfos[slotNum] = slotInfo;
            } 
            for (int i = TotalSlots; i < TotalSlots + numSlotsAddedByChildBlock; i++)
            { 
                SlotInfo slotInfo = childBlock.Slots[i]; 
                AliasedSlot childAddedSlot = new AliasedSlot(childBlock, slotInfo.SlotValue, slotInfo.MemberPath, i);
                slotInfos[i] = new SlotInfo(true, true, childAddedSlot, childAddedSlot.MemberPath); 
            }
            return slotInfos;
        }
 
        // effects: Returns the next slot starting at startSlotNum that is present in
        // the case statements 
        private int FindNextCaseStatementSlot(int startSlotNum, bool[] parentRequiredSlots, int numMembers) 
        {
            int foundSlot = -1; 
            // Simply go through the slots and check the m_caseStatements map
            for (int slotNum = startSlotNum; slotNum < numMembers; slotNum++)
            {
                MemberPath member = m_projectedSlotMap[slotNum]; 
                if (parentRequiredSlots[slotNum] && m_caseStatements.ContainsKey(member))
                { 
                    foundSlot = slotNum; 
                    break;
                } 
            }
            return foundSlot;
        }
 
        // requires: member is part of m_caseStatements
        // effects: Returns an array of size TotalSlots which indicates the 
        // slots that are needed to constuct "member", e.g., CPerson may need 
        // pid and name (say slots 2 and 5 -- then bools[2] and bools[5] will
        // be true 
        private void GetRequiredSlotsForCaseMember(int memberSlotNum, MemberPath member, bool[] requiredSlots)
        {
            Debug.Assert(true == m_caseStatements.ContainsKey(member),
                         "Constructing case for regular field?"); 
            Debug.Assert(requiredSlots.Length == TotalSlots, "Invalid array size for populating required slots");
            CaseStatement statement = m_caseStatements[member]; 
 
            // Find the required slots from the when then clause conditions
            // and values 
            bool mustRequireThisSlot = false;
            foreach (CaseStatement.WhenThen clause in statement.Clauses)
            {
                clause.Condition.GetRequiredSlots(m_projectedSlotMap, requiredSlots); 
                ProjectedSlot slot = clause.Value;
                if (false == (slot is ConstantSlot)) 
                { 
                    // If this slot is a scalar and a non-constant,
                    // we need the lower down blocks to generate it for us 
                    mustRequireThisSlot = true;
                }
            }
 
            EdmType edmType = member.EdmType;
            bool isTypeMember = Helper.IsEntityType(edmType) || Helper.IsComplexType(edmType); 
            //// Non-scalar field 
            if (isTypeMember)
            { 
                foreach (EdmType instantiatedType in statement.InstantiatedTypes)
                {
                    foreach (EdmMember childMember in Helper.GetAllStructuralMembers(instantiatedType))
                    { 
                        int slotNum = GetSlotIndex(member, childMember);
                        requiredSlots[slotNum] = true; 
                    } 
                }
                return; 
            }

            if (member.IsScalarType())
            { 
                // A scalar does not need anything per se to be constructed
                // unless it is referring to a field in the tree below, i.e., the THEN 
                // slot is not a constant slot 
                if (mustRequireThisSlot)
                { 
                    requiredSlots[memberSlotNum] = true;
                }
                return;
            } 

            // For an association, get the indices of the ends, e.g., 
            // CProduct and CCategory in CProductCategory1 
            if (Helper.IsAssociationType(edmType))
            { 
                // Need just it's ends
                AssociationSet associationSet = (AssociationSet)member.Extent;
                AssociationType associationType = associationSet.ElementType;
                foreach (AssociationEndMember endMember in associationType.AssociationEndMembers) 
                {
                    int slotNum = GetSlotIndex(member, endMember); 
                    requiredSlots[slotNum] = true; 
                }
                return; 
            }

            // For a reference, all we need are the keys
            RefType refType = edmType as RefType; 
            Debug.Assert(refType != null, "What other non scalars do we have? Relation end must be a reference type");
            EntityTypeBase refElementType = refType.ElementType; 
            // Go through all the members of elementType and get the key properties 

            EntitySet entitySet = MetadataHelper.GetEntitySetAtEnd((AssociationSet)member.Extent, 
                                                                   (AssociationEndMember)member.LastMember);
            foreach (EdmMember entityMember in refElementType.KeyMembers)
            {
                int slotNum = GetSlotIndex(member, entityMember); 
                requiredSlots[slotNum] = true;
            } 
            return; 
        }
        #endregion 

        #region Helper methods
        // effects: Given a slot number, 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
        private MemberPath GetMemberPath(int slotNum) 
        { 
            return ProjectedSlot.GetMemberPath(slotNum, m_projectedSlotMap, TotalSlots - m_projectedSlotMap.Count);
        } 

        // requires: "member.child" be present in m_projectedSlotMap
        // effects: Returns the index where member.child "e.g., CPerson1.pid" exists.
        // Returns -1 if no such entry found 
        private int GetSlotIndex(MemberPath member, EdmMember child)
        { 
            MemberPath fullMember = new MemberPath(member, child); 
            int index = m_projectedSlotMap.IndexOf(fullMember);
            Debug.Assert(index != -1, "Couldn't locate " + fullMember.ToString() + " in m_projectedSlotMap"); 
            return index;
        }
        #endregion
 
        #region String methods
        internal override void ToCompactString(StringBuilder builder) 
        { 
            builder.Append("View: ");
            m_view.ToCompactString(builder); 
            builder.Append("ProjectedSlotMap: ");
            m_projectedSlotMap.ToCompactString(builder);
            builder.Append("Case statements: ");
            foreach (MemberPath member in m_caseStatements.Keys) 
            {
                CaseStatement statement = m_caseStatements[member]; 
                statement.ToCompactString(builder); 
                builder.AppendLine();
            } 
        }
        #endregion
    }
} 

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

using System.Data.Common; 
using System.Data.Common.Utils;
using System.Data.Mapping.ViewGeneration.Structures;
using System.Collections.Generic;
using System.Data.Mapping.ViewGeneration.CqlGeneration; 
using System.Text;
using System.Diagnostics; 
using System.Data.Metadata.Edm; 

namespace System.Data.Mapping.ViewGeneration 
{

    // This class is responsible for generation of CQL after the cell merging
    // process has been done 
    internal class CqlGenerator : InternalBase
    { 
 
        #region Constructor
        // effects: Given the generated "view", the case statements for the 
        // multiconstant fields (caseStatements), a map that maps different
        // paths of the entityset (for which the view is being generated) to
        // slot indexes in the view, creates an object that is capable of
        // generating the Cql for "view" 
        internal CqlGenerator(CellTreeNode view, Dictionary caseStatements,
                              CqlIdentifiers identifiers, MemberPathMapBase projectedSlotMap, 
                              int numCellsInView, BoolExpression topLevelWhereClause, MetadataWorkspace workspace) 
        {
            m_view = view; 
            m_caseStatements = caseStatements;
            m_projectedSlotMap = projectedSlotMap;
            m_numBools = numCellsInView; // We have that many booleans
            m_topLevelWhereClause = topLevelWhereClause; 
            m_identifiers = identifiers;
            m_workspace = workspace; 
        } 
        #endregion
 
        #region Fields
        // The generated view from the cells (e.g., after cell merging)
        private CellTreeNode m_view;
        // Case statements for the multiconstant fields 
        private Dictionary m_caseStatements;
        // Mapping from Memberpaths to slot indexes 
        private MemberPathMapBase m_projectedSlotMap; 
        // No. of booleans in the view, one per cell (from0, from1, etc
        private int m_numBools; // CHANGE_[....]_IMPROVE: Get this from view and cache. Not as a param to constructor 
        // A counter used to generate aliases for blocks
        private int m_currentBlockNum;
        private BoolExpression m_topLevelWhereClause;
        // The prefixes for _from and block aliases (T) 
        private CqlIdentifiers m_identifiers;
        private MetadataWorkspace m_workspace; 
        #endregion 

        #region Properties 
        private int TotalSlots
        {
            get { return m_projectedSlotMap.Count + m_numBools; }
        } 
        #endregion
 
        #region CqlBlock Generation Methods for all node types 
        // effects: Returns a CQL query that represents a query/update
        // mapping view for the view information that was supplied in the constructor 
        internal string GenerateCql()
        {
            // Generate a CqlBlock tree and then convert that to Cql
            CqlBlock blockTree = GenerateCqlBlockTree(); 
            // Create the string builder with 1K so that we don't have to
            // keep growing it 
            StringBuilder builder = new StringBuilder(1024); 
            blockTree.AsCql(builder, true, 1);
            return builder.ToString(); 
        }

        // effects: Generates a CqlBlock tree that is capable of generating
        // the actual Cql strings 
        private CqlBlock GenerateCqlBlockTree()
        { 
 
            // Essentially, we create a block for each CellTreeNode in the
            // tree and then we layer case statements on top of that view -- 
            // one case statement for each multiconstant entry

            // Dertmine the slots that are projected by the whole tree. Tell
            // the children that they need to produce those slots somehow -- 
            // if they don't have it, they can produce null
            bool[] requiredSlots = GetRequiredSlots(); 
            Debug.Assert(requiredSlots.Length == TotalSlots, "Wrong number of requiredSlots"); 

            List withStatements = new List(); 
            CqlBlock viewBlock = m_view.ToCqlBlock(requiredSlots, m_identifiers, ref m_currentBlockNum, ref withStatements);

            // Handle case statements for multiconstant entries
            // Right now, we have a simplication step that removes one of the 
            // entries and adds ELSE instead
            foreach (CaseStatement statement in m_caseStatements.Values) 
            { 
                statement.Simplify();
            } 

            // Generate the case statements and get the top level block which
            // must correspond to the entity set
            CqlBlock finalViewBlock = ConstructCaseBlocks(viewBlock, withStatements); 
            return finalViewBlock;
        } 
 

        private bool[] GetRequiredSlots() 
        {
            bool[] requiredSlots = new bool[TotalSlots];
            // union all slots that are required in case statements
            foreach (CaseStatement caseStatement in m_caseStatements.Values) 
            {
                int slotNum = m_projectedSlotMap.IndexOf(caseStatement.MemberPath); 
                GetRequiredSlotsForCaseMember(slotNum, caseStatement.MemberPath, requiredSlots); 
            }
 
            // For now, make sure that all booleans are required
            // Reason: OUTER JOINs may introduce an extra CASE statement (in OpCellTreeNode.cs/GetJoinSlotInfo)
            // if a member is projected in both inputs to the join.
            // This case statement may use boolean variables that may not be marked as "required" 
            // The problem is that this decision is made _after_ CqlBlocks for children get produced (in OpCellTreeNode.cs/JoinToCqlBlock)
            for (int i = TotalSlots - m_numBools; i < TotalSlots; i++) 
            { 
                requiredSlots[i] = true;
            } 
            // Because of the above we don't need to harvest used booleans from the top-level WHERE clause
            // m_topLevelWhereClause.GetRequiredSlots(m_projectedSlotMap, requiredSlots);

            // slot value is required for case statement? 
            foreach (CaseStatement caseStatement in m_caseStatements.Values)
            { 
                if (!caseStatement.MemberPath.IsScalarType() || 
                    !caseStatement.DependsOnMemberValue)
                { 
                    requiredSlots[m_projectedSlotMap.IndexOf(caseStatement.MemberPath)] = false;
                }
            }
            return requiredSlots; 
        }
 
        #endregion 

        #region CaseStatement Block Methods 
        // effects: Given a CqlBlock tree, generates the case statements
        // blocks on top of it (using m_casestatements) and returns the
        // resulting tree
        private CqlBlock ConstructCaseBlocks(CqlBlock viewBlock, IEnumerable withStatements) 
        {
            // Get the 0th slot only, i.e., the extent 
            bool[] topSlots = new bool[TotalSlots]; 
            topSlots[0] = true;
 
            // all booleans in the top-level WHERE clause are required and get bubbled up
            // this makes some _fromX booleans be marked as 'required by parent'
            m_topLevelWhereClause.GetRequiredSlots(m_projectedSlotMap, topSlots);
            CqlBlock result = ConstructCaseBlocks(viewBlock, 0, topSlots, withStatements); 
            return result;
        } 
 
        // effects: Given a CqlBlock tree generated by the cell merging
        // process (viewBlock) and the required slots by the parent, 
        // generates the casestatement block tree starting from startSlotNum,
        // i.e., only for case statements that are beyond startSlotNum
        private CqlBlock ConstructCaseBlocks(CqlBlock viewBlock, int startSlotNum,
                                             bool[] parentRequiredSlots, IEnumerable withStatements) 
        {
            int numMembers = m_projectedSlotMap.Count; 
            // Find the next slot for which we have a case statement, i.e., 
            // which was in the multiconstants
            int foundSlot = FindNextCaseStatementSlot(startSlotNum, parentRequiredSlots, numMembers); 

            if (foundSlot == -1)
            {
                // We have bottomed out - no more slots to generate cases for 
                // Just get the base view block
                return viewBlock; 
            } 

            // Compute the requiredSlots for this member, i.e., what slots 
            // are needed to produce this member
            MemberPath thisMember = m_projectedSlotMap[foundSlot];
            bool[] thisRequiredSlots = new bool[TotalSlots];
            GetRequiredSlotsForCaseMember(foundSlot, thisMember, thisRequiredSlots); 
            Debug.Assert(thisRequiredSlots.Length == parentRequiredSlots.Length &&
                         thisRequiredSlots.Length == TotalSlots, 
                         "Number of slots in array should not vary across blocks"); 

            // Merge parent's requirements with this requirements 
            for (int i = 0; i < TotalSlots; i++)
            {
                // We do ask the children to generate the slot that we are
                // producing if it is available 
                if (parentRequiredSlots[i])
                { 
                    thisRequiredSlots[i] = true; 
                }
            } 
            // we just filled foundSlot - mark it as non-required if it produces constants only
            CaseStatement thisCaseStatement = m_caseStatements[thisMember];
            thisRequiredSlots[foundSlot] = thisCaseStatement.DependsOnMemberValue;
 
            // Recursively, determine the block tree for slots beyond
            // foundSlot. 
 
            CqlBlock childBlock = ConstructCaseBlocks(viewBlock, foundSlot + 1, thisRequiredSlots, null);
 
            // For each slot, create a SlotInfo object
            SlotInfo[] slotInfos = CreateSlotInfosForCaseStatement(parentRequiredSlots, foundSlot,
                                                                   childBlock, thisCaseStatement, withStatements);
            m_currentBlockNum++; 
            // We have a where clause only at the top level
            BoolExpression whereClause = startSlotNum == 0 ? m_topLevelWhereClause : BoolExpression.True; 
            if (startSlotNum == 0) 
            {
                // only slot #0 is required by parent; reset all 'required by parent' booleans introduced above 
                for (int i = 1; i < slotInfos.Length; i++)
                {
                    slotInfos[i].ResetIsRequiredByParent();
                } 
            }
            CaseCqlBlock result = new CaseCqlBlock(slotInfos, foundSlot, childBlock, whereClause, m_identifiers, m_currentBlockNum); 
            return result; 
        }
 
        // effects: Given the slot (foundSlot) and its corresponding case
        // statements (thisCaseStatement), generates the slotinfos for the
        // case statement block. Uses parentRequiredSlots and childblock to
        // determine if the slot is required and if it is projected by the child 
        private SlotInfo[] CreateSlotInfosForCaseStatement(bool[] parentRequiredSlots, int foundSlot,
                                                           CqlBlock childBlock, CaseStatement thisCaseStatement, 
                                                           IEnumerable withStatements ) 
        {
            int numSlotsAddedByChildBlock = childBlock.Slots.Count - TotalSlots; 
            SlotInfo[] slotInfos = new SlotInfo[TotalSlots + numSlotsAddedByChildBlock];
            for (int slotNum = 0; slotNum < TotalSlots; slotNum++)
            {
                bool isProjected = childBlock.IsProjected(slotNum); 
                bool isRequiredByParent = parentRequiredSlots[slotNum];
                ProjectedSlot slot = childBlock.ProjectedSlot(slotNum); 
                MemberPath memberPath = GetMemberPath(slotNum); 
                if (slotNum == foundSlot)
                { 
                    // We need a case statement instead for this slot that we
                    // are handling right now
                    Debug.Assert(isRequiredByParent, "Case result not needed by parent");
 
                    // Get a case statement with all slots replaced by aliases slots
                    CaseStatement newCaseStatement = thisCaseStatement.MakeCaseWithAliasedSlots(childBlock, memberPath, slotNum); 
                    slot = new CaseStatementSlot(newCaseStatement, withStatements); 
                    isProjected = true; // We are projecting this slot now
                } 
                else if (slot != null && isProjected && isRequiredByParent)
                {
                    // We only alias something that is needed and is being
                    // projected by the child 
                    // It is an aliased slot into the child block
                    slot = new AliasedSlot(childBlock, slot, memberPath, slotNum); 
                } 
                // For slots, if it is not required by the parent, we want to
                // set the isRequiredByParent for this slot to be 
                // false. Furthermore, we do not want to introduce any "NULL
                // AS something" at this stage for slots not being
                // projected. So if the child does not project that slot, we
                // declare it as not being required by the parent (if such a 
                // NULL was needed, it would have been pushed all the way
                // down to a non-case block. 
                // Essentially, from a Case statement's parent perspective, 
                // it is saying "If you can produce a slot either by yourself
                // or your children, please do. Otherwise, do not concoct anything" 
                SlotInfo slotInfo = new SlotInfo(isRequiredByParent && isProjected, isProjected,
                                                 slot, memberPath);
                slotInfos[slotNum] = slotInfo;
            } 
            for (int i = TotalSlots; i < TotalSlots + numSlotsAddedByChildBlock; i++)
            { 
                SlotInfo slotInfo = childBlock.Slots[i]; 
                AliasedSlot childAddedSlot = new AliasedSlot(childBlock, slotInfo.SlotValue, slotInfo.MemberPath, i);
                slotInfos[i] = new SlotInfo(true, true, childAddedSlot, childAddedSlot.MemberPath); 
            }
            return slotInfos;
        }
 
        // effects: Returns the next slot starting at startSlotNum that is present in
        // the case statements 
        private int FindNextCaseStatementSlot(int startSlotNum, bool[] parentRequiredSlots, int numMembers) 
        {
            int foundSlot = -1; 
            // Simply go through the slots and check the m_caseStatements map
            for (int slotNum = startSlotNum; slotNum < numMembers; slotNum++)
            {
                MemberPath member = m_projectedSlotMap[slotNum]; 
                if (parentRequiredSlots[slotNum] && m_caseStatements.ContainsKey(member))
                { 
                    foundSlot = slotNum; 
                    break;
                } 
            }
            return foundSlot;
        }
 
        // requires: member is part of m_caseStatements
        // effects: Returns an array of size TotalSlots which indicates the 
        // slots that are needed to constuct "member", e.g., CPerson may need 
        // pid and name (say slots 2 and 5 -- then bools[2] and bools[5] will
        // be true 
        private void GetRequiredSlotsForCaseMember(int memberSlotNum, MemberPath member, bool[] requiredSlots)
        {
            Debug.Assert(true == m_caseStatements.ContainsKey(member),
                         "Constructing case for regular field?"); 
            Debug.Assert(requiredSlots.Length == TotalSlots, "Invalid array size for populating required slots");
            CaseStatement statement = m_caseStatements[member]; 
 
            // Find the required slots from the when then clause conditions
            // and values 
            bool mustRequireThisSlot = false;
            foreach (CaseStatement.WhenThen clause in statement.Clauses)
            {
                clause.Condition.GetRequiredSlots(m_projectedSlotMap, requiredSlots); 
                ProjectedSlot slot = clause.Value;
                if (false == (slot is ConstantSlot)) 
                { 
                    // If this slot is a scalar and a non-constant,
                    // we need the lower down blocks to generate it for us 
                    mustRequireThisSlot = true;
                }
            }
 
            EdmType edmType = member.EdmType;
            bool isTypeMember = Helper.IsEntityType(edmType) || Helper.IsComplexType(edmType); 
            //// Non-scalar field 
            if (isTypeMember)
            { 
                foreach (EdmType instantiatedType in statement.InstantiatedTypes)
                {
                    foreach (EdmMember childMember in Helper.GetAllStructuralMembers(instantiatedType))
                    { 
                        int slotNum = GetSlotIndex(member, childMember);
                        requiredSlots[slotNum] = true; 
                    } 
                }
                return; 
            }

            if (member.IsScalarType())
            { 
                // A scalar does not need anything per se to be constructed
                // unless it is referring to a field in the tree below, i.e., the THEN 
                // slot is not a constant slot 
                if (mustRequireThisSlot)
                { 
                    requiredSlots[memberSlotNum] = true;
                }
                return;
            } 

            // For an association, get the indices of the ends, e.g., 
            // CProduct and CCategory in CProductCategory1 
            if (Helper.IsAssociationType(edmType))
            { 
                // Need just it's ends
                AssociationSet associationSet = (AssociationSet)member.Extent;
                AssociationType associationType = associationSet.ElementType;
                foreach (AssociationEndMember endMember in associationType.AssociationEndMembers) 
                {
                    int slotNum = GetSlotIndex(member, endMember); 
                    requiredSlots[slotNum] = true; 
                }
                return; 
            }

            // For a reference, all we need are the keys
            RefType refType = edmType as RefType; 
            Debug.Assert(refType != null, "What other non scalars do we have? Relation end must be a reference type");
            EntityTypeBase refElementType = refType.ElementType; 
            // Go through all the members of elementType and get the key properties 

            EntitySet entitySet = MetadataHelper.GetEntitySetAtEnd((AssociationSet)member.Extent, 
                                                                   (AssociationEndMember)member.LastMember);
            foreach (EdmMember entityMember in refElementType.KeyMembers)
            {
                int slotNum = GetSlotIndex(member, entityMember); 
                requiredSlots[slotNum] = true;
            } 
            return; 
        }
        #endregion 

        #region Helper methods
        // effects: Given a slot number, 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
        private MemberPath GetMemberPath(int slotNum) 
        { 
            return ProjectedSlot.GetMemberPath(slotNum, m_projectedSlotMap, TotalSlots - m_projectedSlotMap.Count);
        } 

        // requires: "member.child" be present in m_projectedSlotMap
        // effects: Returns the index where member.child "e.g., CPerson1.pid" exists.
        // Returns -1 if no such entry found 
        private int GetSlotIndex(MemberPath member, EdmMember child)
        { 
            MemberPath fullMember = new MemberPath(member, child); 
            int index = m_projectedSlotMap.IndexOf(fullMember);
            Debug.Assert(index != -1, "Couldn't locate " + fullMember.ToString() + " in m_projectedSlotMap"); 
            return index;
        }
        #endregion
 
        #region String methods
        internal override void ToCompactString(StringBuilder builder) 
        { 
            builder.Append("View: ");
            m_view.ToCompactString(builder); 
            builder.Append("ProjectedSlotMap: ");
            m_projectedSlotMap.ToCompactString(builder);
            builder.Append("Case statements: ");
            foreach (MemberPath member in m_caseStatements.Keys) 
            {
                CaseStatement statement = m_caseStatements[member]; 
                statement.ToCompactString(builder); 
                builder.AppendLine();
            } 
        }
        #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