2008-09-12 Astushi Enomoto <atsushi@ximian.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         internal abstract class BaseMessagesFormatter
44                 : IDispatchMessageFormatter, IClientMessageFormatter
45         {
46                 MessageDescriptionCollection messages;
47                 bool isAsync;
48                 ParameterInfo [] requestMethodParams;
49                 ParameterInfo [] replyMethodParams;
50
51                 public BaseMessagesFormatter (MessageDescriptionCollection messages)
52                 {
53                         this.messages = messages;
54                 }
55
56                 public BaseMessagesFormatter (OperationDescription desc)
57                         : this (desc.Messages)
58                 {
59                         if (desc.SyncMethod != null)
60                         {
61                                 isAsync = false;
62                                 requestMethodParams = replyMethodParams = desc.SyncMethod.GetParameters ();
63                                 return;
64                         }
65                         isAsync = true;
66                         ParameterInfo [] methodParams = desc.BeginMethod.GetParameters ();
67                         requestMethodParams = new ParameterInfo [methodParams.Length - 2];
68                         Array.Copy (methodParams, requestMethodParams, requestMethodParams.Length);
69                         methodParams = desc.EndMethod.GetParameters ();
70                         replyMethodParams = new ParameterInfo [methodParams.Length - 1];
71                         Array.Copy (methodParams, replyMethodParams, replyMethodParams.Length);
72                 }
73
74                 public static BaseMessagesFormatter Create (OperationDescription desc)
75                 {
76                         MethodInfo attrProvider = desc.SyncMethod ?? desc.BeginMethod;
77                         object [] attrs;
78 #if !NET_2_1
79                         attrs = attrProvider.GetCustomAttributes (typeof (XmlSerializerFormatAttribute), false);
80                         if (attrs != null && attrs.Length > 0)
81                                 return new XmlMessagesFormatter (desc, (XmlSerializerFormatAttribute) attrs [0]);
82 #endif
83
84                         attrs = attrProvider.GetCustomAttributes (typeof (DataContractFormatAttribute), false);
85                         DataContractFormatAttribute dataAttr = null;
86                         if (attrs != null && attrs.Length > 0)
87                                 dataAttr = (DataContractFormatAttribute) attrs [0];
88                         return new DataContractMessagesFormatter (desc, dataAttr);
89                 }
90
91                 protected abstract Message PartsToMessage (
92                         MessageDescription md, MessageVersion version, string action, object [] parts);
93                 protected abstract object [] MessageToParts (MessageDescription md, Message message);
94
95                 public Message SerializeRequest (
96                         MessageVersion version, object [] parameters)
97                 {
98                         MessageDescription md = null;
99                         foreach (MessageDescription mdi in messages)
100                                 if (mdi.Direction == MessageDirection.Input)
101                                         md = mdi;
102
103                         object [] parts = CreatePartsArray (md.Body);
104                         if (md.MessageType != null)
105                                 MessageObjectToParts (md, parameters [0], parts);
106                         else {
107                                 int index = 0;
108                                 foreach (ParameterInfo pi in requestMethodParams)
109                                         if (!pi.IsOut)
110                                                 parts [index++] = parameters [pi.Position];
111                         }
112                         return PartsToMessage (md, version, md.Action, parts);
113                 }
114
115                 public Message SerializeReply (
116                         MessageVersion version, object [] parameters, object result)
117                 {
118                         // use_response_converter
119
120                         MessageDescription md = null;
121                         foreach (MessageDescription mdi in messages)
122                                 if (mdi.Direction == MessageDirection.Output)
123                                         md = mdi;
124
125                         object [] parts = CreatePartsArray (md.Body);
126                         if (md.MessageType != null)
127                                 MessageObjectToParts (md, result, parts);
128                         else {
129                                 if (HasReturnValue (md.Body))
130                                         parts [0] = result;
131                                 int index = ParamsOffset (md.Body);
132                                 foreach (ParameterInfo pi in replyMethodParams)
133                                         if (pi.IsOut || pi.ParameterType.IsByRef)
134                                                 parts [index++] = parameters [pi.Position];
135                         }
136                         string action = version.Addressing == AddressingVersion.None ? null : md.Action;
137                         return PartsToMessage (md, version, action, parts);
138                 }
139
140                 public void DeserializeRequest (Message message, object [] parameters)
141                 {
142                         string action = message.Headers.Action;
143                         MessageDescription md = messages.Find (action);
144                         if (md == null)
145                                 throw new ActionNotSupportedException (String.Format ("Action '{0}' is not supported by this operation.", action));
146
147                         object [] parts = MessageToParts (md, message);
148                         if (md.MessageType != null) {
149                                 parameters [0] = Activator.CreateInstance (md.MessageType, true);
150                                 PartsToMessageObject (md, parts, parameters [0]);
151                         }
152                         else
153                         {
154                                 int index = 0;
155                                 foreach (ParameterInfo pi in requestMethodParams)
156                                         if (!pi.IsOut)
157                                                 parameters [pi.Position] = parts [index++];
158                         }
159                 }
160
161                 public object DeserializeReply (Message message, object [] parameters)
162                 {
163                         MessageDescription md = null;
164                         foreach (MessageDescription mdi in messages)
165                                 if (mdi.Direction == MessageDirection.Output)
166                                         md = mdi;
167
168                         object [] parts = MessageToParts (md, message);
169                         if (md.MessageType != null) {
170                                 object msgObject = Activator.CreateInstance (md.MessageType, true);
171                                 PartsToMessageObject (md, parts, msgObject);
172                                 return msgObject;
173                         }
174                         else {
175                                 int index = ParamsOffset (md.Body);
176                                 foreach (ParameterInfo pi in requestMethodParams)
177                                         if (pi.IsOut || pi.ParameterType.IsByRef)
178                                                 parameters [pi.Position] = parts [index++];
179                                 return HasReturnValue (md.Body) ? parts [0] : null;
180                         }
181                 }
182
183                 void PartsToMessageObject (MessageDescription md, object [] parts, object msgObject)
184                 {
185                         foreach (MessagePartDescription partDesc in md.Body.Parts)
186                                 if (partDesc.MemberInfo is FieldInfo)
187                                         ((FieldInfo) partDesc.MemberInfo).SetValue (msgObject, parts [partDesc.Index]);
188                                 else
189                                         ((PropertyInfo) partDesc.MemberInfo).SetValue (msgObject, parts [partDesc.Index], null);
190                 }
191
192                 void MessageObjectToParts (MessageDescription md, object msgObject, object [] parts)
193                 {
194                         foreach (MessagePartDescription partDesc in md.Body.Parts)
195                                 if (partDesc.MemberInfo is FieldInfo)
196                                         parts [partDesc.Index] = ((FieldInfo) partDesc.MemberInfo).GetValue (msgObject);
197                                 else
198                                         parts [partDesc.Index] = ((PropertyInfo) partDesc.MemberInfo).GetValue (msgObject, null);
199                 }
200
201                 internal static bool HasReturnValue (MessageBodyDescription desc)
202                 {
203                         return desc.ReturnValue != null && desc.ReturnValue.Type != typeof (void);
204                 }
205
206                 protected static int ParamsOffset (MessageBodyDescription desc)
207                 {
208                         return HasReturnValue (desc) ? 1 : 0;
209                 }
210
211                 protected static object [] CreatePartsArray (MessageBodyDescription desc)
212                 {
213                         if (HasReturnValue (desc))
214                                 return new object [desc.Parts.Count + 1];
215                         return new object [desc.Parts.Count];
216                 }
217         }
218
219 #if !NET_2_1
220         class XmlMessagesFormatter : BaseMessagesFormatter
221         {
222                 XmlSerializerFormatAttribute attr;
223                 Dictionary<MessageBodyDescription,XmlSerializer> bodySerializers
224                         = new Dictionary<MessageBodyDescription,XmlSerializer> ();
225
226                 public XmlMessagesFormatter (OperationDescription desc, XmlSerializerFormatAttribute attr)
227                         : base (desc)
228                 {
229                         this.attr = attr;
230                 }
231
232                 public XmlMessagesFormatter (MessageDescriptionCollection messages, XmlSerializerFormatAttribute attr)
233                         : base (messages)
234                 {
235                         this.attr = attr;
236                 }
237
238                 private XmlReflectionMember CreateReflectionMember (MessagePartDescription partDesc, bool isReturnValue)
239                 {
240                         XmlReflectionMember m = new XmlReflectionMember ();
241                         m.IsReturnValue = isReturnValue;
242                         m.MemberName = partDesc.Name;
243                         m.MemberType = partDesc.Type;
244                         return m;
245                 }
246
247                 protected override Message PartsToMessage (
248                         MessageDescription md, MessageVersion version, string action, object [] parts)
249                 {
250                         return Message.CreateMessage (version, action, new XmlBodyWriter (GetSerializer (md.Body), parts));
251                 }
252
253                 protected override object [] MessageToParts (MessageDescription md, Message message)
254                 {
255                         if (message.IsEmpty)
256                                 return null;
257                                 
258                         XmlDictionaryReader r = message.GetReaderAtBodyContents ();
259                         return (object []) GetSerializer (md.Body).Deserialize (r);
260                 }
261
262                 // FIXME: Handle ServiceKnownTypes
263                 XmlSerializer GetSerializer (MessageBodyDescription desc)
264                 {
265                         if (bodySerializers.ContainsKey (desc))
266                                 return bodySerializers [desc];
267
268                         int count = desc.Parts.Count + (HasReturnValue (desc) ? 1 : 0);
269                         XmlReflectionMember [] members = new XmlReflectionMember [count];
270
271                         int ind = 0;
272                         if (HasReturnValue (desc))
273                                 members [ind++] = CreateReflectionMember (desc.ReturnValue, true);
274
275                         foreach (MessagePartDescription partDesc in desc.Parts)
276                                 members [ind++] = CreateReflectionMember (partDesc, false);
277
278                         // FIXME: Register known types into xmlImporter.
279                         XmlReflectionImporter xmlImporter = new XmlReflectionImporter ();
280                         XmlMembersMapping [] partsMapping = new XmlMembersMapping [1];
281                         partsMapping [0] = xmlImporter.ImportMembersMapping (desc.WrapperName, desc.WrapperNamespace, members, true);
282                         bodySerializers [desc] = XmlSerializer.FromMappings (partsMapping) [0];
283                         return bodySerializers [desc];
284                 }
285
286                 class XmlBodyWriter : BodyWriter
287                 {
288                         XmlSerializer serializer;
289                         object body;
290
291                         public XmlBodyWriter (XmlSerializer serializer, object parts)
292                                 : base (false)
293                         {
294                                 this.serializer = serializer;
295                                 this.body = parts;
296                         }
297
298                         [MonoTODO]
299                         protected override BodyWriter OnCreateBufferedCopy (int maxBufferSize)
300                         {
301                                 throw new NotSupportedException ();
302                         }
303
304                         protected override void OnWriteBodyContents (XmlDictionaryWriter writer)
305                         {
306                                 serializer.Serialize (writer, body);
307                         }
308                 }
309         }
310 #endif
311
312         class DataContractMessagesFormatter : BaseMessagesFormatter
313         {
314                 DataContractFormatAttribute attr;
315
316                 public DataContractMessagesFormatter (OperationDescription desc, DataContractFormatAttribute attr)
317                         : base (desc)
318                 {
319                         this.attr = attr;
320                 }
321
322                 public DataContractMessagesFormatter (MessageDescriptionCollection messages, DataContractFormatAttribute attr)
323                         : base (messages)
324                 {
325                         this.attr = attr;
326                 }
327
328                 Dictionary<MessagePartDescription, XmlObjectSerializer> serializers
329                         = new Dictionary<MessagePartDescription,XmlObjectSerializer> ();
330
331                 protected override Message PartsToMessage (
332                         MessageDescription md, MessageVersion version, string action, object [] parts)
333                 {
334                         return Message.CreateMessage (version, action, new DataContractBodyWriter (md.Body, this, parts));
335                 }
336
337                 protected override object [] MessageToParts (
338                         MessageDescription md, Message message)
339                 {
340                         if (message.IsEmpty)
341                                 return null;
342
343                         int offset = ParamsOffset (md.Body);
344                         object [] parts = CreatePartsArray (md.Body);
345
346                         XmlDictionaryReader r = message.GetReaderAtBodyContents ();
347                         if (md.Body.WrapperName != null)
348                                 r.ReadStartElement (md.Body.WrapperName, md.Body.WrapperNamespace);
349
350                         for (r.MoveToContent (); r.NodeType == XmlNodeType.Element; r.MoveToContent ()) {
351                                 XmlQualifiedName key = new XmlQualifiedName (r.LocalName, r.NamespaceURI);
352                                 MessagePartDescription rv = md.Body.ReturnValue;
353                                 if (rv != null && rv.Name == key.Name && rv.Namespace == key.Namespace)
354                                         parts [0] = GetSerializer (md.Body.ReturnValue).ReadObject (r);
355                                 else if (md.Body.Parts.Contains (key)) {
356                                         MessagePartDescription p = md.Body.Parts [key];
357                                         parts [p.Index + offset] = GetSerializer (p).ReadObject (r);
358                                 }
359                                 else // Skip unknown elements
360                                         r.Skip ();
361                         }
362
363                         if (md.Body.WrapperName != null && !r.EOF)
364                                 r.ReadEndElement ();
365
366                         return parts;
367                 }
368
369                 // FIXME: Handle ServiceKnownTypes
370                 XmlObjectSerializer GetSerializer (MessagePartDescription partDesc)
371                 {
372                         if (!serializers.ContainsKey (partDesc))
373                                 serializers [partDesc] = new DataContractSerializer (
374                                         partDesc.Type, partDesc.Name, partDesc.Namespace);
375                         return serializers [partDesc];
376                 }
377
378                 class DataContractBodyWriter : BodyWriter
379                 {
380                         MessageBodyDescription desc;
381                         object [] parts;
382                         DataContractMessagesFormatter parent;
383
384                         public DataContractBodyWriter (MessageBodyDescription desc, DataContractMessagesFormatter parent, object [] parts)
385                                 : base (false)
386                         {
387                                 this.desc = desc;
388                                 this.parent = parent;
389                                 this.parts = parts;
390                         }
391
392                         protected override void OnWriteBodyContents (XmlDictionaryWriter writer)
393                         {
394                                 int offset = HasReturnValue (desc) ? 1 : 0;
395                                 if (desc.WrapperName != null)
396                                         writer.WriteStartElement (desc.WrapperName, desc.WrapperNamespace);
397                                 if (HasReturnValue (desc))
398                                         WriteMessagePart (writer, desc, desc.ReturnValue, parts [0]);
399                                 foreach (MessagePartDescription partDesc in desc.Parts)
400                                         WriteMessagePart (writer, desc, partDesc, parts [partDesc.Index + offset]);
401                                 if (desc.WrapperName != null)
402                                         writer.WriteEndElement ();
403                         }
404
405                         void WriteMessagePart (
406                                 XmlDictionaryWriter writer, MessageBodyDescription desc, MessagePartDescription partDesc, object obj)
407                         {
408                                 parent.GetSerializer (partDesc).WriteObject (writer, obj);
409                         }
410                 }
411         }
412 }