2010-03-12 Jb Evain <jbevain@novell.com>
[mono.git] / mcs / class / System.ServiceModel / System.ServiceModel.Dispatcher / BaseMessagesFormatter.cs
1 //
2 // DefaultMessageOperationFormatter.cs
3 //
4 // Author:
5 //      Atsushi Enomoto <atsushi@ximian.com>
6 //      Eyal Alaluf <eyala@mainsoft.com>
7 //
8 // Copyright (C) 2005-2007 Novell, Inc.  http://www.novell.com
9 // Copyright (C) 2008 Mainsoft Co. http://www.mainsoft.com
10 //
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
18 // 
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
21 // 
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 //
30 using System;
31 using System.Collections.Generic;
32 using System.Reflection;
33 using System.Runtime.Serialization;
34 using System.ServiceModel;
35 using System.ServiceModel.Channels;
36 using System.ServiceModel.Description;
37 using System.Text;
38 using System.Xml;
39 using System.Xml.Serialization;
40
41 namespace System.ServiceModel.Dispatcher
42 {
43         // This type is introduced for moonlight compatibility.
44         internal class OperationFormatter
45                 : IDispatchMessageFormatter, IClientMessageFormatter
46         {
47                 BaseMessagesFormatter impl;
48                 string operation_name;
49
50                 public OperationFormatter (OperationDescription od, bool isRpc, bool isEncoded)
51                 {
52                         Validate (od, isRpc, isEncoded);
53
54                         impl = BaseMessagesFormatter.Create (od);
55
56                         operation_name = od.Name;
57                 }
58
59                 public string OperationName {
60                         get { return operation_name; }
61                 }
62
63                 public bool IsValidReturnValue (MessagePartDescription part)
64                 {
65                         return part != null && part.Type != typeof (void);
66                 }
67
68                 void Validate (OperationDescription od, bool isRpc, bool isEncoded)
69                 {
70                         bool hasParameter = false, hasVoid = false;
71                         foreach (var md in od.Messages) {
72                                 if (md.IsTypedMessage || md.IsUntypedMessage) {
73                                         if (isRpc && !isEncoded)
74                                                 throw new InvalidOperationException ("Message with action {0} is either strongly-typed or untyped, but defined as RPC and encoded.");
75                                         if (hasParameter)
76                                                 throw new InvalidOperationException ("This operation contains a message with parameters. Strongly-typed or untyped message can be paired only with strongly-typed, untyped or void message.");
77                                         if (hasVoid)
78                                                 throw new InvalidOperationException ("This operation is defined as RPC and contains a message with void, which is not allowed.");
79                                 } else {
80                                         hasParameter |= !md.IsVoid;
81                                         hasVoid |= md.IsVoid;
82                                 }
83                         }
84                 }
85
86                 public object DeserializeReply (Message message, object [] parameters)
87                 {
88                         return impl.DeserializeReply (message, parameters);
89                 }
90
91                 public Message SerializeRequest (MessageVersion messageVersion, object [] parameters)
92                 {
93                         return impl.SerializeRequest (messageVersion, parameters);
94                 }
95
96                 public void DeserializeRequest (Message message, object [] parameters)
97                 {
98                         impl.DeserializeRequest (message, parameters);
99                 }
100
101                 public Message SerializeReply (MessageVersion messageVersion, object [] parameters, object result)
102                 {
103                         return impl.SerializeReply (messageVersion, parameters, result);
104                 }
105         }
106
107         internal abstract class BaseMessagesFormatter
108                 : IDispatchMessageFormatter, IClientMessageFormatter
109         {
110                 MessageDescriptionCollection messages;
111                 bool isAsync;
112                 ParameterInfo [] requestMethodParams;
113                 ParameterInfo [] replyMethodParams;
114
115                 public BaseMessagesFormatter (MessageDescriptionCollection messages)
116                 {
117                         this.messages = messages;
118                 }
119
120                 public BaseMessagesFormatter (OperationDescription desc)
121                         : this (desc.Messages)
122                 {
123                         if (desc.SyncMethod != null)
124                         {
125                                 isAsync = false;
126                                 requestMethodParams = replyMethodParams = desc.SyncMethod.GetParameters ();
127                                 return;
128                         }
129                         isAsync = true;
130                         ParameterInfo [] methodParams = desc.BeginMethod.GetParameters ();
131                         requestMethodParams = new ParameterInfo [methodParams.Length - 2];
132                         Array.Copy (methodParams, requestMethodParams, requestMethodParams.Length);
133                         methodParams = desc.EndMethod.GetParameters ();
134                         replyMethodParams = new ParameterInfo [methodParams.Length - 1];
135                         Array.Copy (methodParams, replyMethodParams, replyMethodParams.Length);
136                 }
137
138                 public static BaseMessagesFormatter Create (OperationDescription desc)
139                 {
140                         MethodInfo attrProvider = desc.SyncMethod ?? desc.BeginMethod;
141                         object [] attrs;
142 #if !NET_2_1
143                         attrs = attrProvider.GetCustomAttributes (typeof (XmlSerializerFormatAttribute), false);
144                         if (attrs != null && attrs.Length > 0)
145                                 return new XmlMessagesFormatter (desc, (XmlSerializerFormatAttribute) attrs [0]);
146 #endif
147
148                         attrs = attrProvider.GetCustomAttributes (typeof (DataContractFormatAttribute), false);
149                         DataContractFormatAttribute dataAttr = null;
150                         if (attrs != null && attrs.Length > 0)
151                                 dataAttr = (DataContractFormatAttribute) attrs [0];
152                         return new DataContractMessagesFormatter (desc, dataAttr);
153                 }
154
155                 protected abstract Message PartsToMessage (
156                         MessageDescription md, MessageVersion version, string action, object [] parts);
157                 protected abstract object [] MessageToParts (MessageDescription md, Message message);
158
159                 public Message SerializeRequest (
160                         MessageVersion version, object [] parameters)
161                 {
162                         MessageDescription md = null;
163                         foreach (MessageDescription mdi in messages)
164                                 if (mdi.Direction == MessageDirection.Input)
165                                         md = mdi;
166
167                         object [] parts = CreatePartsArray (md.Body);
168                         if (md.MessageType != null)
169                                 MessageObjectToParts (md, parameters [0], parts);
170                         else {
171                                 int index = 0;
172                                 foreach (ParameterInfo pi in requestMethodParams)
173                                         if (!pi.IsOut)
174                                                 parts [index++] = parameters [pi.Position];
175                         }
176                         return PartsToMessage (md, version, md.Action, parts);
177                 }
178
179                 public Message SerializeReply (
180                         MessageVersion version, object [] parameters, object result)
181                 {
182                         // use_response_converter
183
184                         MessageDescription md = null;
185                         foreach (MessageDescription mdi in messages)
186                                 if (mdi.Direction == MessageDirection.Output)
187                                         md = mdi;
188
189                         object [] parts = CreatePartsArray (md.Body);
190                         if (md.MessageType != null)
191                                 MessageObjectToParts (md, result, parts);
192                         else {
193                                 if (HasReturnValue (md.Body))
194                                         parts [0] = result;
195                                 int index = ParamsOffset (md.Body);
196                                 int paramsIdx = 0;
197                                 foreach (ParameterInfo pi in replyMethodParams)
198                                         if (pi.IsOut || pi.ParameterType.IsByRef)
199                                 parts [index++] = parameters [paramsIdx++];
200                         }
201                         string action = version.Addressing == AddressingVersion.None ? null : md.Action;
202                         return PartsToMessage (md, version, action, parts);
203                 }
204
205                 public void DeserializeRequest (Message message, object [] parameters)
206                 {
207                         string action = message.Headers.Action;
208                         MessageDescription md = messages.Find (action);
209                         if (md == null)
210                                 throw new ActionNotSupportedException (String.Format ("Action '{0}' is not supported by this operation.", action));
211
212                         object [] parts = MessageToParts (md, message);
213                         if (md.MessageType != null) {
214 #if NET_2_1
215                                 parameters [0] = Activator.CreateInstance (md.MessageType);
216 #else
217                                 parameters [0] = Activator.CreateInstance (md.MessageType, true);
218 #endif
219                                 PartsToMessageObject (md, parts, parameters [0]);
220                         }
221                         else
222                         {
223                                 int index = 0;
224                                 foreach (ParameterInfo pi in requestMethodParams)
225                                         if (!pi.IsOut) {
226                                                 parameters [index] = parts [index];
227                                                 index++;
228                                         }
229                         }
230                 }
231
232                 public object DeserializeReply (Message message, object [] parameters)
233                 {
234                         MessageDescription md = null;
235                         foreach (MessageDescription mdi in messages)
236                                 if (mdi.Direction == MessageDirection.Output)
237                                         md = mdi;
238
239                         object [] parts = MessageToParts (md, message);
240                         if (md.MessageType != null) {
241 #if NET_2_1
242                                 object msgObject = Activator.CreateInstance (md.MessageType);
243 #else
244                                 object msgObject = Activator.CreateInstance (md.MessageType, true);
245 #endif
246                                 PartsToMessageObject (md, parts, msgObject);
247                                 return msgObject;
248                         }
249                         else {
250                                 int index = ParamsOffset (md.Body);
251                                 foreach (ParameterInfo pi in requestMethodParams)
252                                         if (pi.IsOut || pi.ParameterType.IsByRef)
253                                                 parameters [pi.Position] = parts [index++];
254                                 return HasReturnValue (md.Body) ? parts [0] : null;
255                         }
256                 }
257
258                 void PartsToMessageObject (MessageDescription md, object [] parts, object msgObject)
259                 {
260                         foreach (MessagePartDescription partDesc in md.Body.Parts)
261                                 if (partDesc.MemberInfo is FieldInfo)
262                                         ((FieldInfo) partDesc.MemberInfo).SetValue (msgObject, parts [partDesc.Index]);
263                                 else
264                                         ((PropertyInfo) partDesc.MemberInfo).SetValue (msgObject, parts [partDesc.Index], null);
265                 }
266
267                 void MessageObjectToParts (MessageDescription md, object msgObject, object [] parts)
268                 {
269                         foreach (MessagePartDescription partDesc in md.Body.Parts)
270                                 if (partDesc.MemberInfo is FieldInfo)
271                                         parts [partDesc.Index] = ((FieldInfo) partDesc.MemberInfo).GetValue (msgObject);
272                                 else
273                                         parts [partDesc.Index] = ((PropertyInfo) partDesc.MemberInfo).GetValue (msgObject, null);
274                 }
275
276                 internal static bool HasReturnValue (MessageBodyDescription desc)
277                 {
278                         return desc.ReturnValue != null && desc.ReturnValue.Type != typeof (void);
279                 }
280
281                 protected static int ParamsOffset (MessageBodyDescription desc)
282                 {
283                         return HasReturnValue (desc) ? 1 : 0;
284                 }
285
286                 protected static object [] CreatePartsArray (MessageBodyDescription desc)
287                 {
288                         if (HasReturnValue (desc))
289                                 return new object [desc.Parts.Count + 1];
290                         return new object [desc.Parts.Count];
291                 }
292         }
293
294 #if !NET_2_1
295         class XmlMessagesFormatter : BaseMessagesFormatter
296         {
297                 XmlSerializerFormatAttribute attr;
298                 Dictionary<MessageBodyDescription,XmlSerializer> bodySerializers
299                         = new Dictionary<MessageBodyDescription,XmlSerializer> ();
300
301                 public XmlMessagesFormatter (OperationDescription desc, XmlSerializerFormatAttribute attr)
302                         : base (desc)
303                 {
304                         this.attr = attr;
305                 }
306
307                 public XmlMessagesFormatter (MessageDescriptionCollection messages, XmlSerializerFormatAttribute attr)
308                         : base (messages)
309                 {
310                         this.attr = attr;
311                 }
312
313                 private XmlReflectionMember CreateReflectionMember (MessagePartDescription partDesc, bool isReturnValue)
314                 {
315                         XmlReflectionMember m = new XmlReflectionMember ();
316                         m.IsReturnValue = isReturnValue;
317                         m.MemberName = partDesc.Name;
318                         m.MemberType = partDesc.Type;
319                         return m;
320                 }
321
322                 protected override Message PartsToMessage (
323                         MessageDescription md, MessageVersion version, string action, object [] parts)
324                 {
325                         return Message.CreateMessage (version, action, new XmlBodyWriter (GetSerializer (md.Body), parts));
326                 }
327
328                 protected override object [] MessageToParts (MessageDescription md, Message message)
329                 {
330                         if (message.IsEmpty)
331                                 return null;
332                                 
333                         XmlDictionaryReader r = message.GetReaderAtBodyContents ();
334                         return (object []) GetSerializer (md.Body).Deserialize (r);
335                 }
336
337                 // FIXME: Handle ServiceKnownTypes
338                 XmlSerializer GetSerializer (MessageBodyDescription desc)
339                 {
340                         if (bodySerializers.ContainsKey (desc))
341                                 return bodySerializers [desc];
342
343                         int count = desc.Parts.Count + (HasReturnValue (desc) ? 1 : 0);
344                         XmlReflectionMember [] members = new XmlReflectionMember [count];
345
346                         int ind = 0;
347                         if (HasReturnValue (desc))
348                                 members [ind++] = CreateReflectionMember (desc.ReturnValue, true);
349
350                         foreach (MessagePartDescription partDesc in desc.Parts)
351                                 members [ind++] = CreateReflectionMember (partDesc, false);
352
353                         // FIXME: Register known types into xmlImporter.
354                         XmlReflectionImporter xmlImporter = new XmlReflectionImporter ();
355                         XmlMembersMapping [] partsMapping = new XmlMembersMapping [1];
356                         partsMapping [0] = xmlImporter.ImportMembersMapping (desc.WrapperName, desc.WrapperNamespace, members, true);
357                         bodySerializers [desc] = XmlSerializer.FromMappings (partsMapping) [0];
358                         return bodySerializers [desc];
359                 }
360
361                 class XmlBodyWriter : BodyWriter
362                 {
363                         XmlSerializer serializer;
364                         object body;
365
366                         public XmlBodyWriter (XmlSerializer serializer, object parts)
367                                 : base (false)
368                         {
369                                 this.serializer = serializer;
370                                 this.body = parts;
371                         }
372
373                         protected override BodyWriter OnCreateBufferedCopy (int maxBufferSize)
374                         {
375                                 return new XmlBodyWriter (serializer, body);
376                         }
377
378                         protected override void OnWriteBodyContents (XmlDictionaryWriter writer)
379                         {
380                                 serializer.Serialize (writer, body);
381                         }
382                 }
383         }
384 #endif
385
386         class DataContractMessagesFormatter : BaseMessagesFormatter
387         {
388                 DataContractFormatAttribute attr;
389
390                 public DataContractMessagesFormatter (OperationDescription desc, DataContractFormatAttribute attr)
391                         : base (desc)
392                 {
393                         this.attr = attr;
394                 }
395
396                 public DataContractMessagesFormatter (MessageDescriptionCollection messages, DataContractFormatAttribute attr)
397                         : base (messages)
398                 {
399                         this.attr = attr;
400                 }
401
402                 Dictionary<MessagePartDescription, XmlObjectSerializer> serializers
403                         = new Dictionary<MessagePartDescription,XmlObjectSerializer> ();
404
405                 protected override Message PartsToMessage (
406                         MessageDescription md, MessageVersion version, string action, object [] parts)
407                 {
408                         return Message.CreateMessage (version, action, new DataContractBodyWriter (md.Body, this, parts));
409                 }
410
411                 protected override object [] MessageToParts (
412                         MessageDescription md, Message message)
413                 {
414                         if (message.IsEmpty)
415                                 return null;
416
417                         int offset = ParamsOffset (md.Body);
418                         object [] parts = CreatePartsArray (md.Body);
419
420                         XmlDictionaryReader r = message.GetReaderAtBodyContents ();
421                         if (md.Body.WrapperName != null)
422                                 r.ReadStartElement (md.Body.WrapperName, md.Body.WrapperNamespace);
423
424                         for (r.MoveToContent (); r.NodeType == XmlNodeType.Element; r.MoveToContent ()) {
425                                 XmlQualifiedName key = new XmlQualifiedName (r.LocalName, r.NamespaceURI);
426                                 MessagePartDescription rv = md.Body.ReturnValue;
427                                 if (rv != null && rv.Name == key.Name && rv.Namespace == key.Namespace)
428                                         parts [0] = GetSerializer (md.Body.ReturnValue).ReadObject (r);
429                                 else if (md.Body.Parts.Contains (key)) {
430                                         MessagePartDescription p = md.Body.Parts [key];
431                                         parts [p.Index + offset] = GetSerializer (p).ReadObject (r);
432                                 }
433                                 else // Skip unknown elements
434                                         r.Skip ();
435                         }
436
437                         if (md.Body.WrapperName != null && !r.EOF)
438                                 r.ReadEndElement ();
439
440                         return parts;
441                 }
442
443                 // FIXME: Handle ServiceKnownTypes
444                 XmlObjectSerializer GetSerializer (MessagePartDescription partDesc)
445                 {
446                         if (!serializers.ContainsKey (partDesc))
447                                 serializers [partDesc] = new DataContractSerializer (
448                                         partDesc.Type, partDesc.Name, partDesc.Namespace);
449                         return serializers [partDesc];
450                 }
451
452                 class DataContractBodyWriter : BodyWriter
453                 {
454                         MessageBodyDescription desc;
455                         object [] parts;
456                         DataContractMessagesFormatter parent;
457
458                         public DataContractBodyWriter (MessageBodyDescription desc, DataContractMessagesFormatter parent, object [] parts)
459                                 : base (false)
460                         {
461                                 this.desc = desc;
462                                 this.parent = parent;
463                                 this.parts = parts;
464                         }
465
466                         protected override void OnWriteBodyContents (XmlDictionaryWriter writer)
467                         {
468                                 int offset = HasReturnValue (desc) ? 1 : 0;
469                                 if (desc.WrapperName != null)
470                                         writer.WriteStartElement (desc.WrapperName, desc.WrapperNamespace);
471                                 if (HasReturnValue (desc))
472                                         WriteMessagePart (writer, desc, desc.ReturnValue, parts [0]);
473                                 foreach (MessagePartDescription partDesc in desc.Parts)
474                                         WriteMessagePart (writer, desc, partDesc, parts [partDesc.Index + offset]);
475                                 if (desc.WrapperName != null)
476                                         writer.WriteEndElement ();
477                         }
478
479                         void WriteMessagePart (
480                                 XmlDictionaryWriter writer, MessageBodyDescription desc, MessagePartDescription partDesc, object obj)
481                         {
482                                 parent.GetSerializer (partDesc).WriteObject (writer, obj);
483                         }
484                 }
485         }
486 }