2003-12-17 Zoltan Varga <vargaz@freemail.hu>
[mono.git] / mcs / class / corlib / System.Reflection / Binder.cs
1 // System.Reflection.Binder
2 //
3 // Sean MacIsaac (macisaac@ximian.com)
4 // Paolo Molaro (lupus@ximian.com)
5 //
6 // (C) Ximian, Inc. 2001 - 2002
7
8 using System.Globalization;
9 using System.Runtime.InteropServices;
10
11 namespace System.Reflection
12 {
13         [Serializable]
14         [ClassInterface(ClassInterfaceType.AutoDual)]
15         public abstract class Binder
16         {
17                 protected Binder () {}
18
19                 public abstract FieldInfo BindToField (BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo culture);
20                 public abstract MethodBase BindToMethod (BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] names, out object state);
21                 public abstract object ChangeType (object value, Type type, CultureInfo culture);
22                 public abstract void ReorderArgumentArray( ref object[] args, object state);
23                 public abstract MethodBase SelectMethod (BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers);
24                 public abstract PropertyInfo SelectProperty( BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, Type[] indexes, ParameterModifier[] modifiers);
25
26                 static Binder default_binder;
27
28                 internal static Binder DefaultBinder {
29                         get {
30                                 if (null == default_binder)
31                                 {
32                                         lock (typeof (Binder)) \r
33                                         {
34                                                 if (default_binder == null)
35                                                         default_binder = new Default ();
36
37                                                 return default_binder;
38                                         }
39                                 }
40
41                                 return default_binder;
42                         }
43                 }
44                 
45                 internal static bool ConvertArgs (Binder binder, object[] args, ParameterInfo[] pinfo, CultureInfo culture) {
46                         if (args == null) {
47                                 if ( pinfo.Length == 0)
48                                         return true;
49                                 else
50                                         throw new TargetParameterCountException ();
51                         }
52                         if (pinfo.Length != args.Length)
53                                 throw new TargetParameterCountException ();
54                         for (int i = 0; i < args.Length; ++i) {
55                                 object v = binder.ChangeType (args [i], pinfo[i].ParameterType, culture);
56                                 if ((v == null) && (args [i] != null))
57                                         return false;
58                                 args [i] = v;
59                         }
60                         return true;
61                 }
62
63                 internal static int GetDerivedLevel (Type type) \r
64                 {\r
65                         Type searchType = type;\r
66                         int level = 1;\r
67 \r
68                         while (searchType.BaseType != null) \r
69                         {\r
70                                 level++;\r
71                                 searchType = searchType.BaseType;\r
72                         }\r
73 \r
74                         return level;\r
75                 }\r
76 \r
77                 internal static MethodBase FindMostDerivedMatch (MethodBase [] match) \r
78                 {\r
79                         int highLevel = 0;\r
80                         int matchId = -1;\r
81                         int count = match.Length;\r
82 \r
83                         for (int current = 0; current < count; current++) \r
84                         {\r
85                                 int level = GetDerivedLevel (match[current].DeclaringType);\r
86                                 if (level == highLevel)\r
87                                         throw new AmbiguousMatchException ();\r
88 \r
89                                 if (level > highLevel) \r
90                                 {\r
91                                         highLevel = level;\r
92                                         matchId = current;\r
93                                 }\r
94                         }\r
95 \r
96                         return match[matchId];\r
97                 }\r
98
99                 internal sealed class Default : Binder {
100                         public override FieldInfo BindToField (BindingFlags bindingAttr, FieldInfo[] match, object value, CultureInfo culture) 
101                         {
102                                 if (match == null)
103                                         throw new ArgumentNullException ("match");
104                                 foreach (FieldInfo f in match) {
105                                         if (check_type (value.GetType (), f.FieldType))
106                                                 return f;
107                                 }
108                                 return null;
109                         }
110
111                         [MonoTODO]
112                         public override MethodBase BindToMethod (BindingFlags bindingAttr, MethodBase[] match, ref object[] args, ParameterModifier[] modifiers, CultureInfo culture, string[] names, out object state)
113                         {
114                                 Type[] types;
115                                 if (args == null)
116                                         types = Type.EmptyTypes;
117                                 else {
118                                         types = new Type [args.Length];
119                                         for (int i = 0; i < args.Length; ++i) {
120                                                 if (args [i] != null)
121                                                         types [i] = args [i].GetType ();
122                                         }
123                                 }
124                                 MethodBase selected = SelectMethod (bindingAttr, match, types, modifiers);
125                                 state = null;
126                                 return selected;
127                         }
128
129                         static bool IsArrayAssignable (Type object_type, Type target_type)
130                         {
131                                 if (object_type.IsArray && target_type.IsArray)
132                                         return IsArrayAssignable (object_type.GetElementType (), target_type.GetElementType ());
133                                                 
134                                 if (target_type.IsAssignableFrom (object_type))
135                                         return true;
136
137                                 return false;
138                         }
139                         
140                         public override object ChangeType (object value, Type type, CultureInfo culture)
141                         {
142                                 if (value == null)
143                                         return null;
144                                 Type vtype = value.GetType ();
145                                 if (vtype == type || type.IsAssignableFrom (vtype))
146                                         return value;
147                                 if (vtype.IsArray && type.IsArray){
148                                         if (IsArrayAssignable (vtype.GetElementType (), type.GetElementType ()))
149                                                 return value;
150                                 }
151
152                                 if (check_type (vtype, type))
153                                         return Convert.ChangeType (value, type);
154                                 return null;
155                         }
156
157                         [MonoTODO]
158                         public override void ReorderArgumentArray (ref object[] args, object state)
159                         {
160                                 //do nothing until we support named arguments
161                                 //throw new NotImplementedException ();
162                         }
163
164                         private static bool check_type (Type from, Type to) {
165                                 if (from == to)
166                                         return true;
167                                 TypeCode fromt = Type.GetTypeCode (from);
168                                 TypeCode tot = Type.GetTypeCode (to);
169
170                                 switch (fromt) {
171                                 case TypeCode.Char:
172                                         switch (tot) {
173                                         case TypeCode.UInt16:
174                                         case TypeCode.UInt32:
175                                         case TypeCode.Int32:
176                                         case TypeCode.UInt64:
177                                         case TypeCode.Int64:
178                                         case TypeCode.Single:
179                                         case TypeCode.Double:
180                                                 return true;
181                                         }
182                                         return to == typeof (object);
183                                 case TypeCode.Byte:
184                                         switch (tot) {
185                                         case TypeCode.Char:
186                                         case TypeCode.UInt16:
187                                         case TypeCode.Int16:
188                                         case TypeCode.UInt32:
189                                         case TypeCode.Int32:
190                                         case TypeCode.UInt64:
191                                         case TypeCode.Int64:
192                                         case TypeCode.Single:
193                                         case TypeCode.Double:
194                                                 return true;
195                                         }
196                                         return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
197                                 case TypeCode.SByte:
198                                         switch (tot) {
199                                         case TypeCode.Int16:
200                                         case TypeCode.Int32:
201                                         case TypeCode.Int64:
202                                         case TypeCode.Single:
203                                         case TypeCode.Double:
204                                                 return true;
205                                         }
206                                         return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
207                                 case TypeCode.UInt16:
208                                         switch (tot) {
209                                         case TypeCode.UInt32:
210                                         case TypeCode.Int32:
211                                         case TypeCode.UInt64:
212                                         case TypeCode.Int64:
213                                         case TypeCode.Single:
214                                         case TypeCode.Double:
215                                                 return true;
216                                         }
217                                         return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
218                                 case TypeCode.Int16:
219                                         switch (tot) {
220                                         case TypeCode.Int32:
221                                         case TypeCode.Int64:
222                                         case TypeCode.Single:
223                                         case TypeCode.Double:
224                                                 return true;
225                                         }
226                                         return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
227                                 case TypeCode.UInt32:
228                                         switch (tot) {
229                                         case TypeCode.UInt64:
230                                         case TypeCode.Int64:
231                                         case TypeCode.Single:
232                                         case TypeCode.Double:
233                                                 return true;
234                                         }
235                                         return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
236                                 case TypeCode.Int32:
237                                         switch (tot) {
238                                         case TypeCode.Int64:
239                                         case TypeCode.Single:
240                                         case TypeCode.Double:
241                                                 return true;
242                                         }
243                                         return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
244                                 case TypeCode.UInt64:
245                                 case TypeCode.Int64:
246                                         switch (tot) {
247                                         case TypeCode.Single:
248                                         case TypeCode.Double:
249                                                 return true;
250                                         }
251                                         return to == typeof (object) || (from.IsEnum && to == typeof (Enum));
252                                 case TypeCode.Single:
253                                         return tot == TypeCode.Double || to == typeof (object);
254                                 default:
255                                         /* TODO: handle valuetype -> byref */
256                                         if (to == typeof (object) && from.IsValueType)
257                                                 return true;
258
259                                         return to.IsAssignableFrom (from);
260                                 }
261                         }
262
263                         private static bool check_arguments (Type[] types, ParameterInfo[] args) {
264                                 for (int i = 0; i < types.Length; ++i) {
265                                         if (!check_type (types [i], args [i].ParameterType))
266                                                 return false;
267                                 }
268                                 return true;
269                         }
270
271                         public override MethodBase SelectMethod (BindingFlags bindingAttr, MethodBase[] match, Type[] types, ParameterModifier[] modifiers)
272                         {
273                                 MethodBase m;
274                                 int i, j;
275                                 if (match == null)
276                                         throw new ArgumentNullException ("match");
277                                 /* first look for an exact match... */
278                                 for (i = 0; i < match.Length; ++i) {
279                                         m = match [i];
280                                         ParameterInfo[] args = m.GetParameters ();
281                                         if (args.Length != types.Length)
282                                                 continue;
283                                         for (j = 0; j < types.Length; ++j) {
284                                                 if (types [j] != args [j].ParameterType)
285                                                         break;
286                                         }
287                                         if (j == types.Length)
288                                                 return m;
289                                 }
290                                 for (i = 0; i < match.Length; ++i) {
291                                         m = match [i];
292                                         ParameterInfo[] args = m.GetParameters ();
293                                         if (args.Length != types.Length)
294                                                 continue;
295                                         if (!check_arguments (types, args))
296                                                 continue;
297                                         return m;
298                                 }
299                                 return null;
300                         }
301
302                         public override PropertyInfo SelectProperty (BindingFlags bindingAttr, PropertyInfo[] match, Type returnType, Type[] indexes, ParameterModifier[] modifiers)
303                         {
304                                 if (match == null)
305                                         throw new ArgumentNullException ("match");
306                                 foreach (PropertyInfo m in match) {
307                                         ParameterInfo[] args = m.GetIndexParameters ();
308                                         if (args.Length != indexes.Length)
309                                                 continue;
310                                         if (!check_arguments (indexes, args))
311                                                 continue;
312                                         return m;
313                                 }
314                                 return null;
315                         }
316                 }
317         }
318 }