1 //---------------------------------------------------------------------
2 // <copyright file="FunctionImportMapping.ReturnTypeRanameMapping.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;
11 using System.Collections.Generic;
12 using System.Linq;
13 using System.Text;
14 using System.Data.Metadata.Edm;
15 using System.Data.Common.Utils;
16 using System.Xml;
17 using System.Collections.ObjectModel;
18 using System.Diagnostics;
19 
20 namespace System.Data.Mapping
21 {
22     internal abstract class FunctionImportStructuralTypeMapping
23     {
24         internal readonly LineInfo LineInfo;
25         internal readonly Collection<FunctionImportReturnTypePropertyMapping> ColumnsRenameList;
26 
FunctionImportStructuralTypeMapping(Collection<FunctionImportReturnTypePropertyMapping> columnsRenameList, LineInfo lineInfo)27         internal FunctionImportStructuralTypeMapping(Collection<FunctionImportReturnTypePropertyMapping> columnsRenameList, LineInfo lineInfo)
28         {
29             this.ColumnsRenameList = columnsRenameList;
30             this.LineInfo = lineInfo;
31         }
32     }
33 
34     internal sealed class FunctionImportEntityTypeMapping : FunctionImportStructuralTypeMapping
35     {
FunctionImportEntityTypeMapping(IEnumerable<EntityType> isOfTypeEntityTypes, IEnumerable<EntityType> entityTypes, IEnumerable<FunctionImportEntityTypeMappingCondition> conditions, Collection<FunctionImportReturnTypePropertyMapping> columnsRenameList, LineInfo lineInfo)36         internal FunctionImportEntityTypeMapping(IEnumerable<EntityType> isOfTypeEntityTypes,
37             IEnumerable<EntityType> entityTypes, IEnumerable<FunctionImportEntityTypeMappingCondition> conditions,
38             Collection<FunctionImportReturnTypePropertyMapping> columnsRenameList,
39             LineInfo lineInfo)
40             : base(columnsRenameList, lineInfo)
41         {
42             this.IsOfTypeEntityTypes = new ReadOnlyCollection<EntityType>(
43                 EntityUtil.CheckArgumentNull(isOfTypeEntityTypes, "isOfTypeEntityTypes").ToList());
44             this.EntityTypes = new ReadOnlyCollection<EntityType>(
45                 EntityUtil.CheckArgumentNull(entityTypes, "entityTypes").ToList());
46             this.Conditions = new ReadOnlyCollection<FunctionImportEntityTypeMappingCondition>(
47                 EntityUtil.CheckArgumentNull(conditions, "conditions").ToList());
48         }
49 
50         internal readonly ReadOnlyCollection<FunctionImportEntityTypeMappingCondition> Conditions;
51         internal readonly ReadOnlyCollection<EntityType> EntityTypes;
52         internal readonly ReadOnlyCollection<EntityType> IsOfTypeEntityTypes;
53 
54         /// <summary>
55         /// Gets all (concrete) entity types implied by this type mapping.
56         /// </summary>
GetMappedEntityTypes(ItemCollection itemCollection)57         internal IEnumerable<EntityType> GetMappedEntityTypes(ItemCollection itemCollection)
58         {
59             const bool includeAbstractTypes = false;
60             return this.EntityTypes.Concat(
61                 this.IsOfTypeEntityTypes.SelectMany(entityType =>
62                     MetadataHelper.GetTypeAndSubtypesOf(entityType, itemCollection, includeAbstractTypes)
63                     .Cast<EntityType>()));
64         }
65 
GetDiscriminatorColumns()66         internal IEnumerable<String> GetDiscriminatorColumns()
67         {
68             return this.Conditions.Select(condition => condition.ColumnName);
69         }
70     }
71 
72     internal sealed class FunctionImportComplexTypeMapping : FunctionImportStructuralTypeMapping
73     {
74         internal readonly ComplexType ReturnType;
75 
FunctionImportComplexTypeMapping(ComplexType returnType, Collection<FunctionImportReturnTypePropertyMapping> columnsRenameList, LineInfo lineInfo)76         internal FunctionImportComplexTypeMapping(ComplexType returnType, Collection<FunctionImportReturnTypePropertyMapping> columnsRenameList, LineInfo lineInfo)
77             : base(columnsRenameList, lineInfo)
78         {
79             this.ReturnType = returnType;
80         }
81     }
82 
83     internal abstract class FunctionImportReturnTypePropertyMapping
84     {
85         internal readonly string CMember;
86         internal readonly string SColumn;
87         internal readonly LineInfo LineInfo;
88 
FunctionImportReturnTypePropertyMapping(string cMember, string sColumn, LineInfo lineInfo)89         internal FunctionImportReturnTypePropertyMapping(string cMember, string sColumn, LineInfo lineInfo)
90         {
91             this.CMember = cMember;
92             this.SColumn = sColumn;
93             this.LineInfo = lineInfo;
94         }
95     }
96 
97     internal sealed class FunctionImportReturnTypeScalarPropertyMapping : FunctionImportReturnTypePropertyMapping
98     {
FunctionImportReturnTypeScalarPropertyMapping(string cMember, string sColumn, LineInfo lineInfo)99         internal FunctionImportReturnTypeScalarPropertyMapping(string cMember, string sColumn, LineInfo lineInfo)
100             : base(cMember, sColumn, lineInfo)
101         {
102         }
103     }
104 
105     /// <summary>
106     /// extract the column rename info from polymorphic entity type mappings
107     /// </summary>
108     internal sealed class FunctionImportReturnTypeEntityTypeColumnsRenameBuilder
109     {
110         /// <summary>
111         /// CMember -> SMember*
112         /// </summary>
113         internal Dictionary<string, FunctionImportReturnTypeStructuralTypeColumnRenameMapping> ColumnRenameMapping;
114 
FunctionImportReturnTypeEntityTypeColumnsRenameBuilder( Dictionary<EntityType, Collection<FunctionImportReturnTypePropertyMapping>> isOfTypeEntityTypeColumnsRenameMapping, Dictionary<EntityType, Collection<FunctionImportReturnTypePropertyMapping>> entityTypeColumnsRenameMapping)115         internal FunctionImportReturnTypeEntityTypeColumnsRenameBuilder(
116             Dictionary<EntityType, Collection<FunctionImportReturnTypePropertyMapping>> isOfTypeEntityTypeColumnsRenameMapping,
117             Dictionary<EntityType, Collection<FunctionImportReturnTypePropertyMapping>> entityTypeColumnsRenameMapping)
118         {
119             EntityUtil.CheckArgumentNull(isOfTypeEntityTypeColumnsRenameMapping, "isOfTypeEntityTypeColumnsRenameMapping");
120             EntityUtil.CheckArgumentNull(entityTypeColumnsRenameMapping, "entityTypeColumnsRenameMapping");
121 
122             this.ColumnRenameMapping = new Dictionary<string, FunctionImportReturnTypeStructuralTypeColumnRenameMapping>();
123 
124             // Assign the columns renameMapping to the result dictionary.
125             foreach (EntityType entityType in isOfTypeEntityTypeColumnsRenameMapping.Keys)
126             {
127                 this.SetStructuralTypeColumnsRename(
128                     entityType, isOfTypeEntityTypeColumnsRenameMapping[entityType], true/*isTypeOf*/);
129             }
130 
131             foreach (EntityType entityType in entityTypeColumnsRenameMapping.Keys)
132             {
133                 this.SetStructuralTypeColumnsRename(
134                     entityType, entityTypeColumnsRenameMapping[entityType], false/*isTypeOf*/);
135             }
136         }
137 
138         /// <summary>
139         /// Set the column mappings for each defaultMemberName.
140         /// </summary>
SetStructuralTypeColumnsRename( EntityType entityType, Collection<FunctionImportReturnTypePropertyMapping> columnsRenameMapping, bool isTypeOf)141         private void SetStructuralTypeColumnsRename(
142             EntityType entityType,
143             Collection<FunctionImportReturnTypePropertyMapping> columnsRenameMapping,
144             bool isTypeOf)
145         {
146             EntityUtil.CheckArgumentNull(entityType, "entityType");
147             EntityUtil.CheckArgumentNull(columnsRenameMapping, "columnsRenameMapping");
148 
149             foreach (var mapping in columnsRenameMapping)
150             {
151                 if (!this.ColumnRenameMapping.Keys.Contains(mapping.CMember))
152                 {
153                     this.ColumnRenameMapping[mapping.CMember] = new FunctionImportReturnTypeStructuralTypeColumnRenameMapping(mapping.CMember);
154                 }
155                 this.ColumnRenameMapping[mapping.CMember].AddRename(new FunctionImportReturnTypeStructuralTypeColumn(mapping.SColumn, entityType, isTypeOf, mapping.LineInfo));
156             }
157         }
158     }
159 
160     internal sealed class FunctionImportReturnTypeStructuralTypeColumn
161     {
162         internal readonly StructuralType Type;
163         internal readonly bool IsTypeOf;
164         internal readonly string ColumnName;
165         internal readonly LineInfo LineInfo;
166 
FunctionImportReturnTypeStructuralTypeColumn(string columnName, StructuralType type, bool isTypeOf, LineInfo lineInfo)167         internal FunctionImportReturnTypeStructuralTypeColumn(string columnName, StructuralType type, bool isTypeOf, LineInfo lineInfo)
168         {
169             this.ColumnName = columnName;
170             this.IsTypeOf = isTypeOf;
171             this.Type = type;
172             this.LineInfo = lineInfo;
173         }
174     }
175 
176     internal class FunctionImportReturnTypeStructuralTypeColumnRenameMapping
177     {
178         private Collection<FunctionImportReturnTypeStructuralTypeColumn> _columnListForType;
179         private Collection<FunctionImportReturnTypeStructuralTypeColumn> _columnListForIsTypeOfType;
180         /// <summary>
181         /// Null if default mapping is not allowed.
182         /// </summary>
183         private readonly string _defaultMemberName;
184         private Memoizer<StructuralType, FunctionImportReturnTypeStructuralTypeColumn> _renameCache;
185 
FunctionImportReturnTypeStructuralTypeColumnRenameMapping(string defaultMemberName)186         internal FunctionImportReturnTypeStructuralTypeColumnRenameMapping(string defaultMemberName)
187         {
188             this._defaultMemberName = defaultMemberName;
189             this._columnListForType = new Collection<FunctionImportReturnTypeStructuralTypeColumn>();
190             this._columnListForIsTypeOfType = new Collection<FunctionImportReturnTypeStructuralTypeColumn>();
191             this._renameCache = new Memoizer<StructuralType, FunctionImportReturnTypeStructuralTypeColumn>(
192                     this.GetRename, EqualityComparer<StructuralType>.Default);
193         }
194 
195         /// <summary>
196         /// <see cref="GetRename(EdmType, out IXmlLineInfo)"/> for more info.
197         /// </summary>
GetRename(EdmType type)198         internal string GetRename(EdmType type)
199         {
200             IXmlLineInfo lineInfo;
201             return GetRename(type, out lineInfo);
202         }
203 
204         /// <summary>
205         /// A default mapping (property "Foo" maps by convention to column "Foo"), if allowed, has the lowest precedence.
206         /// A mapping for a specific type (EntityType="Bar") takes precedence over a mapping for a hierarchy (EntityType="IsTypeOf(Bar)"))
207         /// If there are two hierarchy mappings, the most specific mapping takes precedence.
208         /// For instance, given the types Base, Derived1 : Base, and Derived2 : Derived1,
209         /// w.r.t. Derived1 "IsTypeOf(Derived1)" takes precedence over "IsTypeOf(Base)" when you ask for the rename of Derived1
210         /// </summary>
211         /// <param name="lineInfo">Empty for default rename mapping.</param>
GetRename(EdmType type, out IXmlLineInfo lineInfo)212         internal string GetRename(EdmType type, out IXmlLineInfo lineInfo)
213         {
214             Debug.Assert(type is StructuralType, "we can only rename structural type");
215             EntityUtil.CheckArgumentNull(type, "type");
216 
217             var rename = this._renameCache.Evaluate(type as StructuralType);
218             lineInfo = rename.LineInfo;
219             return rename.ColumnName;
220         }
221 
GetRename(StructuralType typeForRename)222         private FunctionImportReturnTypeStructuralTypeColumn GetRename(StructuralType typeForRename)
223         {
224             FunctionImportReturnTypeStructuralTypeColumn ofTypecolumn = _columnListForType.FirstOrDefault(t => t.Type == typeForRename);
225             if (null != ofTypecolumn)
226             {
227                 return ofTypecolumn;
228             }
229 
230             // if there are duplicate istypeof mapping defined rename for the same column, the last one wins
231             FunctionImportReturnTypeStructuralTypeColumn isOfTypeColumn = _columnListForIsTypeOfType.Where(t => t.Type == typeForRename).LastOrDefault();
232 
233             if (null != isOfTypeColumn)
234             {
235                 return isOfTypeColumn;
236             }
237             else
238             {
239                 // find out all the tyes that is isparent type of this lookup type
240                 IEnumerable<FunctionImportReturnTypeStructuralTypeColumn> nodesInBaseHierachy =
241                     _columnListForIsTypeOfType.Where(t => t.Type.IsAssignableFrom(typeForRename));
242 
243                 if (nodesInBaseHierachy.Count() == 0)
244                 {
245                     // non of its parent is renamed, so it will take the default one
246                     return new FunctionImportReturnTypeStructuralTypeColumn(this._defaultMemberName, typeForRename, false, null);
247                 }
248                 else
249                 {
250                     // we will guarantee that there will be some mapping for us on this column
251                     // find out which one is lowest on the link
252                     return GetLowestParentInHierachy(nodesInBaseHierachy);
253                 }
254             }
255         }
256 
GetLowestParentInHierachy(IEnumerable<FunctionImportReturnTypeStructuralTypeColumn> nodesInHierachy)257         private FunctionImportReturnTypeStructuralTypeColumn GetLowestParentInHierachy(IEnumerable<FunctionImportReturnTypeStructuralTypeColumn> nodesInHierachy)
258         {
259             FunctionImportReturnTypeStructuralTypeColumn lowestParent = null;
260             foreach (var node in nodesInHierachy)
261             {
262                 if (lowestParent == null)
263                 {
264                     lowestParent = node;
265                 }
266                 else if (lowestParent.Type.IsAssignableFrom(node.Type))
267                 {
268                     lowestParent = node;
269                 }
270             }
271             Debug.Assert(null != lowestParent, "We should have the lowest parent");
272             return lowestParent;
273         }
274 
AddRename(FunctionImportReturnTypeStructuralTypeColumn renamedColumn)275         internal void AddRename(FunctionImportReturnTypeStructuralTypeColumn renamedColumn)
276         {
277             EntityUtil.CheckArgumentNull(renamedColumn, "renamedColumn");
278 
279             if (!renamedColumn.IsTypeOf)
280             {
281                 // add to collection if the mapping is for specific type
282                 this._columnListForType.Add(renamedColumn);
283             }
284             else
285             {
286                 _columnListForIsTypeOfType.Add(renamedColumn);
287             }
288         }
289     }
290 }
291