Protect everything in #if NET_2_0.
[mono.git] / mcs / class / Mono.C5 / Builder.cs
1 #if NET_2_0\r
2 /*\r
3  Copyright (c) 2003-2004 Niels Kokholm <kokholm@itu.dk> and Peter Sestoft <sestoft@dina.kvl.dk>\r
4  Permission is hereby granted, free of charge, to any person obtaining a copy\r
5  of this software and associated documentation files (the "Software"), to deal\r
6  in the Software without restriction, including without limitation the rights\r
7  to use, copy, modify, merge, publish, distribute, sublicense, and/or sell\r
8  copies of the Software, and to permit persons to whom the Software is\r
9  furnished to do so, subject to the following conditions:\r
10  \r
11  The above copyright notice and this permission notice shall be included in\r
12  all copies or substantial portions of the Software.\r
13  \r
14  THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\r
15  IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\r
16  FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\r
17  AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\r
18  LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\r
19  OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\r
20  SOFTWARE.\r
21 */\r
22 \r
23 using C5;\r
24 using System;\r
25 using System.Reflection;\r
26 using System.Reflection.Emit;\r
27 using System.Diagnostics;\r
28 namespace C5.ComparerBuilder\r
29 {\r
30     /// <summary>\r
31     /// A default item comparer for an item type that is either generic (IComparable&lt;T&gt;)\r
32     /// or ordinarily (System.IComparable) comparable.\r
33     /// </summary>\r
34     public class FromComparable<T>\r
35     {\r
36         static Type naturalComparerO = typeof(NaturalComparerO<>);\r
37 \r
38         static Type naturalComparer = typeof(NaturalComparer<>);\r
39 \r
40 \r
41         /// <summary>\r
42         /// Create a default comparer\r
43         /// <exception cref="ArgumentException"/> if T is not comparable. \r
44         /// </summary>\r
45         /// <returns>The comparer</returns>\r
46         [Tested]\r
47         public static IComparer<T> Examine()\r
48         {\r
49             Type t = typeof(T);\r
50 \r
51             if (t.Equals(typeof(int)))\r
52                 return (IComparer<T>)(new IC());\r
53 \r
54             if (typeof(IComparable<T>).IsAssignableFrom(t))\r
55             {\r
56                 Type c = naturalComparer.BindGenericParameters(new Type[] { t });\r
57 \r
58                 return (IComparer<T>)(c.GetConstructor(System.Type.EmptyTypes).Invoke(null));\r
59             }\r
60 \r
61             if (t.GetInterface("System.IComparable") != null)\r
62             {\r
63                 Type c = naturalComparerO.BindGenericParameters(new Type[] { t });\r
64 \r
65                 return (IComparer<T>)(c.GetConstructor(System.Type.EmptyTypes).Invoke(null));\r
66             }\r
67 \r
68             throw new ArgumentException(String.Format("Cannot make IComparer<{0}>", t));\r
69         }\r
70     }\r
71 }\r
72 namespace C5.HasherBuilder\r
73 {\r
74     /// <summary>\r
75     /// Prototype for an sequenced hasher for IIndexed[W]\r
76     /// This will use the IIndexed[W] specific operations\r
77     /// </summary>\r
78     public class SequencedHasher<S, W> : IHasher<S>\r
79         where S : ISequenced<W>\r
80     {\r
81         /// <summary>\r
82         /// Get the hash code with respect to this sequenced hasher\r
83         /// </summary>\r
84         /// <param name="item">The item</param>\r
85         /// <returns>The hash code</returns>\r
86         [Tested]\r
87         public int GetHashCode(S item) { return item.GetHashCode(); }\r
88 \r
89 \r
90         /// <summary>\r
91         /// Check if two items are equal with respect to this sequenced hasher\r
92         /// </summary>\r
93         /// <param name="i1">first item</param>\r
94         /// <param name="i2">second item</param>\r
95         /// <returns>True if equal</returns>\r
96         [Tested]\r
97         public bool Equals(S i1, S i2) { return i1 == null ? i2 == null : i1.Equals(i2); }\r
98     }\r
99 \r
100 \r
101 \r
102     /// <summary>\r
103     /// Prototype for an unsequenced hasher for ICollection[W]\r
104     /// This will use the ICollection[W] specific operations\r
105     /// </summary>\r
106     public class UnsequencedHasher<S, W> : IHasher<S>\r
107         where S : ICollection<W>\r
108     {\r
109         /// <summary>\r
110         /// Get the hash code with respect to this unsequenced hasher\r
111         /// </summary>\r
112         /// <param name="item">The item</param>\r
113         /// <returns>The hash code</returns>\r
114         [Tested]\r
115         public int GetHashCode(S item) { return item.GetHashCode(); }\r
116 \r
117 \r
118         /// <summary>\r
119         /// Check if two items are equal with respect to this unsequenced hasher\r
120         /// </summary>\r
121         /// <param name="i1">first item</param>\r
122         /// <param name="i2">second item</param>\r
123         /// <returns>True if equal</returns>\r
124         [Tested]\r
125         public bool Equals(S i1, S i2) { return i1 == null ? i2 == null : i1.Equals(i2); }\r
126     }\r
127 \r
128 \r
129 \r
130     /// <summary>\r
131     /// Create a hasher for T that is DefaultValueTypeHasher[T] \r
132     /// or DefaultReferenceTypeHasher[T] unless T has been \r
133     /// instatiated to a type of the exact form IIndexed[W] or ICollection[W]\r
134     /// in which case Examine will return Sequenced- repectively UnsequencedHasher.\r
135     /// </summary>\r
136     public class ByPrototype<T>\r
137     {\r
138         static Type isequenced = typeof(ISequenced<>);\r
139 \r
140         static Type ieditable = typeof(ICollection<>);\r
141 \r
142         static Type orderedhasher = typeof(HasherBuilder.SequencedHasher<,>);\r
143 \r
144         static Type unorderedhasher = typeof(HasherBuilder.UnsequencedHasher<,>);\r
145 /*\r
146         static Type isequenced = Type.GetType("C5.ISequenced");\r
147 \r
148         static Type ieditable = Type.GetType("C5.ICollection");\r
149 \r
150         static Type orderedhasher = Type.GetType("C5.HasherBuilder.SequencedHasher");\r
151 \r
152         static Type unorderedhasher = Type.GetType("C5.HasherBuilder.UnsequencedHasher");\r
153 */\r
154 \r
155         /// <summary>\r
156         /// See class description\r
157         /// </summary>\r
158         /// <returns>The hasher</returns>\r
159         [Tested]\r
160         public static IHasher<T> Examine()\r
161         {\r
162             Type t = typeof(T);\r
163 \r
164             if (!t.HasGenericArguments)\r
165             {\r
166                 if (t.Equals(typeof(int)))\r
167                     return (IHasher<T>)(new IntHasher());\r
168                 else if (t.IsValueType)\r
169                     return new DefaultValueTypeHasher<T>();\r
170                 else\r
171                     return new DefaultReferenceTypeHasher<T>();\r
172             }\r
173 \r
174             Type s = t.GetGenericTypeDefinition();\r
175             Type[] v = t.GetGenericArguments();\r
176             Type b;\r
177 \r
178             if (s.Equals(isequenced))\r
179                 b = orderedhasher;\r
180             else if (s.Equals(ieditable))\r
181                 b = unorderedhasher;\r
182             else if (t.IsValueType)\r
183                 return new DefaultValueTypeHasher<T>();\r
184             else\r
185                 return new DefaultReferenceTypeHasher<T>();\r
186 \r
187             Type c = b.BindGenericParameters(new Type[] { t, v[0] });\r
188 \r
189             return (IHasher<T>)(c.GetConstructor(System.Type.EmptyTypes).Invoke(null));\r
190         }\r
191     }\r
192 \r
193 \r
194 #if !EXPERIMENTAL\r
195 \r
196     /// <summary>\r
197     /// IHasher factory class: examines at instatiation time if T is an\r
198     /// interface implementing "int GetHashCode()" and "bool Equals(T)".\r
199     /// If those are not present, MakeHasher will return a default hasher,\r
200     /// else this class will implement Ihasher[T] via Invoke() on the\r
201     /// reflected method infos.\r
202     /// </summary>\r
203     public class ByInvoke<T> : IHasher<T>\r
204     {\r
205         internal static readonly System.Reflection.MethodInfo hinfo, einfo;\r
206 \r
207 \r
208         static ByInvoke()\r
209         {\r
210             Type t = typeof(T);\r
211 \r
212             if (!t.IsInterface) return;\r
213 \r
214             BindingFlags f = BindingFlags.Public | BindingFlags.Instance;\r
215 \r
216             hinfo = t.GetMethod("GetHashCode", f, null, new Type[0], null);\r
217             einfo = t.GetMethod("Equals", f, null, new Type[1] { t }, null);\r
218         }\r
219 \r
220 \r
221         private ByInvoke() { }\r
222 \r
223 /// <summary>\r
224 /// \r
225 /// </summary>\r
226 /// <returns></returns>\r
227         public static IHasher<T> MakeHasher()\r
228         {\r
229             if (hinfo != null && einfo != null)\r
230                 return new ByInvoke<T>();\r
231             else\r
232                 return new DefaultReferenceTypeHasher<T>();\r
233         }\r
234 \r
235 /// <summary>\r
236 /// \r
237 /// </summary>\r
238 /// <param name="item"></param>\r
239 /// <returns></returns>\r
240         public int GetHashCode(T item)\r
241         {\r
242             return (int)(hinfo.Invoke(item, null));\r
243         }\r
244 \r
245 /// <summary>\r
246 /// \r
247 /// </summary>\r
248 /// <param name="i1"></param>\r
249 /// <param name="i2"></param>\r
250 /// <returns></returns>\r
251         public bool Equals(T i1, T i2)\r
252         {\r
253             return (bool)(einfo.Invoke(i1, new object[1] { i2 }));\r
254         }\r
255     }\r
256 \r
257 \r
258 \r
259     /// <summary>\r
260     /// Like ByInvoke, but tries to build a hasher by RTCG to\r
261     /// avoid the Invoke() overhead. Does not work as intended \r
262     /// because of a Whidbey RTCG bug.\r
263     /// </summary>\r
264     public class ByRTCG\r
265     {\r
266         private static ModuleBuilder moduleBuilder;\r
267 \r
268         private static AssemblyBuilder assemblyBuilder;\r
269 \r
270         private static int uid = 0;\r
271 \r
272 \r
273         /// <summary>\r
274         /// \r
275         /// </summary>\r
276         /// <param name="hinfo"></param>\r
277         /// <param name="einfo"></param>\r
278         /// <returns></returns>\r
279         public static /*ObjectHasher */ IHasher<T> CreateHasher<T>(MethodInfo hinfo, MethodInfo einfo)\r
280         {\r
281             if (moduleBuilder == null)\r
282             {\r
283                 string assmname = "LeFake";\r
284                 string filename = assmname + ".dll";\r
285                 AssemblyName assemblyName = new AssemblyName("LeFake");\r
286                 AppDomain appdomain = AppDomain.CurrentDomain;\r
287                 AssemblyBuilderAccess acc = AssemblyBuilderAccess.RunAndSave;\r
288 \r
289                 assemblyBuilder = appdomain.DefineDynamicAssembly(assemblyName, acc);\r
290                 moduleBuilder = assemblyBuilder.DefineDynamicModule(assmname, filename);\r
291             }\r
292 \r
293             Type t = typeof(/*object*/ T);\r
294             Type o_t = typeof(object);\r
295             Type h_t = typeof(/*ObjectHasher*/ IHasher<T>);\r
296             Type i_t = typeof(int);\r
297             //TODO: protect uid for thread safety!\r
298             string name = "C5.Dynamic.Hasher_" + uid++;\r
299             TypeAttributes tatt = TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.Sealed;\r
300             TypeBuilder tb = moduleBuilder.DefineType(name, tatt, o_t, new Type[1] { h_t });\r
301             MethodAttributes matt = MethodAttributes.Public | MethodAttributes.Virtual;\r
302             MethodBuilder mb = tb.DefineMethod("GetHashCode", matt, i_t, new Type[1] { t });\r
303             ILGenerator ilg = mb.GetILGenerator();\r
304 \r
305             ilg.Emit(OpCodes.Ldarg_1);\r
306             ilg.Emit(OpCodes.Callvirt, hinfo);\r
307             ilg.Emit(OpCodes.Ret);\r
308             mb = tb.DefineMethod("Equals", matt, typeof(bool), new Type[2] { t, t });\r
309             ilg = mb.GetILGenerator();\r
310             ilg.Emit(OpCodes.Ldarg_1);\r
311             ilg.Emit(OpCodes.Ldarg_2);\r
312             ilg.Emit(OpCodes.Callvirt, einfo);\r
313             ilg.Emit(OpCodes.Ret);\r
314 \r
315             Type hasher_t = tb.CreateType();\r
316             object hasher = hasher_t.GetConstructor(new Type[0]).Invoke(null);\r
317 \r
318             return (IHasher<T>)hasher;\r
319         }\r
320 \r
321 /// <summary>\r
322 /// \r
323 /// </summary>\r
324 /// <typeparam name="T"></typeparam>\r
325 /// <returns></returns>\r
326         public static IHasher<T> build<T>()\r
327         {\r
328             MethodInfo hinfo = ByInvoke<T>.hinfo, einfo = ByInvoke<T>.einfo;\r
329 \r
330             if (hinfo != null && einfo != null)\r
331                 return CreateHasher<T>(hinfo, einfo);\r
332             else\r
333                 return ByPrototype<T>.Examine();\r
334         }\r
335 \r
336 /// <summary>\r
337 /// \r
338 /// </summary>\r
339         public void dump()\r
340         {\r
341             assemblyBuilder.Save("LeFake.dll");\r
342         }\r
343     }\r
344 #endif\r
345 }\r
346 #endif\r