second step.
[mono.git] / mcs / class / System.Data.Linq / src / DbLinq / Data / Linq / Sugar / Implementation / DataMapper.cs
1 #region MIT license\r
2 // \r
3 // MIT license\r
4 //\r
5 // Copyright (c) 2007-2008 Jiri Moudry, Pascal Craponne\r
6 // \r
7 // Permission is hereby granted, free of charge, to any person obtaining a copy\r
8 // of this software and associated documentation files (the "Software"), to deal\r
9 // in the Software without restriction, including without limitation the rights\r
10 // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
11 // copies of the Software, and to permit persons to whom the Software is\r
12 // furnished to do so, subject to the following conditions:\r
13 // \r
14 // The above copyright notice and this permission notice shall be included in\r
15 // all copies or substantial portions of the Software.\r
16 // \r
17 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
18 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
19 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
20 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
21 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
22 // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\r
23 // THE SOFTWARE.\r
24 // \r
25 #endregion\r
26 \r
27 using System;\r
28 using System.Collections.Generic;\r
29 using System.Collections.ObjectModel;\r
30 using System.Data.Linq.Mapping;\r
31 using System.Linq;\r
32 using System.Reflection;\r
33 #if MONO_STRICT\r
34 using System.Data.Linq.Sugar;\r
35 using System.Data.Linq.Sugar.Expressions;\r
36 #else\r
37 using DbLinq.Data.Linq.Sugar.Expressions;\r
38 #endif\r
39 \r
40 #if MONO_STRICT\r
41 namespace System.Data.Linq.Sugar.Implementation\r
42 #else\r
43 namespace DbLinq.Data.Linq.Sugar.Implementation\r
44 #endif\r
45 {\r
46     internal class DataMapper : IDataMapper\r
47     {\r
48         /// <summary>\r
49         /// Returns a table given a type, or null if the type is not mapped\r
50         /// </summary>\r
51         /// <param name="tableType"></param>\r
52         /// <param name="dataContext"></param>\r
53         /// <returns></returns>\r
54         public virtual string GetTableName(Type tableType, DataContext dataContext)\r
55         {\r
56             var tableDescription = dataContext.Mapping.GetTable(tableType);\r
57             if (tableDescription != null)\r
58                 return tableDescription.TableName;\r
59             return null;\r
60         }\r
61 \r
62         public virtual string GetColumnName(TableExpression tableExpression, MemberInfo memberInfo, DataContext dataContext)\r
63         {\r
64             return GetColumnName(tableExpression.Type, memberInfo, dataContext);\r
65         }\r
66 \r
67         public virtual string GetColumnName(Type tableType, MemberInfo memberInfo, DataContext dataContext)\r
68         {\r
69             var tableDescription = dataContext.Mapping.GetTable(tableType);\r
70             var columnDescription = tableDescription.RowType.GetDataMember(memberInfo);\r
71             if (columnDescription != null)\r
72                 return columnDescription.MappedName;\r
73             return null;\r
74         }\r
75 \r
76         public virtual IList<MemberInfo> GetPrimaryKeys(TableExpression tableExpression, DataContext dataContext)\r
77         {\r
78             var tableDescription = dataContext.Mapping.GetTable(tableExpression.Type);\r
79             if (tableDescription != null)\r
80                 return GetPrimaryKeys(tableDescription);\r
81             return null;\r
82         }\r
83 \r
84         public virtual IList<MemberInfo> GetPrimaryKeys(MetaTable tableDescription)\r
85         {\r
86             return (from column in tableDescription.RowType.IdentityMembers select column.Member).ToList();\r
87         }\r
88 \r
89         /// <summary>\r
90         /// Lists table mapped columns\r
91         /// </summary>\r
92         /// <param name="tableDescription"></param>\r
93         /// <returns></returns>\r
94         public IList<MemberInfo> GetColumns(MetaTable tableDescription)\r
95         {\r
96             return (from column in tableDescription.RowType.PersistentDataMembers select column.Member).ToList();\r
97         }\r
98 \r
99         /// <summary>\r
100         /// Returns association definition, if any\r
101         /// </summary>\r
102         /// <param name="thisTableExpression">The table referenced by the assocation (the type holding the member)</param>\r
103         /// <param name="memberInfo">The memberInfo related to association</param>\r
104         /// <param name="thisKey">The keys in the joined table</param>\r
105         /// <param name="otherKey">The keys in the associated table</param>\r
106         /// <param name="joinType"></param>\r
107         /// <param name="joinID"></param>\r
108         /// <param name="dataContext"></param>\r
109         /// <returns></returns>\r
110         public virtual Type GetAssociation(TableExpression thisTableExpression, MemberInfo memberInfo,\r
111                                            out IList<MemberInfo> thisKey, out IList<MemberInfo> otherKey,\r
112                                            out TableJoinType joinType, out string joinID, DataContext dataContext)\r
113         {\r
114             var thisTableDescription = dataContext.Mapping.GetTable(thisTableExpression.Type);\r
115             var thisAssociation =\r
116                 (from association in thisTableDescription.RowType.Associations\r
117                  where association.ThisMember.Member == memberInfo\r
118                  select association).SingleOrDefault();\r
119             if (thisAssociation != null)\r
120             {\r
121                 // by default, join is inner\r
122                 joinType = TableJoinType.Inner;\r
123                 joinID = thisAssociation.ThisMember.MappedName;\r
124                 if (string.IsNullOrEmpty(joinID))\r
125                     throw Error.BadArgument("S0108: Association name is required to ensure join uniqueness");\r
126 \r
127                 var otherType = thisAssociation.ThisMember.Type;\r
128                 if (otherType.IsGenericType) // TODO: something serious here\r
129                     otherType = otherType.GetGenericArguments()[0];\r
130 \r
131                 var otherTableDescription = dataContext.Mapping.GetTable(otherType);\r
132                 bool thisKeyHasNullables, otherKeyHasNullables;\r
133                 thisKey = GetAssociationKeys(thisTableDescription, thisAssociation.ThisKey, dataContext, out thisKeyHasNullables);\r
134                 otherKey = GetAssociationKeys(otherTableDescription, thisAssociation.OtherKey, dataContext, out otherKeyHasNullables);\r
135 \r
136                 // we just test here the left join (since associations are symmetric,\r
137                 //        we can only find left joins here, and the otherKeyHasNullables is\r
138                 //        always equal to thisKeyHasNullables)\r
139                 if (thisKeyHasNullables)\r
140                     joinType |= TableJoinType.LeftOuter;\r
141 \r
142                 return otherType;\r
143             }\r
144             thisKey = null;\r
145             otherKey = null;\r
146             joinType = TableJoinType.Default;\r
147             joinID = null;\r
148             return null;\r
149         }\r
150 \r
151         /// <summary>\r
152         /// Enumerates Keys for a given table.\r
153         /// Keys can be provided as input. If none provided, PKs are taken from table\r
154         /// </summary>\r
155         /// <param name="description"></param>\r
156         /// <param name="keys">Keys to be used, leave empty to use PKs instead</param>\r
157         /// <param name="dataContext"></param>\r
158         /// <param name="hasNullableKeys">returned as true if some keys can be null (we then have an outer join)</param>\r
159         /// <returns></returns>\r
160         protected virtual IList<MemberInfo> GetAssociationKeys(MetaTable description, ReadOnlyCollection<MetaDataMember> keys,\r
161                                                                DataContext dataContext, out bool hasNullableKeys)\r
162         {\r
163             var sourceKeys = keys;\r
164             if (sourceKeys.Count == 0)\r
165                 sourceKeys = description.RowType.IdentityMembers;\r
166 \r
167             hasNullableKeys = false;\r
168             var members = new List<MemberInfo>();\r
169             foreach (var sourceKey in sourceKeys)\r
170             {\r
171                 members.Add(sourceKey.Member);\r
172                 if (sourceKey.CanBeNull)\r
173                     hasNullableKeys = true;\r
174             }\r
175             return members;\r
176         }\r
177 \r
178         public IList<MemberInfo> GetEntitySetAssociations(Type type)\r
179         {\r
180             return type.GetProperties()\r
181                 .Where(p => p.PropertyType.IsGenericType &&\r
182                     p.PropertyType.GetGenericTypeDefinition() == typeof(System.Data.Linq.EntitySet<>) &&\r
183                     p.IsDefined(typeof(AssociationAttribute), true))\r
184                 .Cast<MemberInfo>().ToList();\r
185         }\r
186 \r
187         public IList<MemberInfo> GetEntityRefAssociations(Type type)\r
188         {\r
189             return (from p in type.GetProperties()\r
190                     let associationAttribute = p.GetCustomAttributes(typeof(AssociationAttribute), true).FirstOrDefault() as AssociationAttribute\r
191                     let field = type.GetField(associationAttribute != null ? (associationAttribute.Storage ?? string.Empty) : string.Empty, BindingFlags.NonPublic | BindingFlags.Instance)\r
192                     where associationAttribute != null &&\r
193                              field != null &&\r
194                             field.FieldType.IsGenericType &&\r
195                             field.FieldType.GetGenericTypeDefinition() == typeof(System.Data.Linq.EntityRef<>)\r
196                     select p)\r
197                 .Cast<MemberInfo>().ToList();\r
198         }\r
199     }\r
200 }\r