**** Merged from MCS ****
[mono-project.git] / mcs / class / System.Web.Services / System.Web.Services.Protocols / Methods.cs
blob0bd7140545bff08d846abaed6fc1c6516353de4a
1 //
2 // Methods.cs: Information about a method and its mapping to a SOAP web service.
3 //
4 // Author:
5 // Miguel de Icaza
6 // Lluis Sanchez Gual (lluis@ximian.com)
7 //
8 // (C) 2003 Ximian, Inc.
9 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 //
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 //
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.Reflection;
33 using System.Collections;
34 using System.Xml;
35 using System.Xml.Serialization;
36 using System.Web.Services;
37 using System.Web.Services.Description;
39 namespace System.Web.Services.Protocols {
42 // This class represents all the information we extract from a MethodInfo
43 // in the SoapHttpClientProtocol derivative stub class
45 internal class SoapMethodStubInfo : MethodStubInfo
47 internal string Action;
48 internal string Binding;
50 // The name/namespace of the request
51 internal string RequestName;
52 internal string RequestNamespace;
54 // The name/namespace of the response.
55 internal string ResponseName;
56 internal string ResponseNamespace;
58 internal bool OneWay;
59 internal SoapParameterStyle ParameterStyle;
60 internal SoapBindingStyle SoapBindingStyle;
61 internal SoapBindingUse Use;
63 internal HeaderInfo[] Headers;
64 internal SoapExtensionRuntimeConfig[] SoapExtensions;
66 internal XmlMembersMapping InputMembersMapping;
67 internal XmlMembersMapping OutputMembersMapping;
69 private int requestSerializerId;
70 private int responseSerializerId;
72 internal XmlSerializer RequestSerializer
74 get { return TypeStub.GetSerializer (requestSerializerId); }
77 internal XmlSerializer ResponseSerializer
79 get { return TypeStub.GetSerializer (responseSerializerId); }
83 // Constructor
85 public SoapMethodStubInfo (TypeStubInfo typeStub, LogicalMethodInfo source, object kind, XmlReflectionImporter xmlImporter, SoapReflectionImporter soapImporter)
86 : base (typeStub, source)
88 SoapTypeStubInfo parent = (SoapTypeStubInfo) typeStub;
89 XmlElementAttribute optional_ns = null;
91 if (kind == null) {
92 Use = parent.Use;
93 RequestName = "";
94 RequestNamespace = "";
95 ResponseName = "";
96 ResponseNamespace = "";
97 ParameterStyle = parent.ParameterStyle;
98 SoapBindingStyle = parent.SoapBindingStyle;
99 OneWay = false;
101 else if (kind is SoapDocumentMethodAttribute){
102 SoapDocumentMethodAttribute dma = (SoapDocumentMethodAttribute) kind;
104 Use = dma.Use;
105 if (Use == SoapBindingUse.Default) {
106 if (parent.SoapBindingStyle == SoapBindingStyle.Document)
107 Use = parent.Use;
108 else
109 Use = SoapBindingUse.Literal;
112 Action = dma.Action;
113 Binding = dma.Binding;
114 RequestName = dma.RequestElementName;
115 RequestNamespace = dma.RequestNamespace;
116 ResponseName = dma.ResponseElementName;
117 ResponseNamespace = dma.ResponseNamespace;
118 ParameterStyle = dma.ParameterStyle;
119 if (ParameterStyle == SoapParameterStyle.Default)
120 ParameterStyle = parent.ParameterStyle;
121 OneWay = dma.OneWay;
122 SoapBindingStyle = SoapBindingStyle.Document;
123 } else {
124 SoapRpcMethodAttribute rma = (SoapRpcMethodAttribute) kind;
125 Use = SoapBindingUse.Encoded; // RPC always use encoded
127 Action = rma.Action;
128 Binding = rma.Binding;
130 // When using RPC, MS.NET seems to ignore RequestElementName and
131 // MessageName, and it always uses the method name
132 RequestName = source.Name;
133 ResponseName = source.Name + "Response";
134 // RequestName = rma.RequestElementName;
135 // ResponseName = rma.ResponseElementName;
136 RequestNamespace = rma.RequestNamespace;
137 ResponseNamespace = rma.ResponseNamespace;
138 ParameterStyle = SoapParameterStyle.Wrapped;
139 OneWay = rma.OneWay;
140 SoapBindingStyle = SoapBindingStyle.Rpc;
142 // For RPC calls, make all arguments be part of the empty namespace
143 optional_ns = new XmlElementAttribute ();
144 optional_ns.Namespace = "";
147 if (OneWay){
148 if (source.ReturnType != typeof (void))
149 throw new Exception ("OneWay methods should not have a return value.");
150 if (source.OutParameters.Length != 0)
151 throw new Exception ("OneWay methods should not have out/ref parameters.");
154 BindingInfo binfo = parent.GetBinding (Binding);
155 if (binfo == null) throw new InvalidOperationException ("Type '" + parent.Type + "' is missing WebServiceBinding attribute that defines a binding named '" + Binding + "'.");
157 string serviceNamespace = binfo.Namespace;
159 if (RequestNamespace == "") RequestNamespace = parent.LogicalType.GetWebServiceNamespace (serviceNamespace, Use);
160 if (ResponseNamespace == "") ResponseNamespace = parent.LogicalType.GetWebServiceNamespace (serviceNamespace, Use);
161 if (RequestName == "") RequestName = Name;
162 if (ResponseName == "") ResponseName = Name + "Response";
163 if (Action == null || Action == "")
164 Action = serviceNamespace.EndsWith("/") ? (serviceNamespace + Name) : (serviceNamespace + "/" + Name);
166 bool hasWrappingElem = (ParameterStyle == SoapParameterStyle.Wrapped);
167 bool writeAccessors = (SoapBindingStyle == SoapBindingStyle.Rpc);
169 XmlReflectionMember [] in_members = BuildRequestReflectionMembers (optional_ns);
170 XmlReflectionMember [] out_members = BuildResponseReflectionMembers (optional_ns);
172 if (Use == SoapBindingUse.Literal) {
173 xmlImporter.IncludeTypes (source.CustomAttributeProvider);
174 InputMembersMapping = xmlImporter.ImportMembersMapping (RequestName, RequestNamespace, in_members, hasWrappingElem);
175 OutputMembersMapping = xmlImporter.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, hasWrappingElem);
177 else {
178 soapImporter.IncludeTypes (source.CustomAttributeProvider);
179 InputMembersMapping = soapImporter.ImportMembersMapping (RequestName, RequestNamespace, in_members, hasWrappingElem, writeAccessors);
180 OutputMembersMapping = soapImporter.ImportMembersMapping (ResponseName, ResponseNamespace, out_members, hasWrappingElem, writeAccessors);
183 requestSerializerId = parent.RegisterSerializer (InputMembersMapping);
184 responseSerializerId = parent.RegisterSerializer (OutputMembersMapping);
186 object[] o = source.GetCustomAttributes (typeof (SoapHeaderAttribute));
187 ArrayList headerList = new ArrayList (o.Length);
189 for (int i = 0; i < o.Length; i++) {
190 SoapHeaderAttribute att = (SoapHeaderAttribute) o[i];
191 MemberInfo[] mems = source.DeclaringType.GetMember (att.MemberName);
192 if (mems.Length == 0) throw new InvalidOperationException ("Member " + att.MemberName + " not found in class " + source.DeclaringType.FullName + ".");
194 HeaderInfo header = new HeaderInfo (mems[0], att);
195 headerList.Add (header);
197 if (!header.IsUnknownHeader)
198 parent.RegisterHeaderType (header.HeaderType, serviceNamespace, Use);
200 Headers = (HeaderInfo[]) headerList.ToArray (typeof(HeaderInfo));
202 SoapExtensions = SoapExtension.GetMethodExtensions (source);
205 XmlReflectionMember [] BuildRequestReflectionMembers (XmlElementAttribute optional_ns)
207 ParameterInfo [] input = MethodInfo.InParameters;
208 XmlReflectionMember [] in_members = new XmlReflectionMember [input.Length];
210 for (int i = 0; i < input.Length; i++)
212 XmlReflectionMember m = new XmlReflectionMember ();
213 m.IsReturnValue = false;
214 m.MemberName = input [i].Name;
215 m.MemberType = input [i].ParameterType;
217 m.XmlAttributes = new XmlAttributes (input[i]);
218 m.SoapAttributes = new SoapAttributes (input[i]);
220 if (m.MemberType.IsByRef)
221 m.MemberType = m.MemberType.GetElementType ();
222 if (optional_ns != null)
223 m.XmlAttributes.XmlElements.Add (optional_ns);
224 in_members [i] = m;
226 return in_members;
229 XmlReflectionMember [] BuildResponseReflectionMembers (XmlElementAttribute optional_ns)
231 ParameterInfo [] output = MethodInfo.OutParameters;
232 bool has_return_value = !(OneWay || MethodInfo.ReturnType == typeof (void));
233 XmlReflectionMember [] out_members = new XmlReflectionMember [(has_return_value ? 1 : 0) + output.Length];
234 XmlReflectionMember m;
235 int idx = 0;
237 if (has_return_value)
239 m = new XmlReflectionMember ();
240 m.IsReturnValue = true;
241 m.MemberName = RequestName + "Result";
242 m.MemberType = MethodInfo.ReturnType;
244 m.XmlAttributes = new XmlAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
245 m.SoapAttributes = new SoapAttributes (MethodInfo.ReturnTypeCustomAttributeProvider);
247 if (optional_ns != null)
248 m.XmlAttributes.XmlElements.Add (optional_ns);
249 idx++;
250 out_members [0] = m;
253 for (int i = 0; i < output.Length; i++)
255 m = new XmlReflectionMember ();
256 m.IsReturnValue = false;
257 m.MemberName = output [i].Name;
258 m.MemberType = output [i].ParameterType;
259 m.XmlAttributes = new XmlAttributes (output[i]);
260 m.SoapAttributes = new SoapAttributes (output[i]);
262 if (m.MemberType.IsByRef)
263 m.MemberType = m.MemberType.GetElementType ();
264 if (optional_ns != null)
265 m.XmlAttributes.XmlElements.Add (optional_ns);
266 out_members [i + idx] = m;
268 return out_members;
271 public HeaderInfo GetHeaderInfo (Type headerType)
273 foreach (HeaderInfo headerInfo in Headers)
274 if (headerInfo.HeaderType == headerType) return headerInfo;
275 return null;
279 internal class HeaderInfo
281 internal MemberInfo Member;
282 internal SoapHeaderAttribute AttributeInfo;
283 internal Type HeaderType;
284 internal bool IsUnknownHeader;
286 public HeaderInfo (MemberInfo member, SoapHeaderAttribute attributeInfo)
288 Member = member;
289 AttributeInfo = attributeInfo;
290 if (Member is PropertyInfo) HeaderType = ((PropertyInfo)Member).PropertyType;
291 else HeaderType = ((FieldInfo)Member).FieldType;
293 if (HeaderType == typeof(SoapHeader) || HeaderType == typeof(SoapUnknownHeader) ||
294 HeaderType == typeof(SoapHeader[]) || HeaderType == typeof(SoapUnknownHeader[]))
296 IsUnknownHeader = true;
298 else if (!typeof(SoapHeader).IsAssignableFrom (HeaderType))
299 throw new InvalidOperationException (string.Format ("Header members type must be a SoapHeader subclass"));
302 public object GetHeaderValue (object ob)
304 if (Member is PropertyInfo) return ((PropertyInfo)Member).GetValue (ob, null);
305 else return ((FieldInfo)Member).GetValue (ob);
308 public void SetHeaderValue (object ob, SoapHeader header)
310 object value = header;
311 if (IsUnknownHeader && HeaderType.IsArray)
313 SoapUnknownHeader uheader = header as SoapUnknownHeader;
314 SoapUnknownHeader[] array = (SoapUnknownHeader[]) GetHeaderValue (ob);
315 if (array == null || array.Length == 0) {
316 value = new SoapUnknownHeader[] { uheader };
318 else {
319 SoapUnknownHeader[] newArray = new SoapUnknownHeader [array.Length+1];
320 Array.Copy (array, newArray, array.Length);
321 newArray [array.Length] = uheader;
322 value = newArray;
326 if (Member is PropertyInfo) ((PropertyInfo)Member).SetValue (ob, value, null);
327 else ((FieldInfo)Member).SetValue (ob, value);
330 public SoapHeaderDirection Direction
332 get { return AttributeInfo.Direction; }
338 // Holds the metadata loaded from the type stub, as well as
339 // the metadata for all the methods in the type
341 internal class SoapTypeStubInfo : TypeStubInfo
343 Hashtable[] header_serializers = new Hashtable [3];
344 Hashtable[] header_serializers_byname = new Hashtable [3];
345 Hashtable methods_byaction = new Hashtable ();
347 // Precomputed
348 internal SoapParameterStyle ParameterStyle;
349 internal SoapServiceRoutingStyle RoutingStyle;
350 internal SoapBindingUse Use;
351 internal SoapExtensionRuntimeConfig[][] SoapExtensions;
352 internal SoapBindingStyle SoapBindingStyle;
353 internal XmlReflectionImporter xmlImporter;
354 internal SoapReflectionImporter soapImporter;
356 public SoapTypeStubInfo (LogicalTypeInfo logicalTypeInfo)
357 : base (logicalTypeInfo)
359 xmlImporter = new XmlReflectionImporter ();
360 soapImporter = new SoapReflectionImporter ();
362 object [] o;
364 o = Type.GetCustomAttributes (typeof (WebServiceBindingAttribute), false);
366 if (typeof (SoapHttpClientProtocol).IsAssignableFrom (Type))
368 if (o.Length == 0)
369 throw new InvalidOperationException ("WebServiceBindingAttribute is required on proxy class '" + Type + "'.");
370 if (o.Length > 1)
371 throw new InvalidOperationException ("Only one WebServiceBinding attribute may be specified on type '" + Type + "'.");
373 // Remove the default binding, it is not needed since there is always
374 // a binding attribute.
375 Bindings.Clear ();
378 foreach (WebServiceBindingAttribute at in o)
379 AddBinding (new BindingInfo (at, LogicalType.WebServiceNamespace));
381 o = Type.GetCustomAttributes (typeof (SoapDocumentServiceAttribute), false);
382 if (o.Length == 1){
383 SoapDocumentServiceAttribute a = (SoapDocumentServiceAttribute) o [0];
385 ParameterStyle = a.ParameterStyle;
386 RoutingStyle = a.RoutingStyle;
387 Use = a.Use;
388 SoapBindingStyle = SoapBindingStyle.Document;
389 } else {
390 o = Type.GetCustomAttributes (typeof (SoapRpcServiceAttribute), false);
391 if (o.Length == 1){
392 SoapRpcServiceAttribute srs = (SoapRpcServiceAttribute) o [0];
394 ParameterStyle = SoapParameterStyle.Wrapped;
395 RoutingStyle = srs.RoutingStyle;
396 Use = SoapBindingUse.Encoded;
397 SoapBindingStyle = SoapBindingStyle.Rpc;
398 } else {
399 ParameterStyle = SoapParameterStyle.Wrapped;
400 RoutingStyle = SoapServiceRoutingStyle.SoapAction;
401 Use = SoapBindingUse.Literal;
402 SoapBindingStyle = SoapBindingStyle.Document;
406 if (ParameterStyle == SoapParameterStyle.Default) ParameterStyle = SoapParameterStyle.Wrapped;
407 if (Use == SoapBindingUse.Default) Use = SoapBindingUse.Literal;
409 xmlImporter.IncludeTypes (Type);
410 soapImporter.IncludeTypes (Type);
412 SoapExtensions = SoapExtension.GetTypeExtensions (Type);
415 public override XmlReflectionImporter XmlImporter
417 get { return xmlImporter; }
420 public override SoapReflectionImporter SoapImporter
422 get { return soapImporter; }
425 public override string ProtocolName
427 get { return "Soap"; }
430 protected override MethodStubInfo CreateMethodStubInfo (TypeStubInfo parent, LogicalMethodInfo lmi, bool isClientProxy)
432 SoapMethodStubInfo res = null;
433 object [] ats = lmi.GetCustomAttributes (typeof (SoapDocumentMethodAttribute));
434 if (ats.Length == 0) ats = lmi.GetCustomAttributes (typeof (SoapRpcMethodAttribute));
436 if (ats.Length == 0 && isClientProxy)
437 return null;
438 else if (ats.Length == 0)
439 res = new SoapMethodStubInfo (parent, lmi, null, xmlImporter, soapImporter);
440 else
441 res = new SoapMethodStubInfo (parent, lmi, ats[0], xmlImporter, soapImporter);
443 methods_byaction [res.Action] = res;
444 return res;
447 internal void RegisterHeaderType (Type type, string serviceNamespace, SoapBindingUse use)
449 Hashtable serializers = header_serializers [(int)use];
450 if (serializers == null) {
451 serializers = new Hashtable ();
452 header_serializers [(int)use] = serializers;
453 header_serializers_byname [(int)use] = new Hashtable ();
456 if (serializers.ContainsKey (type))
457 return;
459 XmlTypeMapping tm;
460 if (use == SoapBindingUse.Literal) {
461 XmlReflectionImporter ri = new XmlReflectionImporter ();
463 // MS.NET reflects header classes in a weird way. The root element
464 // name is the CLR class name unless it is specified in an XmlRootAttribute.
465 // The usual is to use the xml type name by default, but not in this case.
467 XmlRootAttribute root;
468 XmlAttributes ats = new XmlAttributes (type);
469 if (ats.XmlRoot != null) root = ats.XmlRoot;
470 else root = new XmlRootAttribute (type.Name);
472 if (root.Namespace == null) root.Namespace = LogicalType.GetWebServiceLiteralNamespace (serviceNamespace);
473 if (root.ElementName == null) root.ElementName = type.Name;
475 tm = ri.ImportTypeMapping (type, root);
477 else {
478 SoapReflectionImporter ri = new SoapReflectionImporter ();
479 tm = ri.ImportTypeMapping (type, LogicalType.GetWebServiceEncodedNamespace (serviceNamespace));
482 int sid = RegisterSerializer (tm);
484 serializers [type] = sid;
485 header_serializers_byname [(int)use] [new XmlQualifiedName (tm.ElementName, tm.Namespace)] = sid;
488 internal XmlSerializer GetHeaderSerializer (Type type, SoapBindingUse use)
490 Hashtable table = header_serializers [(int)use];
491 if (table == null) return null;
493 return GetSerializer ((int) table [type]);
496 internal XmlSerializer GetHeaderSerializer (XmlQualifiedName qname, SoapBindingUse use)
498 Hashtable table = header_serializers_byname [(int)use];
499 if (table == null) return null;
501 object serId = table [qname];
502 if (serId == null) return null;
504 return GetSerializer ((int) serId);
507 public SoapMethodStubInfo GetMethodForSoapAction (string name)
509 return (SoapMethodStubInfo) methods_byaction [name.Trim ('"',' ')];