Updates referencesource to .NET 4.7
[mono.git] / mcs / class / referencesource / System.Data.Entity / System / Data / Mapping / ViewGeneration / Structures / MemberProjectionIndex.cs
1 //---------------------------------------------------------------------
2 // <copyright file="MemberProjectionIndex.cs" company="Microsoft">
3 //      Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //
6 // @owner Microsoft
7 // @backupOwner Microsoft
8 //---------------------------------------------------------------------
9
10 using System.Collections.Generic;
11 using System.Text;
12 using System.Data.Common.Utils;
13 using System.Data.Common;
14 using System.Data.Mapping.ViewGeneration.Structures;
15 using System.Diagnostics;
16 using System.Data.Metadata.Edm;
17
18 namespace System.Data.Mapping.ViewGeneration.Structures
19 {
20     /// <summary>
21     /// Manages <see cref="MemberPath"/>s of the members of the types stored in an extent.
22     /// This is a bi-directional dictionary of <see cref="MemberPath"/>s to integer indexes and back.
23     /// </summary>
24     internal sealed class MemberProjectionIndex : InternalBase
25     {
26         #region Fields
27         private readonly Dictionary<MemberPath, int> m_indexMap;
28         private readonly List<MemberPath> m_members;
29         #endregion
30
31         #region Constructor/Factory
32         /// <summary>
33         /// Recursively generates <see cref="MemberPath"/>s for the members of the types stored in the <paramref name="extent"/>.
34         /// </summary>
35         internal static MemberProjectionIndex Create(EntitySetBase extent, EdmItemCollection edmItemCollection)
36         {
37             // We generate the indices for the projected slots as we traverse the metadata.
38             MemberProjectionIndex index = new MemberProjectionIndex();
39             GatherPartialSignature(index, edmItemCollection, new MemberPath(extent), false); // need not only keys
40             return index;
41         }
42
43         /// <summary>
44         /// Creates an empty index.
45         /// </summary>
46         private MemberProjectionIndex()
47         {
48             m_indexMap = new Dictionary<MemberPath, int>(MemberPath.EqualityComparer);
49             m_members = new List<MemberPath>();
50         }
51         #endregion
52
53         #region Properties
54         internal int Count
55         {
56             get { return m_members.Count; }
57         }
58
59         internal MemberPath this[int index]
60         {
61             get { return m_members[index]; }
62         }
63
64         /// <summary>
65         /// Returns the indexes of the key slots corresponding to fields in this for which IsPartOfKey is true.
66         /// </summary>
67         internal IEnumerable<int> KeySlots
68         {
69             get
70             {
71                 List<int> result = new List<int>();
72                 for (int slotNum = 0; slotNum < Count; slotNum++)
73                 {
74                     // We pass for numboolslots since we know that this is not a
75                     // bool slot
76                     if (this.IsKeySlot(slotNum, 0))
77                     {
78                         result.Add(slotNum);
79                     }
80                 }
81                 return result;
82             }
83         }
84
85         /// <summary>
86         /// Returns an enumeration of all members
87         /// </summary>
88         internal IEnumerable<MemberPath> Members
89         {
90             get { return m_members; }
91         }
92         #endregion
93
94         #region Methods
95         /// <summary>
96         /// Returns a non-negative index of the <paramref name="member"/> if found, otherwise -1.
97         /// </summary>
98         internal int IndexOf(MemberPath member)
99         {
100             int index;
101             if (m_indexMap.TryGetValue(member, out index))
102             {
103                 return index;
104             }
105             else
106             {
107                 return -1;
108             }
109         }
110
111         /// <summary>
112         /// If an index already exists for member, this is a no-op. Else creates the next index available for member and returns it.
113         /// </summary>
114         internal int CreateIndex(MemberPath member)
115         {
116             int index;
117             if (false == m_indexMap.TryGetValue(member, out index))
118             {
119                 index = m_indexMap.Count;
120                 m_indexMap[member] = index;
121                 m_members.Add(member);
122             }
123             return index;
124         }
125
126         /// <summary>
127         /// Given the <paramref name="slotNum"/>, returns the output member path that this slot contributes/corresponds to in the extent view.
128         /// If the slot corresponds to one of the boolean variables, returns null.
129         /// </summary>
130         internal MemberPath GetMemberPath(int slotNum, int numBoolSlots)
131         {
132             MemberPath result = this.IsBoolSlot(slotNum, numBoolSlots) ? null : this[slotNum];
133             return result;
134         }
135
136         /// <summary>
137         /// Given the index of a boolean variable (e.g., of from1), returns the slot number for that boolean in this.
138         /// </summary>
139         internal int BoolIndexToSlot(int boolIndex, int numBoolSlots)
140         {
141             // Booleans appear after the regular slots
142             Debug.Assert(boolIndex >= 0 && boolIndex < numBoolSlots, "No such boolean in this node");
143             return this.Count + boolIndex;
144         }
145
146         /// <summary>
147         /// Given the <paramref name="slotNum"/> corresponding to a boolean slot, returns the cell number that the cell corresponds to.
148         /// </summary>
149         internal int SlotToBoolIndex(int slotNum, int numBoolSlots)
150         {
151             Debug.Assert(slotNum < this.Count + numBoolSlots && slotNum >= this.Count, "No such boolean slot");
152             return slotNum - this.Count;
153         }
154
155         /// <summary>
156         /// Returns true if <paramref name="slotNum"/> corresponds to a key slot in the output extent view.
157         /// </summary>
158         internal bool IsKeySlot(int slotNum, int numBoolSlots)
159         {
160             Debug.Assert(slotNum < this.Count + numBoolSlots, "No such slot in tree");
161             return slotNum < this.Count && this[slotNum].IsPartOfKey;
162         }
163
164         /// <summary>
165         /// Returns true if <paramref name="slotNum"/> corresponds to a bool slot and not a regular field.
166         /// </summary>
167         internal bool IsBoolSlot(int slotNum, int numBoolSlots)
168         {
169             Debug.Assert(slotNum < this.Count + numBoolSlots, "Boolean slot does not exist in tree");
170             return slotNum >= this.Count;
171         }
172
173         internal override void ToCompactString(StringBuilder builder)
174         {
175             builder.Append('<');
176             StringUtil.ToCommaSeparatedString(builder, m_members);
177             builder.Append('>');
178         }
179         #endregion
180
181         #region Signature construction
182         /// <summary>
183         /// Starting at the <paramref name="member"/>, recursively generates <see cref="MemberPath"/>s for the fields embedded in it.
184         /// </summary>
185         /// <param name="member">corresponds to a value of an Entity or Complex or Association type</param>
186         /// <param name="needKeysOnly">indicates whether we need to only collect members that are keys</param>
187         private static void GatherPartialSignature(MemberProjectionIndex index, EdmItemCollection edmItemCollection, MemberPath member, bool needKeysOnly)
188         {
189             EdmType memberType = member.EdmType;
190             ComplexType complexTypemember = memberType as ComplexType;
191             Debug.Assert(complexTypemember != null ||
192                          memberType is EntityType || // for entity sets
193                          memberType is AssociationType || // For association sets
194                          memberType is RefType, // for association ends
195                          "GatherPartialSignature can be called only for complex types, entity sets, association ends");
196
197             if (memberType is ComplexType && needKeysOnly)
198             {
199                 // Check if the complex type needs to be traversed or not. If not, just return 
200                 // from here. Else we need to continue to the code below. Right now, we do not
201                 // allow keys inside complex types
202                 return;
203             }
204
205             // Make sure that this member is in the slot map before any of its embedded objects.
206             index.CreateIndex(member);
207
208             // Consider each possible type value -- each type value conributes to a tuple in the result.
209             // For that possible type, add all the type members into the signature.
210             foreach (EdmType possibleType in MetadataHelper.GetTypeAndSubtypesOf(memberType, edmItemCollection, false /*includeAbstractTypes*/))
211             {
212                 StructuralType possibleStructuralType = possibleType as StructuralType;
213                 Debug.Assert(possibleStructuralType != null, "Non-structural subtype?");
214
215                 GatherSignatureFromTypeStructuralMembers(index, edmItemCollection, member, possibleStructuralType, needKeysOnly);
216             }
217         }
218
219         /// <summary>
220         /// Given the <paramref name="member"/> and one of its <paramref name="possibleType"/>s, determine the attributes that are relevant
221         /// for this <paramref name="possibleType"/> and return a <see cref="MemberPath"/> signature corresponding to the <paramref name="possibleType"/> and the attributes.
222         /// If <paramref name="needKeysOnly"/>=true, collect the key fields only.
223         /// </summary>
224         /// <param name="possibleType">the <paramref name="member"/>'s type or one of its subtypes</param>
225         private static void GatherSignatureFromTypeStructuralMembers(MemberProjectionIndex index,
226                                                                      EdmItemCollection edmItemCollection,
227                                                                      MemberPath member, 
228                                                                      StructuralType possibleType, 
229                                                                      bool needKeysOnly)
230         {
231             // For each child member of this type, collect all the relevant scalar fields
232             foreach (EdmMember structuralMember in Helper.GetAllStructuralMembers(possibleType))
233             {
234                 if (MetadataHelper.IsNonRefSimpleMember(structuralMember))
235                 {
236                     if (!needKeysOnly || MetadataHelper.IsPartOfEntityTypeKey(structuralMember))
237                     {
238                         MemberPath nonStructuredMember = new MemberPath(member, structuralMember);
239                         // Note: scalarMember's parent has already been added to the projectedSlotMap
240                         index.CreateIndex(nonStructuredMember);
241                     }
242                 }
243                 else
244                 {
245                     Debug.Assert(structuralMember.TypeUsage.EdmType is ComplexType ||
246                                  structuralMember.TypeUsage.EdmType is RefType, // for association ends
247                                  "Only non-scalars expected - complex types, association ends");
248
249                     
250
251                     MemberPath structuredMember = new MemberPath(member, structuralMember);
252                     GatherPartialSignature(index, 
253                                            edmItemCollection, 
254                                            structuredMember,
255                                            // Only keys are required for entities referenced by association ends of an association.
256                                            needKeysOnly || Helper.IsAssociationEndMember(structuralMember));
257                 }
258             }
259         }
260         #endregion
261     }
262 }