* Methods.cs: added support for encoded format
[mono.git] / mcs / class / System.Web.Services / System.Web.Services.Protocols / Methods.cs
1 //
2 // Methods.cs: Information about a method and its mapping to a SOAP web service.
3 //
4 // Author:
5 //   Miguel de Icaza
6 //
7 // (C) 2003 Ximian, Inc.
8 //
9 // TODO:
10 //    
11 //
12
13 using System.Reflection;
14 using System.Collections;
15 using System.Xml.Serialization;
16 using System.Web.Services;
17 using System.Web.Services.Description;
18
19 namespace System.Web.Services.Protocols {
20
21         //
22         // This class represents all the information we extract from a MethodInfo
23         // in the SoapHttpClientProtocol derivative stub class
24         //
25         internal class MethodStubInfo {
26                 internal LogicalMethodInfo MethodInfo;
27
28                 // The name used bythe stub class to reference this method.
29                 internal string Name;
30                 
31                 internal string Action;
32                 internal string Binding;
33
34                 // The name/namespace of the request 
35                 internal string RequestName;
36                 internal string RequestNamespace;
37
38                 // The name/namespace of the response.
39                 internal string ResponseName;
40                 internal string ResponseNamespace;
41                 
42                 internal bool   OneWay;
43                 internal SoapParameterStyle ParameterStyle;
44
45                 internal XmlSerializer RequestSerializer;
46                 internal XmlSerializer ResponseSerializer;
47
48                 //
49                 // Constructor
50                 //
51                 MethodStubInfo (TypeStubInfo parent, LogicalMethodInfo source, object kind, XmlReflectionImporter xmlImporter, SoapReflectionImporter soapImporter)
52                 {
53                         MethodInfo = source;
54
55                         XmlElementAttribute optional_ns = null;
56                         SoapBindingUse use;
57                         
58                         if (kind is SoapDocumentMethodAttribute){
59                                 SoapDocumentMethodAttribute dma = (SoapDocumentMethodAttribute) kind;
60                                 
61                                 use = dma.Use;
62                                 if (use == SoapBindingUse.Default)
63                                         use = parent.Use;
64                                 
65                                 Action = dma.Action;
66                                 Binding = dma.Binding;
67                                 RequestName = dma.RequestElementName;
68                                 RequestNamespace = dma.RequestNamespace;
69                                 ResponseName = dma.ResponseElementName;
70                                 ResponseNamespace = dma.ResponseNamespace;
71                                 ParameterStyle = dma.ParameterStyle;
72                                 if (ParameterStyle == SoapParameterStyle.Default)
73                                         ParameterStyle = parent.ParameterStyle;
74                                 OneWay = dma.OneWay;
75                         } else {
76                                 SoapRpcMethodAttribute rma = (SoapRpcMethodAttribute) kind;
77                                 use = SoapBindingUse.Encoded;   // RPC always use encoded
78
79                                 Action = rma.Action;
80                                 Binding = rma.Binding;
81                                 RequestName = rma.RequestElementName;
82                                 RequestNamespace = rma.RequestNamespace;
83                                 ResponseNamespace = rma.ResponseNamespace;
84                                 ResponseName = rma.ResponseElementName;
85                                 OneWay = rma.OneWay;
86
87                                 // For RPC calls, make all arguments be part of the empty namespace
88                                 optional_ns = new XmlElementAttribute ();
89                                 optional_ns.Namespace = "";
90                         }
91                         if (Binding == "")
92                                 Binding = parent.BindingName;
93                         if (RequestName == "")
94                                 RequestName = source.Name;
95                         
96                         if (OneWay){
97                                 if (source.ReturnType != typeof (void))
98                                         throw new Exception ("OneWay methods should not have a return value");
99                                 if (source.OutParameters.Length != 0)
100                                         throw new Exception ("OneWay methods should not have out/ref parameters");
101                         }
102                         
103                         object [] o = source.GetCustomAttributes (typeof (WebMethodAttribute));
104                         if (o.Length == 1){
105                                 WebMethodAttribute wma = (WebMethodAttribute) o [0];
106
107                                 Name = wma.MessageName;
108                                 if (Name == "")
109                                         Name = source.Name;
110                         } else
111                                 Name = source.Name;
112
113                         if (ResponseName == "")
114                                 ResponseName = Name + "Response";
115
116                         XmlReflectionMember [] in_members = BuildRequestReflectionMembers (optional_ns);
117                         XmlReflectionMember [] out_members = BuildResponseReflectionMembers (optional_ns);
118
119                         XmlMembersMapping [] members = new XmlMembersMapping [2];
120                         try {
121                                 if (use == SoapBindingUse.Literal) {
122                                         members [0] = xmlImporter.ImportMembersMapping (RequestName, RequestNamespace, in_members, true);
123                                         members [1] = xmlImporter.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, true);
124                                 }
125                                 else {
126                                         members [0] = soapImporter.ImportMembersMapping (RequestName, RequestNamespace, in_members, true, true);
127                                         members [1] = soapImporter.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, true, true);
128                                 }
129
130                                 XmlSerializer [] s = null;
131                                 s = XmlSerializer.FromMappings (members);
132                                 RequestSerializer = s [0];
133                                 ResponseSerializer = s [1];
134                         } catch {
135                                 Console.WriteLine ("Got exception while creating serializer");
136                                 Console.WriteLine ("Method name: " + RequestName + " parameters are:");
137
138                                 for (int i = 0; i < in_members.Length; i++) {
139                                         Console.WriteLine ("    {0}: {1} {2}", i, in_members [i].MemberName, in_members [i].MemberType);
140                                 }
141 \r
142                                 Console.WriteLine ("Output parameters are:");
143                                 for (int i = 0; i < out_members.Length; i++) {
144                                         Console.WriteLine ("    {0}: {1} {2}", i, out_members [i].MemberName, out_members [i].MemberType);
145                                 }
146                                 throw;
147                         }
148                         ResponseSerializer.UnknownNode += new XmlNodeEventHandler (e);\r
149                 }
150
151                 static internal MethodStubInfo Create (TypeStubInfo parent, LogicalMethodInfo lmi, XmlReflectionImporter xmlImporter, SoapReflectionImporter soapImporter)
152                 {
153                         object [] o = lmi.GetCustomAttributes (typeof (SoapDocumentMethodAttribute));
154                         if (o.Length == 0){
155                                 o = lmi.GetCustomAttributes (typeof (SoapRpcMethodAttribute));
156                                 if (o.Length == 0)
157                                         return null;
158                                 return new MethodStubInfo (parent, lmi, o [0], xmlImporter, soapImporter);
159                         } else 
160                                 return new MethodStubInfo (parent, lmi, o [0], xmlImporter, soapImporter);
161                 }
162
163                 XmlReflectionMember [] BuildRequestReflectionMembers (XmlElementAttribute optional_ns)
164                 {
165                         ParameterInfo [] input = MethodInfo.InParameters;
166                         XmlReflectionMember [] in_members = new XmlReflectionMember [input.Length];
167
168                         for (int i = 0; i < input.Length; i++)\r
169                         {
170                                 XmlReflectionMember m = new XmlReflectionMember ();
171                                 m.IsReturnValue = false;
172                                 m.MemberName = input [i].Name;
173                                 m.MemberType = input [i].ParameterType;
174
175                                 m.XmlAttributes = new XmlAttributes (input[i]);
176                                 m.SoapAttributes = new SoapAttributes (input[i]);
177
178                                 if (m.MemberType.IsByRef)
179                                         m.MemberType = m.MemberType.GetElementType ();
180                                 if (optional_ns != null)
181                                         m.XmlAttributes.XmlElements.Add (optional_ns);
182                                 in_members [i] = m;
183                         }
184                         return in_members;
185                 }
186                 
187                 XmlReflectionMember [] BuildResponseReflectionMembers (XmlElementAttribute optional_ns)
188                 {
189                         ParameterInfo [] output = MethodInfo.OutParameters;
190                         bool has_return_value = !(OneWay || MethodInfo.ReturnType == typeof (void));
191                         XmlReflectionMember [] out_members = new XmlReflectionMember [(has_return_value ? 1 : 0) + output.Length];
192                         XmlReflectionMember m;
193                         int idx = 0;
194
195                         if (has_return_value)\r
196                         {
197                                 m = new XmlReflectionMember ();
198                                 m.IsReturnValue = true;
199                                 m.MemberName = RequestName + "Result";
200                                 m.MemberType = MethodInfo.ReturnType;
201
202                                 m.XmlAttributes = new XmlAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
203                                 m.SoapAttributes = new SoapAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
204
205                                 if (optional_ns != null)
206                                         m.XmlAttributes.XmlElements.Add (optional_ns);
207                                 idx++;
208                                 out_members [0] = m;
209                         }
210                         
211                         for (int i = 0; i < output.Length; i++)\r
212                         {
213                                 m = new XmlReflectionMember ();
214                                 m.IsReturnValue = false;
215                                 m.MemberName = output [i].Name;
216                                 m.MemberType = output [i].ParameterType;
217                                 m.XmlAttributes = new XmlAttributes (output[i]);
218                                 m.SoapAttributes = new SoapAttributes (output[i]);
219
220                                 if (m.MemberType.IsByRef)
221                                         m.MemberType = m.MemberType.GetElementType ();
222                                 if (optional_ns != null)
223                                         m.XmlAttributes.XmlElements.Add (optional_ns);
224                                 out_members [i + idx] = m;
225                         }
226                         return out_members;
227                 }
228
229                 static void e (object o, XmlNodeEventArgs a)
230                 {
231                         Console.WriteLine ("Unexpected Node: {5}:{6} {0}/{1}/{2}/{3}/{4}",
232                                            a.LocalName, a.Name, a.NamespaceURI, a.NodeType, a.Text,
233                                            a.LineNumber, a.LinePosition);
234 //                      throw new Exception ();
235                 }
236         }
237
238         //
239         // Holds the metadata loaded from the type stub, as well as
240         // the metadata for all the methods in the type
241         //
242         internal class TypeStubInfo {
243                 Hashtable name_to_method = new Hashtable ();
244
245                 // Precomputed
246                 internal SoapParameterStyle      ParameterStyle;
247                 internal SoapServiceRoutingStyle RoutingStyle;
248                 internal SoapBindingUse          Use;
249                 internal string                  WebServiceName;
250                 internal string                  WebServiceNamespace;
251                 internal string                  BindingLocation;
252                 internal string                  BindingName;
253                 internal string                  BindingNamespace;
254
255                 void GetTypeAttributes (Type t)
256                 {
257                         object [] o;
258
259                         o = t.GetCustomAttributes (typeof (WebServiceBindingAttribute), false);
260                         if (o.Length != 1)
261                                 throw new Exception ("Expected WebServiceBindingAttribute on "+ t.Name);
262                         WebServiceBindingAttribute b = (WebServiceBindingAttribute) o [0];
263                         BindingLocation = b.Location;
264                         BindingName = b.Name;
265                         BindingNamespace = b.Namespace;
266
267                         o = t.GetCustomAttributes (typeof (WebService), false);
268                         if (o.Length == 1){
269                                 WebServiceAttribute a = (WebServiceAttribute) o [0];
270
271                                 WebServiceName = a.Name;
272                                 WebServiceNamespace = a.Namespace;
273                         } else {
274                                 WebServiceName = t.Name;
275                                 WebServiceNamespace = WebServiceAttribute.DefaultNamespace;
276                         }
277
278                         o = t.GetCustomAttributes (typeof (SoapDocumentServiceAttribute), false);
279                         if (o.Length == 1){
280                                 SoapDocumentServiceAttribute a = (SoapDocumentServiceAttribute) o [0];
281
282                                 ParameterStyle = a.ParameterStyle;
283                                 RoutingStyle = a.RoutingStyle;
284                                 Use = a.Use;
285                         } else {
286                                 o = t.GetCustomAttributes (typeof (SoapRpcServiceAttribute), false);
287                                 if (o.Length == 1){
288                                         SoapRpcServiceAttribute srs = (SoapRpcServiceAttribute) o [0];
289                                         
290                                         ParameterStyle = SoapParameterStyle.Wrapped;
291                                         RoutingStyle = srs.RoutingStyle;
292                                         Use = SoapBindingUse.Literal;
293                                 } else {
294                                         ParameterStyle = SoapParameterStyle.Wrapped;
295                                         RoutingStyle = SoapServiceRoutingStyle.SoapAction;
296                                         Use = SoapBindingUse.Literal;
297                                 }
298                         }
299                 }
300
301                 //
302                 // Extract all method information
303                 //
304                 void GetTypeMethods (Type t, XmlReflectionImporter xmlImporter, SoapReflectionImporter soapImporter)
305                 {
306                         MethodInfo [] type_methods = t.GetMethods (BindingFlags.Instance | BindingFlags.Public);
307                         LogicalMethodInfo [] methods = LogicalMethodInfo.Create (type_methods, LogicalMethodTypes.Sync);
308
309                         foreach (LogicalMethodInfo mi in methods){
310                                 MethodStubInfo msi = MethodStubInfo.Create (this, mi, xmlImporter, soapImporter);
311
312                                 if (msi == null)
313                                         continue;
314
315                                 name_to_method [msi.Name] = msi;
316                         }
317                 }
318                 
319                 internal TypeStubInfo (Type t)
320                 {
321                         GetTypeAttributes (t);
322
323                         XmlReflectionImporter xmlImporter = new XmlReflectionImporter ();
324                         SoapReflectionImporter soapImporter = new SoapReflectionImporter ();
325                         GetTypeMethods (t, xmlImporter, soapImporter);
326                 }
327
328                 internal MethodStubInfo GetMethod (string name)
329                 {
330                         return (MethodStubInfo) name_to_method [name];
331                 }
332         }
333         
334         //
335         // Manages 
336         //
337         internal class TypeStubManager {
338                 static Hashtable type_to_manager;
339                 
340                 static TypeStubManager ()
341                 {
342                         type_to_manager = new Hashtable ();
343                 }
344
345                 //
346                 // This needs to be thread safe
347                 //
348                 static internal TypeStubInfo GetTypeStub (Type t)
349                 {
350                         TypeStubInfo tm = (TypeStubInfo) type_to_manager [t];
351
352                         if (tm != null)
353                                 return tm;
354
355                         lock (typeof (TypeStubInfo)){
356                                 tm = (TypeStubInfo) type_to_manager [t];
357
358                                 if (tm != null)
359                                         return tm;
360                                 
361                                 tm = new TypeStubInfo (t);
362                                 type_to_manager [t] = tm;
363
364                                 return tm;
365                         }
366                 }
367         }
368 }