//----------------------------------------------------------------------- // // Copyright (c) Andrew Arnott. All rights reserved. // //----------------------------------------------------------------------- namespace DotNetOAuth.Messaging { using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.Reflection; using DotNetOAuth.ChannelElements; using DotNetOAuth.Messaging.Reflection; /// /// Serializes/deserializes OAuth messages for/from transit. /// internal class MessageSerializer { /// /// The specific -derived type /// that will be serialized and deserialized using this class. /// private readonly Type messageType; /// /// Initializes a new instance of the MessageSerializer class. /// /// The specific -derived type /// that will be serialized and deserialized using this class. private MessageSerializer(Type messageType) { Debug.Assert(messageType != null, "messageType == null"); if (!typeof(IProtocolMessage).IsAssignableFrom(messageType)) { throw new ArgumentException( string.Format( CultureInfo.CurrentCulture, MessagingStrings.UnexpectedType, typeof(IProtocolMessage).FullName, messageType.FullName), "messageType"); } this.messageType = messageType; } /// /// Creates or reuses a message serializer for a given message type. /// /// The type of message that will be serialized/deserialized. /// A message serializer for the given message type. internal static MessageSerializer Get(Type messageType) { if (messageType == null) { throw new ArgumentNullException("messageType"); } return new MessageSerializer(messageType); } /// /// Reads the data from a message instance and returns a series of name=value pairs for the fields that must be included in the message. /// /// The message to be serialized. /// The dictionary of values to send for the message. [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Parallel design with Deserialize method.")] internal IDictionary Serialize(IProtocolMessage message) { if (message == null) { throw new ArgumentNullException("message"); } var result = new Reflection.MessageDictionary(message); return result; } /// /// Reads name=value pairs into an OAuth message. /// /// The name=value pairs that were read in from the transport. /// The recipient of the message. /// The instantiated and initialized instance. internal IProtocolMessage Deserialize(IDictionary fields, MessageReceivingEndpoint recipient) { if (fields == null) { throw new ArgumentNullException("fields"); } // Before we deserialize the message, make sure all the required parts are present. MessageDescription.Get(this.messageType).EnsureRequiredMessagePartsArePresent(fields.Keys); IProtocolMessage result = this.CreateMessage(recipient); foreach (var pair in fields) { IDictionary dictionary = new MessageDictionary(result); dictionary[pair.Key] = pair.Value; } result.EnsureValidMessage(); return result; } /// /// Instantiates a new message to deserialize data into. /// /// The recipient this message is directed to, if any. /// The newly created message object. private IProtocolMessage CreateMessage(MessageReceivingEndpoint recipient) { IProtocolMessage result; if (typeof(IOAuthDirectedMessage).IsAssignableFrom(this.messageType)) { // Some OAuth messages take just the recipient, while others take the whole endpoint ConstructorInfo ctor; if ((ctor = this.messageType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(Uri) }, null)) != null) { if (recipient == null) { // We need a recipient to deserialize directed messages. throw new ArgumentNullException("recipient"); } result = (IProtocolMessage)ctor.Invoke(new object[] { recipient.Location }); } else if ((ctor = this.messageType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[] { typeof(MessageReceivingEndpoint) }, null)) != null) { if (recipient == null) { // We need a recipient to deserialize directed messages. throw new ArgumentNullException("recipient"); } result = (IProtocolMessage)ctor.Invoke(new object[] { recipient }); } else if ((ctor = this.messageType.GetConstructor(BindingFlags.NonPublic | BindingFlags.Instance, null, new Type[0], null)) != null) { result = (IProtocolMessage)ctor.Invoke(new object[0]); } else { throw new InvalidOperationException("Unrecognized constructor signature on type " + this.messageType); } } else { result = (IProtocolMessage)Activator.CreateInstance(this.messageType, true); } return result; } } }