//--------------------------------------------------------------------- // // Copyright (c) Microsoft Corporation. All rights reserved. // // // @owner [....] // @backupOwner [....] //--------------------------------------------------------------------- namespace System.Data.Mapping.ViewGeneration.Structures { using System.Collections.Generic; using System.Data.Common.CommandTrees; using System.Data.Common.Utils; using System.Data.Mapping.ViewGeneration.CqlGeneration; using System.Diagnostics; using System.Text; /// /// This class represents the constants or members that that can be referenced in a C or S Cell query. /// In addition to fields, may represent constants such as types of fields, booleans, etc. /// internal abstract class ProjectedSlot : InternalBase, IEquatable { internal static readonly IEqualityComparer EqualityComparer = new Comparer(); #region Virtual members /// /// Returns true if this is semantically equivalent to . /// protected virtual bool IsEqualTo(ProjectedSlot right) { return base.Equals(right); } protected virtual int GetHash() { return base.GetHashCode(); } public bool Equals(ProjectedSlot right) { return EqualityComparer.Equals(this, right); } public override bool Equals(object obj) { ProjectedSlot right = obj as ProjectedSlot; if (obj == null) { return false; } return Equals(right); } public override int GetHashCode() { return EqualityComparer.GetHashCode(this); } /// /// Creates new that is qualified with .CqlAlias. /// If current slot is composite (such as , then this method recursively qualifies all parts /// and returns a new deeply qualified slot (as opposed to ). /// internal virtual ProjectedSlot DeepQualify(CqlBlock block) { QualifiedSlot result = new QualifiedSlot(block, this); return result; } /// /// Returns the alias corresponding to the slot based on the , e.g., "CPerson1_pid". /// Derived classes may override this behavior and produce aliases that don't depend on . /// internal virtual string GetCqlFieldAlias(MemberPath outputMember) { return outputMember.CqlFieldAlias; } /// /// Given the slot and the , generates eSQL corresponding to the slot. /// If slot is a qualified slot, is ignored. Returns the modified . /// /// outputMember is non-null if this slot is not a constant slot /// indicates the appropriate indentation level (method can ignore it) internal abstract StringBuilder AsEsql(StringBuilder builder, MemberPath outputMember, string blockAlias, int indentLevel); /// /// Given the slot and the input , generates CQT corresponding to the slot. /// internal abstract DbExpression AsCqt(DbExpression row, MemberPath outputMember); #endregion #region Other Methods /// /// Given fields in and , remap and merge them. /// internal static bool TryMergeRemapSlots(ProjectedSlot[] slots1, ProjectedSlot[] slots2, out ProjectedSlot[] result) { // First merge them and then remap them ProjectedSlot[] mergedSlots; if (!TryMergeSlots(slots1, slots2, out mergedSlots)) { result = null; return false; } result = mergedSlots; return true; } /// /// Given two lists and , merge them and returnthe resulting slots, /// i.e., empty slots from one are overridden by the slots from the other. /// private static bool TryMergeSlots(ProjectedSlot[] slots1, ProjectedSlot[] slots2, out ProjectedSlot[] slots) { Debug.Assert(slots1.Length == slots2.Length, "Merged slots of two cells must be same size"); slots = new ProjectedSlot[slots1.Length]; for (int i = 0; i < slots.Length; i++) { ProjectedSlot slot1 = slots1[i]; ProjectedSlot slot2 = slots2[i]; if (slot1 == null) { slots[i] = slot2; } else if (slot2 == null) { slots[i] = slot1; } else { // Both slots are non-null: Either both are the same // members or one of them is a constant // Note: if both are constants (even different constants) // it does not matter which one we pick because the CASE statement will override it MemberProjectedSlot memberSlot1 = slot1 as MemberProjectedSlot; MemberProjectedSlot memberSlot2 = slot2 as MemberProjectedSlot; if (memberSlot1 != null && memberSlot2 != null && false == EqualityComparer.Equals(memberSlot1, memberSlot2)) { // Illegal combination of slots; non-constant fields disagree return false; } // If one of them is a field we have to get the field ProjectedSlot pickedSlot = (memberSlot1 != null) ? slot1 : slot2; slots[i] = pickedSlot; } } return true; } #endregion #region Comparer class /// /// A class that can compare slots based on their contents. /// private sealed class Comparer : IEqualityComparer { /// /// Returns true if and are semantically equivalent. /// public bool Equals(ProjectedSlot left, ProjectedSlot right) { // Quick check with references if (object.ReferenceEquals(left, right)) { // Gets the Null and Undefined case as well return true; } // One of them is non-null at least. So if the other one is // null, we cannot be equal if (left == null || right == null) { return false; } // Both are non-null at this point return left.IsEqualTo(right); } public int GetHashCode(ProjectedSlot key) { EntityUtil.CheckArgumentNull(key, "key"); return key.GetHash(); } } #endregion } }