2 // DefaultMessageOperationFormatter.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
6 // Eyal Alaluf <eyala@mainsoft.com>
8 // Copyright (C) 2005-2007 Novell, Inc. http://www.novell.com
9 // Copyright (C) 2008 Mainsoft Co. http://www.mainsoft.com
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:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
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.
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;
39 using System.Xml.Serialization;
41 namespace System.ServiceModel.Dispatcher
43 // This type is introduced for moonlight compatibility.
44 internal class OperationFormatter
45 : IDispatchMessageFormatter, IClientMessageFormatter
47 BaseMessagesFormatter impl;
48 string operation_name;
50 public OperationFormatter (OperationDescription od, bool isRpc, bool isEncoded)
52 Validate (od, isRpc, isEncoded);
54 impl = BaseMessagesFormatter.Create (od);
56 operation_name = od.Name;
59 public string OperationName {
60 get { return operation_name; }
63 public bool IsValidReturnValue (MessagePartDescription part)
65 return part != null && part.Type != typeof (void);
68 void Validate (OperationDescription od, bool isRpc, bool isEncoded)
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.");
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.");
78 throw new InvalidOperationException ("This operation is defined as RPC and contains a message with void, which is not allowed.");
80 hasParameter |= !md.IsVoid;
86 public object DeserializeReply (Message message, object [] parameters)
88 return impl.DeserializeReply (message, parameters);
91 public Message SerializeRequest (MessageVersion messageVersion, object [] parameters)
93 return impl.SerializeRequest (messageVersion, parameters);
96 public void DeserializeRequest (Message message, object [] parameters)
98 impl.DeserializeRequest (message, parameters);
101 public Message SerializeReply (MessageVersion messageVersion, object [] parameters, object result)
103 return impl.SerializeReply (messageVersion, parameters, result);
107 internal abstract class BaseMessagesFormatter
108 : IDispatchMessageFormatter, IClientMessageFormatter
110 MessageDescriptionCollection messages;
112 ParameterInfo [] requestMethodParams;
113 ParameterInfo [] replyMethodParams;
115 public BaseMessagesFormatter (MessageDescriptionCollection messages)
117 this.messages = messages;
120 public BaseMessagesFormatter (OperationDescription desc)
121 : this (desc.Messages)
123 if (desc.SyncMethod != null)
126 requestMethodParams = replyMethodParams = desc.SyncMethod.GetParameters ();
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);
138 public static BaseMessagesFormatter Create (OperationDescription desc)
140 MethodInfo attrProvider = desc.SyncMethod ?? desc.BeginMethod;
143 attrs = attrProvider.GetCustomAttributes (typeof (XmlSerializerFormatAttribute), false);
144 if (attrs != null && attrs.Length > 0)
145 return new XmlMessagesFormatter (desc, (XmlSerializerFormatAttribute) attrs [0]);
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);
155 protected abstract Message PartsToMessage (
156 MessageDescription md, MessageVersion version, string action, object [] parts);
157 protected abstract object [] MessageToParts (MessageDescription md, Message message);
159 public Message SerializeRequest (
160 MessageVersion version, object [] parameters)
162 MessageDescription md = null;
163 foreach (MessageDescription mdi in messages)
164 if (mdi.Direction == MessageDirection.Input)
167 object [] parts = CreatePartsArray (md.Body);
168 if (md.MessageType != null)
169 MessageObjectToParts (md, parameters [0], parts);
172 foreach (ParameterInfo pi in requestMethodParams)
174 parts [index++] = parameters [pi.Position];
176 return PartsToMessage (md, version, md.Action, parts);
179 public Message SerializeReply (
180 MessageVersion version, object [] parameters, object result)
182 // use_response_converter
184 MessageDescription md = null;
185 foreach (MessageDescription mdi in messages)
186 if (mdi.Direction == MessageDirection.Output)
189 object [] parts = CreatePartsArray (md.Body);
190 if (md.MessageType != null)
191 MessageObjectToParts (md, result, parts);
193 if (HasReturnValue (md.Body))
195 int index = ParamsOffset (md.Body);
197 foreach (ParameterInfo pi in replyMethodParams)
198 if (pi.IsOut || pi.ParameterType.IsByRef)
199 parts [index++] = parameters [paramsIdx++];
201 string action = version.Addressing == AddressingVersion.None ? null : md.Action;
202 return PartsToMessage (md, version, action, parts);
205 public void DeserializeRequest (Message message, object [] parameters)
207 string action = message.Headers.Action;
208 MessageDescription md = messages.Find (action);
210 throw new ActionNotSupportedException (String.Format ("Action '{0}' is not supported by this operation.", action));
212 object [] parts = MessageToParts (md, message);
213 if (md.MessageType != null) {
215 parameters [0] = Activator.CreateInstance (md.MessageType);
217 parameters [0] = Activator.CreateInstance (md.MessageType, true);
219 PartsToMessageObject (md, parts, parameters [0]);
224 foreach (ParameterInfo pi in requestMethodParams)
226 parameters [index] = parts [index];
232 public object DeserializeReply (Message message, object [] parameters)
234 MessageDescription md = null;
235 foreach (MessageDescription mdi in messages)
236 if (mdi.Direction == MessageDirection.Output)
239 object [] parts = MessageToParts (md, message);
240 if (md.MessageType != null) {
242 object msgObject = Activator.CreateInstance (md.MessageType);
244 object msgObject = Activator.CreateInstance (md.MessageType, true);
246 PartsToMessageObject (md, parts, msgObject);
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;
258 void PartsToMessageObject (MessageDescription md, object [] parts, object msgObject)
260 foreach (MessagePartDescription partDesc in md.Body.Parts)
261 if (partDesc.MemberInfo is FieldInfo)
262 ((FieldInfo) partDesc.MemberInfo).SetValue (msgObject, parts [partDesc.Index]);
264 ((PropertyInfo) partDesc.MemberInfo).SetValue (msgObject, parts [partDesc.Index], null);
267 void MessageObjectToParts (MessageDescription md, object msgObject, object [] parts)
269 foreach (MessagePartDescription partDesc in md.Body.Parts)
270 if (partDesc.MemberInfo is FieldInfo)
271 parts [partDesc.Index] = ((FieldInfo) partDesc.MemberInfo).GetValue (msgObject);
273 parts [partDesc.Index] = ((PropertyInfo) partDesc.MemberInfo).GetValue (msgObject, null);
276 internal static bool HasReturnValue (MessageBodyDescription desc)
278 return desc.ReturnValue != null && desc.ReturnValue.Type != typeof (void);
281 protected static int ParamsOffset (MessageBodyDescription desc)
283 return HasReturnValue (desc) ? 1 : 0;
286 protected static object [] CreatePartsArray (MessageBodyDescription desc)
288 if (HasReturnValue (desc))
289 return new object [desc.Parts.Count + 1];
290 return new object [desc.Parts.Count];
295 class XmlMessagesFormatter : BaseMessagesFormatter
297 XmlSerializerFormatAttribute attr;
298 Dictionary<MessageBodyDescription,XmlSerializer> bodySerializers
299 = new Dictionary<MessageBodyDescription,XmlSerializer> ();
301 public XmlMessagesFormatter (OperationDescription desc, XmlSerializerFormatAttribute attr)
307 public XmlMessagesFormatter (MessageDescriptionCollection messages, XmlSerializerFormatAttribute attr)
313 private XmlReflectionMember CreateReflectionMember (MessagePartDescription partDesc, bool isReturnValue)
315 XmlReflectionMember m = new XmlReflectionMember ();
316 m.IsReturnValue = isReturnValue;
317 m.MemberName = partDesc.Name;
318 m.MemberType = partDesc.Type;
322 protected override Message PartsToMessage (
323 MessageDescription md, MessageVersion version, string action, object [] parts)
325 return Message.CreateMessage (version, action, new XmlBodyWriter (GetSerializer (md.Body), parts));
328 protected override object [] MessageToParts (MessageDescription md, Message message)
333 XmlDictionaryReader r = message.GetReaderAtBodyContents ();
334 return (object []) GetSerializer (md.Body).Deserialize (r);
337 // FIXME: Handle ServiceKnownTypes
338 XmlSerializer GetSerializer (MessageBodyDescription desc)
340 if (bodySerializers.ContainsKey (desc))
341 return bodySerializers [desc];
343 int count = desc.Parts.Count + (HasReturnValue (desc) ? 1 : 0);
344 XmlReflectionMember [] members = new XmlReflectionMember [count];
347 if (HasReturnValue (desc))
348 members [ind++] = CreateReflectionMember (desc.ReturnValue, true);
350 foreach (MessagePartDescription partDesc in desc.Parts)
351 members [ind++] = CreateReflectionMember (partDesc, false);
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];
361 class XmlBodyWriter : BodyWriter
363 XmlSerializer serializer;
366 public XmlBodyWriter (XmlSerializer serializer, object parts)
369 this.serializer = serializer;
373 protected override BodyWriter OnCreateBufferedCopy (int maxBufferSize)
375 return new XmlBodyWriter (serializer, body);
378 protected override void OnWriteBodyContents (XmlDictionaryWriter writer)
380 serializer.Serialize (writer, body);
386 class DataContractMessagesFormatter : BaseMessagesFormatter
388 DataContractFormatAttribute attr;
390 public DataContractMessagesFormatter (OperationDescription desc, DataContractFormatAttribute attr)
396 public DataContractMessagesFormatter (MessageDescriptionCollection messages, DataContractFormatAttribute attr)
402 Dictionary<MessagePartDescription, XmlObjectSerializer> serializers
403 = new Dictionary<MessagePartDescription,XmlObjectSerializer> ();
405 protected override Message PartsToMessage (
406 MessageDescription md, MessageVersion version, string action, object [] parts)
408 return Message.CreateMessage (version, action, new DataContractBodyWriter (md.Body, this, parts));
411 protected override object [] MessageToParts (
412 MessageDescription md, Message message)
417 int offset = ParamsOffset (md.Body);
418 object [] parts = CreatePartsArray (md.Body);
420 XmlDictionaryReader r = message.GetReaderAtBodyContents ();
421 if (md.Body.WrapperName != null)
422 r.ReadStartElement (md.Body.WrapperName, md.Body.WrapperNamespace);
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);
433 else // Skip unknown elements
437 if (md.Body.WrapperName != null && !r.EOF)
443 // FIXME: Handle ServiceKnownTypes
444 XmlObjectSerializer GetSerializer (MessagePartDescription partDesc)
446 if (!serializers.ContainsKey (partDesc))
447 serializers [partDesc] = new DataContractSerializer (
448 partDesc.Type, partDesc.Name, partDesc.Namespace);
449 return serializers [partDesc];
452 class DataContractBodyWriter : BodyWriter
454 MessageBodyDescription desc;
456 DataContractMessagesFormatter parent;
458 public DataContractBodyWriter (MessageBodyDescription desc, DataContractMessagesFormatter parent, object [] parts)
462 this.parent = parent;
466 protected override void OnWriteBodyContents (XmlDictionaryWriter writer)
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 ();
479 void WriteMessagePart (
480 XmlDictionaryWriter writer, MessageBodyDescription desc, MessagePartDescription partDesc, object obj)
482 parent.GetSerializer (partDesc).WriteObject (writer, obj);