2006-01-04 Chris Toshok <toshok@ximian.com>
[mono.git] / mcs / class / System.Web.Services / System.Web.Services.Protocols / LogicalMethodInfo.cs
1 // \r
2 // System.Web.Services.Protocols.LogicalMethodInfo.cs\r
3 //\r
4 // Authors:\r
5 //   Miguel de Icaza (miguel@ximian.com)\r
6 //   Tim Coleman (tim@timcoleman.com)\r
7 //   Lluis Sanchez Gual (lluis@ximian.com)\r
8 //\r
9 // Copyright (C) Tim Coleman, 2002\r
10 // Copyright (C) Ximian, Inc,  2003\r
11 //\r
12 // TODO:\r
13 //    BeginInvoke, EndInvoke are missing.\r
14 //    AsyncResultParameter\r
15 //\r
16 // WILD GUESS:\r
17 //   The reason for this class is so that it can cluster method/begin/end methods\r
18 //   together, as the begin/end methods in generated files from WSDL does *NOT*\r
19 //   contain all the information required to make a request.\r
20 //\r
21 //   Either that, or the Begin*/End* versions probe the attributes on the regular\r
22 //   method (which seems simpler). \r
23 //\r
24
25 //
26 // Permission is hereby granted, free of charge, to any person obtaining
27 // a copy of this software and associated documentation files (the
28 // "Software"), to deal in the Software without restriction, including
29 // without limitation the rights to use, copy, modify, merge, publish,
30 // distribute, sublicense, and/or sell copies of the Software, and to
31 // permit persons to whom the Software is furnished to do so, subject to
32 // the following conditions:
33 // 
34 // The above copyright notice and this permission notice shall be
35 // included in all copies or substantial portions of the Software.
36 // 
37 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
38 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
39 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
40 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
41 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
42 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
43 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
44 //
45 \r
46 using System.Reflection;\r
47 using System.Collections;\r
48 using System.Text;\r
49 using System.Web.Services;\r
50 \r
51 namespace System.Web.Services.Protocols {\r
52         public sealed class LogicalMethodInfo {\r
53                 #region Fields\r
54 \r
55                 MethodInfo method_info, end_method_info;\r
56                 ParameterInfo [] parameters;\r
57                 ParameterInfo [] out_parameters;\r
58                 ParameterInfo [] in_parameters;\r
59                 WebMethodAttribute attribute;\r
60 \r
61                 #endregion // Fields.\r
62                 \r
63                 #region Constructors\r
64         \r
65                 public LogicalMethodInfo (MethodInfo method_info)\r
66                 {\r
67                         if (method_info == null)\r
68                                 throw new ArgumentNullException ("method_info should be non-null");\r
69                         if (method_info.IsStatic)\r
70                                 throw new InvalidOperationException ("method is static");\r
71                         \r
72                         this.method_info = method_info;\r
73                 }\r
74 \r
75                 //\r
76                 // Only an internal contructor, called from "Create"\r
77                 //\r
78                 LogicalMethodInfo (MethodInfo method_info, MethodInfo end_method_info)\r
79                 {\r
80                         if (method_info == null)\r
81                                 throw new ArgumentNullException ("method_info should be non-null");\r
82                         if (method_info.IsStatic)\r
83                                 throw new InvalidOperationException ("method is static");\r
84                         \r
85                         this.end_method_info = end_method_info;\r
86                 }\r
87                 \r
88                 #endregion // Constructors\r
89 \r
90                 #region Properties\r
91 \r
92                 //\r
93                 // Signatures for Begin/End methods:\r
94                 //\r
95                 //        public System.IAsyncResult BeginHelloWorld(ARG1, ARG2, System.AsyncCallback callback, object asyncState) {\r
96                 //        public string EndHelloWorld(System.IAsyncResult asyncResult) {\r
97 \r
98                 public ParameterInfo AsyncCallbackParameter {\r
99                         get {\r
100                                 ParameterInfo [] pi = method_info.GetParameters ();\r
101                                 return pi [pi.Length-2];\r
102                         }\r
103                 }\r
104 \r
105                 public ParameterInfo AsyncResultParameter {\r
106                         get {\r
107                                 ParameterInfo [] pi = end_method_info.GetParameters ();\r
108                                 return pi [pi.Length-1];\r
109                         }\r
110                 }\r
111 \r
112                 public ParameterInfo AsyncStateParameter {\r
113                         get {\r
114                                 ParameterInfo [] pi = method_info.GetParameters ();\r
115                                 return pi [pi.Length-1];\r
116                         }\r
117                 }\r
118 \r
119                 public MethodInfo BeginMethodInfo {\r
120                         get {\r
121                                 if (IsBeginMethod (method_info))\r
122                                         return method_info;\r
123                                 return null;\r
124                         }\r
125                 }\r
126 \r
127                 public ICustomAttributeProvider CustomAttributeProvider {\r
128                         get {\r
129                                 return method_info;\r
130                         }\r
131                 }\r
132 \r
133                 public Type DeclaringType {\r
134                         get {\r
135                                 return method_info.DeclaringType;\r
136                         }\r
137                 }\r
138 \r
139                 public MethodInfo EndMethodInfo {\r
140                         get {\r
141                                 return end_method_info;\r
142                         }\r
143                 }\r
144 \r
145                 public ParameterInfo[] InParameters {\r
146                         get {\r
147                                 if (parameters == null)\r
148                                         ComputeParameters ();\r
149                                 return in_parameters;\r
150                         }\r
151                 }\r
152 \r
153                 public bool IsAsync {\r
154                         get {\r
155                                 return end_method_info != null;\r
156                         }\r
157                 }\r
158 \r
159                 public bool IsVoid {\r
160                         get {\r
161                                 return ReturnType == typeof (void);\r
162                         }\r
163                 }\r
164 \r
165                 public MethodInfo MethodInfo {\r
166                         get {\r
167                                 return method_info;\r
168                         }\r
169                 }\r
170 \r
171                 public string Name {\r
172                         get {\r
173                                 return method_info.Name;\r
174                         }\r
175                 }\r
176 \r
177                 void ComputeParameters ()\r
178                 {\r
179                         ParameterInfo[] pars = method_info.GetParameters ();\r
180                         if (IsAsync)\r
181                         {\r
182                                 parameters = new ParameterInfo [pars.Length - 2];\r
183                                 Array.Copy (pars, 0, parameters, 0, pars.Length - 2);\r
184                                 in_parameters = new ParameterInfo [parameters.Length];\r
185                                 parameters.CopyTo (in_parameters, 0);\r
186                                 \r
187                                 ParameterInfo[] outPars = end_method_info.GetParameters ();\r
188                                 out_parameters = new ParameterInfo [outPars.Length - 1];\r
189                                 Array.Copy (outPars, 0, out_parameters, 0, out_parameters.Length);\r
190                         }\r
191                         else\r
192                         {\r
193                                 parameters = pars;\r
194                                 int out_count = 0;\r
195                                 int in_count = 0;\r
196                                 \r
197                                 foreach (ParameterInfo p in parameters){\r
198                                         Type ptype = p.ParameterType;\r
199                                         if (ptype.IsByRef){\r
200                                                 out_count++;\r
201                                                 if (!p.IsOut)\r
202                                                         in_count++;\r
203                                         } else\r
204                                                 in_count++;\r
205                                 }\r
206                                 out_parameters = new ParameterInfo [out_count];\r
207                                 int i = 0;\r
208                                 for (int j = 0; j < parameters.Length; j++){\r
209                                         if (parameters [j].ParameterType.IsByRef)\r
210                                                 out_parameters [i++] = parameters [j];\r
211                                 }\r
212                                 in_parameters = new ParameterInfo [in_count];\r
213                                 i = 0;\r
214                                 for (int j = 0; j < parameters.Length; j++){\r
215                                         if (parameters [j].ParameterType.IsByRef){\r
216                                                 if (!parameters [j].IsOut)\r
217                                                         in_parameters [i++] = parameters [j];\r
218                                         } else\r
219                                                 in_parameters [i++] = parameters [j];\r
220                                 }\r
221                         }\r
222                 }\r
223                 \r
224                 public ParameterInfo[] OutParameters {\r
225                         get {\r
226                                 if (parameters == null)\r
227                                         ComputeParameters ();\r
228                                 return out_parameters;\r
229                         }\r
230                 }\r
231 \r
232                 public ParameterInfo[] Parameters {\r
233                         get {\r
234                                 if (parameters == null)\r
235                                         ComputeParameters ();\r
236                                 return parameters;\r
237                         }\r
238                 }\r
239 \r
240                 public Type ReturnType {\r
241                         get {\r
242                                 if (IsAsync)\r
243                                         return end_method_info.ReturnType;\r
244                                 else\r
245                                         return method_info.ReturnType;\r
246                         }\r
247                 }\r
248 \r
249                 public ICustomAttributeProvider ReturnTypeCustomAttributeProvider {\r
250                         get {\r
251                                 return method_info.ReturnTypeCustomAttributes;\r
252                         }\r
253                 }\r
254 \r
255                 internal bool EnableSession {\r
256                         get {\r
257                                 if (method_info == null)\r
258                                         return false;\r
259 \r
260                                 if (attribute == null) {\r
261                                         object [] o = method_info.GetCustomAttributes (false);\r
262                                         foreach (Attribute att in o) {\r
263                                                 if (att is WebMethodAttribute) {\r
264                                                         attribute = (WebMethodAttribute) att;\r
265                                                         break;\r
266                                                 }\r
267                                         }\r
268                                 }\r
269 \r
270                                 return (attribute != null) ? attribute.EnableSession : false;\r
271                         }\r
272                 }\r
273                 #endregion // Properties\r
274 \r
275                 #region Methods\r
276 \r
277                 public IAsyncResult BeginInvoke (object target, object[] values, AsyncCallback callback, object asyncState)\r
278                 {\r
279                         int len = (values!=null) ? values.Length : 0;\r
280                         object[] pars = new object [len + 2];\r
281                         \r
282                         if (len > 0)\r
283                                 values.CopyTo (pars, 0);\r
284                         \r
285                         pars [len] = callback;\r
286                         pars [len+1] = asyncState;\r
287                                 \r
288                         return (IAsyncResult) method_info.Invoke (target, pars);\r
289                 }\r
290 \r
291                 public static LogicalMethodInfo[] Create (MethodInfo[] method_infos)\r
292                 {\r
293                         return Create (method_infos, LogicalMethodTypes.Sync | LogicalMethodTypes.Async);\r
294                 }\r
295 \r
296                 public static LogicalMethodInfo[] Create (MethodInfo[] method_infos, LogicalMethodTypes types)\r
297                 {\r
298                         ArrayList sync = ((types & LogicalMethodTypes.Sync) != 0) ? new ArrayList () : null;\r
299                         ArrayList begin, end;\r
300 \r
301                         if ((types & LogicalMethodTypes.Async) != 0){\r
302                                 begin = new ArrayList ();\r
303                                 end = new ArrayList ();\r
304                         } else \r
305                                 begin = end = null;\r
306 \r
307                         foreach (MethodInfo mi in method_infos){\r
308                                 if (IsBeginMethod (mi) && begin != null)\r
309                                         begin.Add (mi);\r
310                                 else if (IsEndMethod (mi) && end != null)\r
311                                         end.Add (mi);\r
312                                 else if (sync != null)\r
313                                         sync.Add (mi);\r
314                         }\r
315 \r
316                         int bcount = 0, count = 0;\r
317                         if (begin != null){\r
318                                 bcount = count = begin.Count;\r
319                                 if (count != end.Count)\r
320                                         throw new InvalidOperationException ("Imbalance of begin/end methods");\r
321                         }\r
322                         if (sync != null)\r
323                                 count += sync.Count;\r
324 \r
325                         LogicalMethodInfo [] res = new LogicalMethodInfo [count];\r
326                         int dest = 0;\r
327                         if (begin != null){\r
328                                 foreach (MethodInfo bm in begin){\r
329                                         string end_name = "End" + bm.Name.Substring (5);\r
330 \r
331                                         for (int i = 0; i < bcount; i++){\r
332                                                 MethodInfo em = (MethodInfo) end [i];\r
333                                                 if (em.Name == end_name){\r
334                                                         res [dest++] = new LogicalMethodInfo (bm, em);\r
335                                                         break;\r
336                                                 }\r
337                                                 throw new InvalidOperationException ("Imbalance of begin/end methods");\r
338                                         }\r
339                                 }\r
340                         }\r
341 \r
342 \r
343                         if (sync != null)\r
344                                 foreach (MethodInfo mi in sync){\r
345                                         res [dest++] = new LogicalMethodInfo (mi);\r
346                                 }\r
347                         \r
348                         return res;\r
349                 }\r
350 \r
351                 public object[] EndInvoke (object target, IAsyncResult asyncResult)\r
352                 {\r
353                         if (parameters == null)\r
354                                 ComputeParameters ();\r
355 \r
356                         object[] values = new object [out_parameters.Length + 1];\r
357                         values [values.Length - 1] = asyncResult;\r
358                         object res = end_method_info.Invoke (target, values);\r
359                         \r
360                         int retc = IsVoid ? 0 : 1;\r
361                         object [] ret = new object [retc + out_parameters.Length];\r
362                         \r
363                         if (retc == 1) ret [0] = res;\r
364                         \r
365                         Array.Copy (values, 0, ret, retc, out_parameters.Length);\r
366                         return ret;\r
367                 }\r
368 \r
369                 public object GetCustomAttribute (Type type)\r
370                 {\r
371                         return Attribute.GetCustomAttribute (method_info, type, false);\r
372                 }\r
373 \r
374                 public object[] GetCustomAttributes (Type type)\r
375                 {\r
376                         return method_info.GetCustomAttributes (type, false);\r
377                 }\r
378 \r
379                 public object[] Invoke (object target, object[] values)\r
380                 {\r
381                         if (parameters == null)\r
382                                 ComputeParameters ();\r
383 \r
384                         int retc = IsVoid ? 0 : 1;\r
385                         object [] ret = new object [retc + out_parameters.Length];\r
386                         object res = method_info.Invoke (target, values);\r
387                         if (retc == 1) ret [0] = res;\r
388 \r
389                         int j = retc;\r
390                         for (int i = 0; i < parameters.Length; i++){\r
391                                 if (parameters [i].ParameterType.IsByRef)\r
392                                         ret [j++] = values [i];\r
393                         }\r
394 \r
395                         return ret;\r
396                 }\r
397 \r
398                 public static bool IsBeginMethod (MethodInfo method_info)\r
399                 {\r
400                         if (method_info == null)\r
401                                 throw new ArgumentNullException ("method_info can not be null");\r
402 \r
403                         if (method_info.ReturnType != typeof (IAsyncResult))\r
404                                 return false;\r
405 \r
406                         if (method_info.Name.StartsWith ("Begin"))\r
407                                 return true;\r
408 \r
409                         return false;\r
410                 }\r
411 \r
412                 public static bool IsEndMethod (MethodInfo method_info)\r
413                 {\r
414                         if (method_info == null)\r
415                                 throw new ArgumentNullException ("method_info can not be null");\r
416 \r
417                         ParameterInfo [] parameter_info = method_info.GetParameters ();\r
418                         if (parameter_info.Length != 1)\r
419                                 return false;\r
420                         if (parameter_info [0].ParameterType != typeof (IAsyncResult))\r
421                                 return false;\r
422                         if (method_info.Name.StartsWith ("End"))\r
423                                 return true;\r
424 \r
425                         return false;\r
426                 }\r
427 \r
428                 public override string ToString ()\r
429                 {\r
430                         StringBuilder sb = new StringBuilder ();\r
431                         if (parameters == null)\r
432                                 ComputeParameters ();\r
433                         \r
434                         for (int i = 0; i < parameters.Length; i++){\r
435                                 sb.Append (parameters [i].ParameterType);\r
436                                 if (parameters [i].ParameterType.IsByRef)\r
437                                         sb.Append (" ByRef");\r
438                                 \r
439                                 if (i+1 != parameters.Length)\r
440                                         sb.Append (", ");\r
441                         }\r
442                         \r
443                         return String.Format (\r
444                                 "{0} {1} ({2})",\r
445                                 method_info.ReturnType, method_info.Name,\r
446                                 sb.ToString ());\r
447                 }\r
448 \r
449                 #endregion // Methods\r
450         }\r
451 }\r