summaryrefslogtreecommitdiffstats
path: root/src/DotNetOAuth/Messaging
diff options
context:
space:
mode:
Diffstat (limited to 'src/DotNetOAuth/Messaging')
-rw-r--r--src/DotNetOAuth/Messaging/Bindings/ExpiredMessageException.cs3
-rw-r--r--src/DotNetOAuth/Messaging/Channel.cs33
-rw-r--r--src/DotNetOAuth/Messaging/HttpRequestInfo.cs6
-rw-r--r--src/DotNetOAuth/Messaging/MessagePartAttribute.cs25
-rw-r--r--src/DotNetOAuth/Messaging/MessageScheme.cs4
-rw-r--r--src/DotNetOAuth/Messaging/MessageSerializer.cs3
-rw-r--r--src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs2
-rw-r--r--src/DotNetOAuth/Messaging/MessagingStrings.resx2
-rw-r--r--src/DotNetOAuth/Messaging/Reflection/MessageDescription.cs59
-rw-r--r--src/DotNetOAuth/Messaging/Reflection/MessageDictionary.cs203
-rw-r--r--src/DotNetOAuth/Messaging/Reflection/MessagePart.cs187
-rw-r--r--src/DotNetOAuth/Messaging/Reflection/ValueMapping.cs15
12 files changed, 415 insertions, 127 deletions
diff --git a/src/DotNetOAuth/Messaging/Bindings/ExpiredMessageException.cs b/src/DotNetOAuth/Messaging/Bindings/ExpiredMessageException.cs
index df4c446..f983999 100644
--- a/src/DotNetOAuth/Messaging/Bindings/ExpiredMessageException.cs
+++ b/src/DotNetOAuth/Messaging/Bindings/ExpiredMessageException.cs
@@ -6,6 +6,7 @@
namespace DotNetOAuth.Messaging.Bindings {
using System;
+using System.Globalization;
/// <summary>
/// An exception thrown when a message is received that exceeds the maximum message age limit.
@@ -18,7 +19,7 @@ namespace DotNetOAuth.Messaging.Bindings {
/// <param name="utcExpirationDate">The date the message expired.</param>
/// <param name="faultedMessage">The expired message.</param>
public ExpiredMessageException(DateTime utcExpirationDate, IProtocolMessage faultedMessage)
- : base(string.Format(MessagingStrings.ExpiredMessage, utcExpirationDate.ToUniversalTime(), DateTime.UtcNow), faultedMessage) {
+ : base(string.Format(CultureInfo.CurrentCulture, MessagingStrings.ExpiredMessage, utcExpirationDate.ToUniversalTime(), DateTime.UtcNow), faultedMessage) {
}
/// <summary>
diff --git a/src/DotNetOAuth/Messaging/Channel.cs b/src/DotNetOAuth/Messaging/Channel.cs
index db6cc10..87c3bbe 100644
--- a/src/DotNetOAuth/Messaging/Channel.cs
+++ b/src/DotNetOAuth/Messaging/Channel.cs
@@ -16,7 +16,6 @@ namespace DotNetOAuth.Messaging {
using System.Text;
using System.Web;
using DotNetOAuth.Messaging.Reflection;
- using DotNetOAuth.Messaging.Bindings;
/// <summary>
/// Manages sending direct messages to a remote party and receiving responses.
@@ -334,7 +333,7 @@ namespace DotNetOAuth.Messaging {
}
WebHeaderCollection headers = new WebHeaderCollection();
- StringWriter bodyWriter = new StringWriter();
+ StringWriter bodyWriter = new StringWriter(CultureInfo.InvariantCulture);
StringBuilder hiddenFields = new StringBuilder();
foreach (var field in fields) {
hiddenFields.AppendFormat(
@@ -436,7 +435,7 @@ namespace DotNetOAuth.Messaging {
}
int countProtectionsOfThisKind = protectionElements.Count(element => (element.Protection & protectionKind) == protectionKind);
-
+
// Each protection binding element is backed by the presence of its dependent protection(s).
if (countProtectionsOfThisKind > 0 && !wasLastProtectionPresent) {
throw new ProtocolException(
@@ -485,6 +484,22 @@ namespace DotNetOAuth.Messaging {
}
/// <summary>
+ /// Verifies that all required message parts are initialized to values
+ /// prior to sending the message to a remote party.
+ /// </summary>
+ /// <param name="message">The message to verify.</param>
+ /// <exception cref="ProtocolException">
+ /// Thrown when any required message part does not have a value.
+ /// </exception>
+ private static void EnsureValidMessageParts(IProtocolMessage message) {
+ Debug.Assert(message != null, "message == null");
+
+ MessageDictionary dictionary = new MessageDictionary(message);
+ MessageDescription description = MessageDescription.Get(message.GetType());
+ description.EnsureRequiredMessagePartsArePresent(dictionary.Keys);
+ }
+
+ /// <summary>
/// Prepares a message for transmit by applying signatures, nonces, etc.
/// </summary>
/// <param name="message">The message to prepare for sending.</param>
@@ -530,16 +545,10 @@ namespace DotNetOAuth.Messaging {
throw new UnprotectedMessageException(message, appliedProtection);
}
- EnsureValidMessageParts(message);
+ // We do NOT verify that all required message parts are present here... the
+ // message deserializer did for us. It would be too late to do it here since
+ // they might look initialized by the time we have an IProtocolMessage instance.
message.EnsureValidMessage();
}
-
- private void EnsureValidMessageParts(IProtocolMessage message) {
- Debug.Assert(message != null, "message == null");
-
- MessageDictionary dictionary = new MessageDictionary(message);
- MessageDescription description = MessageDescription.Get(message.GetType());
- description.EnsureRequiredMessagePartsArePresent(dictionary.Keys);
- }
}
}
diff --git a/src/DotNetOAuth/Messaging/HttpRequestInfo.cs b/src/DotNetOAuth/Messaging/HttpRequestInfo.cs
index 9a583c0..65b5dcb 100644
--- a/src/DotNetOAuth/Messaging/HttpRequestInfo.cs
+++ b/src/DotNetOAuth/Messaging/HttpRequestInfo.cs
@@ -59,19 +59,19 @@ namespace DotNetOAuth.Messaging {
/// Gets or sets the verb in the request (i.e. GET, POST, etc.)
/// </summary>
internal string HttpMethod { get; set; }
-
+
/// <summary>
/// Gets or sets the entire URL of the request.
/// </summary>
internal Uri Url { get; set; }
-
+
/// <summary>
/// Gets the query part of the URL (The ? and everything after it).
/// </summary>
internal string Query {
get { return this.Url != null ? this.Url.Query : null; }
}
-
+
/// <summary>
/// Gets or sets the collection of headers that came in with the request.
/// </summary>
diff --git a/src/DotNetOAuth/Messaging/MessagePartAttribute.cs b/src/DotNetOAuth/Messaging/MessagePartAttribute.cs
index a8de784..ae88b61 100644
--- a/src/DotNetOAuth/Messaging/MessagePartAttribute.cs
+++ b/src/DotNetOAuth/Messaging/MessagePartAttribute.cs
@@ -9,24 +9,49 @@ namespace DotNetOAuth.Messaging {
using System.Net.Security;
using System.Reflection;
+ /// <summary>
+ /// Applied to fields and properties that form a key/value in a protocol message.
+ /// </summary>
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Field, Inherited = true, AllowMultiple = false)]
internal sealed class MessagePartAttribute : Attribute {
+ /// <summary>
+ /// The overridden name to use as the serialized name for the property.
+ /// </summary>
private string name;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessagePartAttribute"/> class.
+ /// </summary>
internal MessagePartAttribute() {
}
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessagePartAttribute"/> class.
+ /// </summary>
+ /// <param name="name">
+ /// A special name to give the value of this member in the serialized message.
+ /// When null or empty, the name of the member will be used in the serialized message.
+ /// </param>
internal MessagePartAttribute(string name) {
this.Name = name;
}
+ /// <summary>
+ /// Gets or sets the name of the serialized form of this member in the message.
+ /// </summary>
public string Name {
get { return this.name; }
set { this.name = string.IsNullOrEmpty(value) ? null : value; }
}
+ /// <summary>
+ /// Gets or sets the level of protection required by this member in the serialized message.
+ /// </summary>
public ProtectionLevel RequiredProtection { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether this member is a required part of the serialized message.
+ /// </summary>
public bool IsRequired { get; set; }
}
}
diff --git a/src/DotNetOAuth/Messaging/MessageScheme.cs b/src/DotNetOAuth/Messaging/MessageScheme.cs
index ee91740..2c0aec1 100644
--- a/src/DotNetOAuth/Messaging/MessageScheme.cs
+++ b/src/DotNetOAuth/Messaging/MessageScheme.cs
@@ -16,7 +16,7 @@ namespace DotNetOAuth.Messaging {
/// In the HTTP Authorization header as defined in OAuth HTTP Authorization Scheme (OAuth HTTP Authorization Scheme).
/// </summary>
AuthorizationHeaderRequest,
-
+
/// <summary>
/// As the HTTP POST request body with a content-type of application/x-www-form-urlencoded.
/// </summary>
@@ -26,7 +26,7 @@ namespace DotNetOAuth.Messaging {
/// Added to the URLs in the query part (as defined by [RFC3986] (Berners-Lee, T., “Uniform Resource Identifiers (URI): Generic Syntax,” .) section 3).
/// </summary>
GetRequest,
-
+
/// <summary>
/// Response parameters are sent by the Service Provider to return Tokens and other information to the Consumer in the HTTP response body. The parameter names and values are first encoded as per Parameter Encoding (Parameter Encoding), and concatenated with the ‘&amp;’ character (ASCII code 38) as defined in [RFC3986] (Berners-Lee, T., “Uniform Resource Identifiers (URI): Generic Syntax,” .) Section 2.1.
/// </summary>
diff --git a/src/DotNetOAuth/Messaging/MessageSerializer.cs b/src/DotNetOAuth/Messaging/MessageSerializer.cs
index 6edb0b6..800f691 100644
--- a/src/DotNetOAuth/Messaging/MessageSerializer.cs
+++ b/src/DotNetOAuth/Messaging/MessageSerializer.cs
@@ -64,6 +64,7 @@ namespace DotNetOAuth.Messaging {
/// </summary>
/// <param name="message">The message to be serialized.</param>
/// <returns>The dictionary of values to send for the message.</returns>
+ [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic", Justification = "Parallel design with Deserialize method.")]
internal IDictionary<string, string> Serialize(IProtocolMessage message) {
if (message == null) {
throw new ArgumentNullException("message");
@@ -87,7 +88,7 @@ namespace DotNetOAuth.Messaging {
// Before we deserialize the message, make sure all the required parts are present.
MessageDescription.Get(this.messageType).EnsureRequiredMessagePartsArePresent(fields.Keys);
- IProtocolMessage result ;
+ IProtocolMessage result;
try {
result = (IProtocolMessage)Activator.CreateInstance(this.messageType, true);
} catch (MissingMethodException ex) {
diff --git a/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs b/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs
index 9386f53..60e7c02 100644
--- a/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs
+++ b/src/DotNetOAuth/Messaging/MessagingStrings.Designer.cs
@@ -97,7 +97,7 @@ namespace DotNetOAuth.Messaging {
}
/// <summary>
- /// Looks up a localized string similar to Error occurred while sending a direct message or gettings the response..
+ /// Looks up a localized string similar to Error occurred while sending a direct message or getting the response..
/// </summary>
internal static string ErrorInRequestReplyMessage {
get {
diff --git a/src/DotNetOAuth/Messaging/MessagingStrings.resx b/src/DotNetOAuth/Messaging/MessagingStrings.resx
index bdbd212..b89ef4b 100644
--- a/src/DotNetOAuth/Messaging/MessagingStrings.resx
+++ b/src/DotNetOAuth/Messaging/MessagingStrings.resx
@@ -130,7 +130,7 @@
<value>The directed message's Recipient property must not be null.</value>
</data>
<data name="ErrorInRequestReplyMessage" xml:space="preserve">
- <value>Error occurred while sending a direct message or gettings the response.</value>
+ <value>Error occurred while sending a direct message or getting the response.</value>
</data>
<data name="ExceptionNotConstructedForTransit" xml:space="preserve">
<value>This exception was not constructed with a root request message that caused it.</value>
diff --git a/src/DotNetOAuth/Messaging/Reflection/MessageDescription.cs b/src/DotNetOAuth/Messaging/Reflection/MessageDescription.cs
index 9442f15..0f180c9 100644
--- a/src/DotNetOAuth/Messaging/Reflection/MessageDescription.cs
+++ b/src/DotNetOAuth/Messaging/Reflection/MessageDescription.cs
@@ -7,16 +7,36 @@
namespace DotNetOAuth.Messaging.Reflection {
using System;
using System.Collections.Generic;
+ using System.Diagnostics;
+ using System.Globalization;
using System.Linq;
using System.Reflection;
- using System.Globalization;
- using System.Diagnostics;
+ /// <summary>
+ /// A mapping between serialized key names and <see cref="MessagePart"/> instances describing
+ /// those key/values pairs.
+ /// </summary>
internal class MessageDescription {
- private static Dictionary<Type, MessageDescription> reflectedMessageTypes = new Dictionary<Type,MessageDescription>();
+ /// <summary>
+ /// A dictionary of reflected message types and the generated reflection information.
+ /// </summary>
+ private static Dictionary<Type, MessageDescription> reflectedMessageTypes = new Dictionary<Type, MessageDescription>();
+
+ /// <summary>
+ /// The type of message this instance was generated from.
+ /// </summary>
private Type messageType;
+
+ /// <summary>
+ /// A mapping between the serialized key names and their
+ /// describing <see cref="MessagePart"/> instances.
+ /// </summary>
private Dictionary<string, MessagePart> mapping;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessageDescription"/> class.
+ /// </summary>
+ /// <param name="messageType">The type of message to reflect over.</param>
private MessageDescription(Type messageType) {
Debug.Assert(messageType != null, "messageType == null");
@@ -32,6 +52,20 @@ namespace DotNetOAuth.Messaging.Reflection {
this.ReflectMessageType();
}
+ /// <summary>
+ /// Gets the mapping between the serialized key names and their describing
+ /// <see cref="MessagePart"/> instances.
+ /// </summary>
+ internal IDictionary<string, MessagePart> Mapping {
+ get { return this.mapping; }
+ }
+
+ /// <summary>
+ /// Gets a <see cref="MessageDescription"/> instance prepared for the
+ /// given message type.
+ /// </summary>
+ /// <param name="messageType">A type that implements <see cref="IProtocolMessage"/>.</param>
+ /// <returns>A <see cref="MessageDescription"/> instance.</returns>
internal static MessageDescription Get(Type messageType) {
if (messageType == null) {
throw new ArgumentNullException("messageType");
@@ -49,10 +83,10 @@ namespace DotNetOAuth.Messaging.Reflection {
return result;
}
- internal IDictionary<string, MessagePart> Mapping {
- get { return this.mapping; }
- }
-
+ /// <summary>
+ /// Reflects over some <see cref="IProtocolMessage"/>-implementing type
+ /// and prepares to serialize/deserialize instances of that type.
+ /// </summary>
internal void ReflectMessageType() {
this.mapping = new Dictionary<string, MessagePart>();
@@ -75,16 +109,19 @@ namespace DotNetOAuth.Messaging.Reflection {
/// Verifies that a given set of keys include all the required parameters
/// for this message type or throws an exception.
/// </summary>
+ /// <param name="keys">The names of all parameters included in a message.</param>
/// <exception cref="ProtocolException">Thrown when required parts of a message are not in <paramref name="keys"/></exception>
internal void EnsureRequiredMessagePartsArePresent(IEnumerable<string> keys) {
var missingKeys = (from part in Mapping.Values
where part.IsRequired && !keys.Contains(part.Name)
select part.Name).ToArray();
if (missingKeys.Length > 0) {
- throw new ProtocolException(string.Format(CultureInfo.CurrentCulture,
- MessagingStrings.RequiredParametersMissing,
- this.messageType.FullName,
- string.Join(", ", missingKeys)));
+ throw new ProtocolException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.RequiredParametersMissing,
+ this.messageType.FullName,
+ string.Join(", ", missingKeys)));
}
}
}
diff --git a/src/DotNetOAuth/Messaging/Reflection/MessageDictionary.cs b/src/DotNetOAuth/Messaging/Reflection/MessageDictionary.cs
index 196af54..3c233ce 100644
--- a/src/DotNetOAuth/Messaging/Reflection/MessageDictionary.cs
+++ b/src/DotNetOAuth/Messaging/Reflection/MessageDictionary.cs
@@ -16,10 +16,20 @@ namespace DotNetOAuth.Messaging.Reflection {
/// name/value pairs that have no properties associated with them.
/// </summary>
internal class MessageDictionary : IDictionary<string, string> {
+ /// <summary>
+ /// The <see cref="IProtocolMessage"/> instance manipulated by this dictionary.
+ /// </summary>
private IProtocolMessage message;
+ /// <summary>
+ /// The <see cref="MessageDescription"/> instance that describes the message type.
+ /// </summary>
private MessageDescription description;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessageDictionary"/> class.
+ /// </summary>
+ /// <param name="message">The message instance whose values will be manipulated by this dictionary.</param>
internal MessageDictionary(IProtocolMessage message) {
if (message == null) {
throw new ArgumentNullException("message");
@@ -29,29 +39,29 @@ namespace DotNetOAuth.Messaging.Reflection {
this.description = MessageDescription.Get(message.GetType());
}
- #region IDictionary<string,string> Members
+ #region ICollection<KeyValuePair<string,string>> Properties
- public void Add(string key, string value) {
- if (value == null) {
- throw new ArgumentNullException("value");
- }
-
- MessagePart part;
- if (this.description.Mapping.TryGetValue(key, out part)) {
- if (part.IsNondefaultValueSet(this.message)) {
- throw new ArgumentException(MessagingStrings.KeyAlreadyExists);
- }
- part.SetValue(this.message, value);
- } else {
- this.message.ExtraData.Add(key, value);
- }
+ /// <summary>
+ /// Gets the number of explicitly set values in the message.
+ /// </summary>
+ public int Count {
+ get { return this.Keys.Count; }
}
- public bool ContainsKey(string key) {
- return this.message.ExtraData.ContainsKey(key) ||
- (this.description.Mapping.ContainsKey(key) && this.description.Mapping[key].GetValue(this.message) != null);
+ /// <summary>
+ /// Gets a value indicating whether this message is read only.
+ /// </summary>
+ bool ICollection<KeyValuePair<string, string>>.IsReadOnly {
+ get { return false; }
}
+ #endregion
+
+ #region IDictionary<string,string> Properties
+
+ /// <summary>
+ /// Gets all the keys that have values associated with them.
+ /// </summary>
public ICollection<string> Keys {
get {
List<string> keys = new List<string>(this.message.ExtraData.Count + this.description.Mapping.Count);
@@ -70,30 +80,9 @@ namespace DotNetOAuth.Messaging.Reflection {
}
}
- public bool Remove(string key) {
- if (this.message.ExtraData.Remove(key)) {
- return true;
- } else {
- MessagePart part;
- if (this.description.Mapping.TryGetValue(key, out part)) {
- if (part.GetValue(this.message) != null) {
- part.SetValue(this.message, null);
- return true;
- }
- }
- return false;
- }
- }
-
- public bool TryGetValue(string key, out string value) {
- MessagePart part;
- if (this.description.Mapping.TryGetValue(key, out part)) {
- value = part.GetValue(this.message);
- return true;
- }
- return this.message.ExtraData.TryGetValue(key, out value);
- }
-
+ /// <summary>
+ /// Gets all the values.
+ /// </summary>
public ICollection<string> Values {
get {
List<string> values = new List<string>(this.message.ExtraData.Count + this.description.Mapping.Count);
@@ -112,6 +101,16 @@ namespace DotNetOAuth.Messaging.Reflection {
}
}
+ /// <summary>
+ /// Gets or sets a value for some named value.
+ /// </summary>
+ /// <param name="key">The serialized form of a name for the value to read or write.</param>
+ /// <returns>The named value.</returns>
+ /// <remarks>
+ /// If the key matches a declared property or field on the message type,
+ /// that type member is set. Otherwise the key/value is stored in a
+ /// dictionary for extra (weakly typed) strings.
+ /// </remarks>
public string this[string key] {
get {
MessagePart part;
@@ -139,18 +138,106 @@ namespace DotNetOAuth.Messaging.Reflection {
#endregion
- #region ICollection<KeyValuePair<string,string>> Members
+ #region IDictionary<string,string> Methods
+
+ /// <summary>
+ /// Adds a named value to the message.
+ /// </summary>
+ /// <param name="key">The serialized form of the name whose value is being set.</param>
+ /// <param name="value">The serialized form of the value.</param>
+ /// <exception cref="ArgumentException">
+ /// Thrown if <paramref name="key"/> already has a set value in this message.
+ /// </exception>
+ /// <exception cref="ArgumentNullException">
+ /// Thrown if <paramref name="value"/> is null.
+ /// </exception>
+ public void Add(string key, string value) {
+ if (value == null) {
+ throw new ArgumentNullException("value");
+ }
+
+ MessagePart part;
+ if (this.description.Mapping.TryGetValue(key, out part)) {
+ if (part.IsNondefaultValueSet(this.message)) {
+ throw new ArgumentException(MessagingStrings.KeyAlreadyExists);
+ }
+ part.SetValue(this.message, value);
+ } else {
+ this.message.ExtraData.Add(key, value);
+ }
+ }
+
+ /// <summary>
+ /// Checks whether some named parameter has a value set in the message.
+ /// </summary>
+ /// <param name="key">The serialized form of the message part's name.</param>
+ /// <returns>True if the parameter by the given name has a set value. False otherwise.</returns>
+ public bool ContainsKey(string key) {
+ return this.message.ExtraData.ContainsKey(key) ||
+ (this.description.Mapping.ContainsKey(key) && this.description.Mapping[key].GetValue(this.message) != null);
+ }
+
+ /// <summary>
+ /// Removes a name and value from the message given its name.
+ /// </summary>
+ /// <param name="key">The serialized form of the name to remove.</param>
+ /// <returns>True if a message part by the given name was found and removed. False otherwise.</returns>
+ public bool Remove(string key) {
+ if (this.message.ExtraData.Remove(key)) {
+ return true;
+ } else {
+ MessagePart part;
+ if (this.description.Mapping.TryGetValue(key, out part)) {
+ if (part.GetValue(this.message) != null) {
+ part.SetValue(this.message, null);
+ return true;
+ }
+ }
+ return false;
+ }
+ }
+
+ /// <summary>
+ /// Gets some named value if the key has a value.
+ /// </summary>
+ /// <param name="key">The name (in serialized form) of the value being sought.</param>
+ /// <param name="value">The variable where the value will be set.</param>
+ /// <returns>True if the key was found and <paramref name="value"/> was set. False otherwise.</returns>
+ public bool TryGetValue(string key, out string value) {
+ MessagePart part;
+ if (this.description.Mapping.TryGetValue(key, out part)) {
+ value = part.GetValue(this.message);
+ return true;
+ }
+ return this.message.ExtraData.TryGetValue(key, out value);
+ }
+
+ #endregion
+
+ #region ICollection<KeyValuePair<string,string>> Methods
+ /// <summary>
+ /// Sets a named value in the message.
+ /// </summary>
+ /// <param name="item">The name-value pair to add. The name is the serialized form of the key.</param>
public void Add(KeyValuePair<string, string> item) {
this.Add(item.Key, item.Value);
}
+ /// <summary>
+ /// Removes all values in the message.
+ /// </summary>
public void Clear() {
foreach (string key in this.Keys) {
this.Remove(key);
}
}
+ /// <summary>
+ /// Checks whether a named value has been set on the message.
+ /// </summary>
+ /// <param name="item">The name/value pair.</param>
+ /// <returns>True if the key exists and has the given value. False otherwise.</returns>
public bool Contains(KeyValuePair<string, string> item) {
MessagePart part;
if (this.description.Mapping.TryGetValue(item.Key, out part)) {
@@ -160,20 +247,22 @@ namespace DotNetOAuth.Messaging.Reflection {
}
}
+ /// <summary>
+ /// Copies all the serializable data from the message to a key/value array.
+ /// </summary>
+ /// <param name="array">The array to copy to.</param>
+ /// <param name="arrayIndex">The index in the <paramref name="array"/> to begin copying to.</param>
void ICollection<KeyValuePair<string, string>>.CopyTo(KeyValuePair<string, string>[] array, int arrayIndex) {
foreach (var pair in (IDictionary<string, string>)this) {
array[arrayIndex++] = pair;
}
}
- public int Count {
- get { return this.Keys.Count; }
- }
-
- bool ICollection<KeyValuePair<string, string>>.IsReadOnly {
- get { return false; }
- }
-
+ /// <summary>
+ /// Removes a named value from the message if it exists.
+ /// </summary>
+ /// <param name="item">The serialized form of the name and value to remove.</param>
+ /// <returns>True if the name/value was found and removed. False otherwise.</returns>
public bool Remove(KeyValuePair<string, string> item) {
// We use contains because that checks that the value is equal as well.
if (((ICollection<KeyValuePair<string, string>>)this).Contains(item)) {
@@ -187,8 +276,13 @@ namespace DotNetOAuth.Messaging.Reflection {
#region IEnumerable<KeyValuePair<string,string>> Members
+ /// <summary>
+ /// Gets an enumerator that generates KeyValuePair&lt;string, string&gt; instances
+ /// for all the key/value pairs that are set in the message.
+ /// </summary>
+ /// <returns>The enumerator that can generate the name/value pairs.</returns>
public IEnumerator<KeyValuePair<string, string>> GetEnumerator() {
- foreach (string key in Keys) {
+ foreach (string key in this.Keys) {
yield return new KeyValuePair<string, string>(key, this[key]);
}
}
@@ -197,6 +291,11 @@ namespace DotNetOAuth.Messaging.Reflection {
#region IEnumerable Members
+ /// <summary>
+ /// Gets an enumerator that generates KeyValuePair&lt;string, string&gt; instances
+ /// for all the key/value pairs that are set in the message.
+ /// </summary>
+ /// <returns>The enumerator that can generate the name/value pairs.</returns>
IEnumerator System.Collections.IEnumerable.GetEnumerator() {
return ((IEnumerable<KeyValuePair<string, string>>)this).GetEnumerator();
}
diff --git a/src/DotNetOAuth/Messaging/Reflection/MessagePart.cs b/src/DotNetOAuth/Messaging/Reflection/MessagePart.cs
index 0cf7cd4..b79ad06 100644
--- a/src/DotNetOAuth/Messaging/Reflection/MessagePart.cs
+++ b/src/DotNetOAuth/Messaging/Reflection/MessagePart.cs
@@ -7,29 +7,64 @@
namespace DotNetOAuth.Messaging.Reflection {
using System;
using System.Collections.Generic;
+ using System.Globalization;
using System.Net.Security;
using System.Reflection;
using System.Xml;
- using System.Globalization;
+ /// <summary>
+ /// Describes an individual member of a message and assists in its serialization.
+ /// </summary>
internal class MessagePart {
+ /// <summary>
+ /// A map of converters that help serialize custom objects to string values and back again.
+ /// </summary>
private static readonly Dictionary<Type, ValueMapping> converters = new Dictionary<Type, ValueMapping>();
+ /// <summary>
+ /// The string-object conversion routines to use for this individual message part.
+ /// </summary>
private ValueMapping converter;
+ /// <summary>
+ /// The property that this message part is associated with, if aplicable.
+ /// </summary>
private PropertyInfo property;
+ /// <summary>
+ /// The field that this message part is associated with, if aplicable.
+ /// </summary>
private FieldInfo field;
+ /// <summary>
+ /// The type of the message part. (Not the type of the message itself).
+ /// </summary>
private Type memberDeclaredType;
+ /// <summary>
+ /// The default (uninitialized) value of the member inherent in its type.
+ /// </summary>
private object defaultMemberValue;
+ /// <summary>
+ /// Initializes static members of the <see cref="MessagePart"/> class.
+ /// </summary>
static MessagePart() {
Map<Uri>(uri => uri.AbsoluteUri, str => new Uri(str));
Map<DateTime>(dt => XmlConvert.ToString(dt, XmlDateTimeSerializationMode.Utc), str => XmlConvert.ToDateTime(str, XmlDateTimeSerializationMode.Utc));
}
+ /// <summary>
+ /// Initializes a new instance of the <see cref="MessagePart"/> class.
+ /// </summary>
+ /// <param name="member">
+ /// A property or field of an <see cref="IProtocolMessage"/> implementing type
+ /// that has a <see cref="MessagePartAttribute"/> attached to it.
+ /// </param>
+ /// <param name="attribute">
+ /// The attribute discovered on <paramref name="member"/> that describes the
+ /// serialization requirements of the message part.
+ /// </param>
internal MessagePart(MemberInfo member, MessagePartAttribute attribute) {
if (member == null) {
throw new ArgumentNullException("member");
@@ -38,11 +73,13 @@ namespace DotNetOAuth.Messaging.Reflection {
this.field = member as FieldInfo;
this.property = member as PropertyInfo;
if (this.field == null && this.property == null) {
- throw new ArgumentException(string.Format(
- CultureInfo.CurrentCulture,
- MessagingStrings.UnexpectedType,
- typeof(FieldInfo).Name + ", " + typeof(PropertyInfo).Name,
- member.GetType().Name), "member");
+ throw new ArgumentException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ MessagingStrings.UnexpectedType,
+ typeof(FieldInfo).Name + ", " + typeof(PropertyInfo).Name,
+ member.GetType().Name),
+ "member");
}
if (attribute == null) {
@@ -53,32 +90,40 @@ namespace DotNetOAuth.Messaging.Reflection {
this.RequiredProtection = attribute.RequiredProtection;
this.IsRequired = attribute.IsRequired;
this.memberDeclaredType = (this.field != null) ? this.field.FieldType : this.property.PropertyType;
- this.defaultMemberValue = deriveDefaultValue(this.memberDeclaredType);
+ this.defaultMemberValue = DeriveDefaultValue(this.memberDeclaredType);
if (!converters.TryGetValue(this.memberDeclaredType, out this.converter)) {
this.converter = new ValueMapping(
obj => obj != null ? obj.ToString() : null,
- str => str != null ? Convert.ChangeType(str, memberDeclaredType) : null);
+ str => str != null ? Convert.ChangeType(str, this.memberDeclaredType, CultureInfo.InvariantCulture) : null);
}
// Validate a sane combination of settings
- ValidateSettings();
+ this.ValidateSettings();
}
+ /// <summary>
+ /// Gets or sets the name to use when serializing or deserializing this parameter in a message.
+ /// </summary>
internal string Name { get; set; }
+ /// <summary>
+ /// Gets or sets whether this message part must be signed.
+ /// </summary>
internal ProtectionLevel RequiredProtection { get; set; }
+ /// <summary>
+ /// Gets or sets a value indicating whether this message part is required for the
+ /// containing message to be valid.
+ /// </summary>
internal bool IsRequired { get; set; }
- internal object ToValue(string value) {
- return this.converter.StringToValue(value);
- }
-
- internal string ToString(object value) {
- return this.converter.ValueToString(value);
- }
-
+ /// <summary>
+ /// Sets the member of a given message to some given value.
+ /// Used in deserialization.
+ /// </summary>
+ /// <param name="message">The message instance containing the member whose value should be set.</param>
+ /// <param name="value">The string representation of the value to set.</param>
internal void SetValue(IProtocolMessage message, string value) {
if (this.property != null) {
this.property.SetValue(message, this.ToValue(value), null);
@@ -87,19 +132,35 @@ namespace DotNetOAuth.Messaging.Reflection {
}
}
+ /// <summary>
+ /// Gets the value of a member of a given message.
+ /// Used in serialization.
+ /// </summary>
+ /// <param name="message">The message instance to read the value from.</param>
+ /// <returns>The string representation of the member's value.</returns>
internal string GetValue(IProtocolMessage message) {
return this.ToString(this.GetValueAsObject(message));
}
+ /// <summary>
+ /// Gets whether the value has been set to something other than its CLR type default value.
+ /// </summary>
+ /// <param name="message">The message instance to check the value on.</param>
+ /// <returns>True if the value is not the CLR default value.</returns>
internal bool IsNondefaultValueSet(IProtocolMessage message) {
if (this.memberDeclaredType.IsValueType) {
- return !GetValueAsObject(message).Equals(this.defaultMemberValue);
+ return !this.GetValueAsObject(message).Equals(this.defaultMemberValue);
} else {
- return this.defaultMemberValue != GetValueAsObject(message);
+ return this.defaultMemberValue != this.GetValueAsObject(message);
}
}
- private static object deriveDefaultValue(Type type) {
+ /// <summary>
+ /// Figures out the CLR default value for a given type.
+ /// </summary>
+ /// <param name="type">The type whose default value is being sought.</param>
+ /// <returns>Either null, or some default value like 0 or 0.0.</returns>
+ private static object DeriveDefaultValue(Type type) {
if (type.IsValueType) {
return Activator.CreateInstance(type);
} else {
@@ -107,32 +168,23 @@ namespace DotNetOAuth.Messaging.Reflection {
}
}
- private object GetValueAsObject(IProtocolMessage message) {
- if (this.property != null) {
- return this.property.GetValue(message, null);
- } else {
- return this.field.GetValue(message);
- }
- }
-
+ /// <summary>
+ /// Adds a pair of type conversion functions to the static converstion map.
+ /// </summary>
+ /// <typeparam name="T">The custom type to convert to and from strings.</typeparam>
+ /// <param name="toString">The function to convert the custom type to a string.</param>
+ /// <param name="toValue">The function to convert a string to the custom type.</param>
private static void Map<T>(Func<T, string> toString, Func<string, T> toValue) {
- converters.Add(
- typeof(T),
- new ValueMapping(
- obj => obj != null ? toString((T)obj) : null,
- str => str != null ? toValue(str) : default(T)));
- }
-
- private void ValidateSettings() {
- // An optional tag on a non-nullable value type is a contradiction.
- if (!this.IsRequired && IsNonNullableValueType(this.memberDeclaredType)) {
- MemberInfo member = (MemberInfo)this.field ?? this.property;
- throw new ArgumentException(string.Format(CultureInfo.CurrentCulture,
- "Invalid combination: {0} on message type {1} is a non-nullable value type but is marked as optional.",
- member.Name, member.DeclaringType));
- }
+ Func<object, string> safeToString = obj => obj != null ? toString((T)obj) : null;
+ Func<string, object> safeToT = str => str != null ? toValue(str) : default(T);
+ converters.Add(typeof(T), new ValueMapping(safeToString, safeToT));
}
+ /// <summary>
+ /// Checks whether a type is a nullable value type (i.e. int?)
+ /// </summary>
+ /// <param name="type">The type in question.</param>
+ /// <returns>True if this is a nullable value type.</returns>
private static bool IsNonNullableValueType(Type type) {
if (!type.IsValueType) {
return false;
@@ -144,5 +196,54 @@ namespace DotNetOAuth.Messaging.Reflection {
return true;
}
+
+ /// <summary>
+ /// Converts a string representation of the member's value to the appropriate type.
+ /// </summary>
+ /// <param name="value">The string representation of the member's value.</param>
+ /// <returns>An instance of the appropriate type for setting the member.</returns>
+ private object ToValue(string value) {
+ return this.converter.StringToValue(value);
+ }
+
+ /// <summary>
+ /// Converts the member's value to its string representation.
+ /// </summary>
+ /// <param name="value">The value of the member.</param>
+ /// <returns>The string representation of the member's value.</returns>
+ private string ToString(object value) {
+ return this.converter.ValueToString(value);
+ }
+
+ /// <summary>
+ /// Gets the value of the message part, without converting it to/from a string.
+ /// </summary>
+ /// <param name="message">The message instance to read from.</param>
+ /// <returns>The value of the member.</returns>
+ private object GetValueAsObject(IProtocolMessage message) {
+ if (this.property != null) {
+ return this.property.GetValue(message, null);
+ } else {
+ return this.field.GetValue(message);
+ }
+ }
+
+ /// <summary>
+ /// Validates that the message part and its attribute have agreeable settings.
+ /// </summary>
+ /// <exception cref="ArgumentException">
+ /// Thrown when a non-nullable value type is set as optional.
+ /// </exception>
+ private void ValidateSettings() {
+ if (!this.IsRequired && IsNonNullableValueType(this.memberDeclaredType)) {
+ MemberInfo member = (MemberInfo)this.field ?? this.property;
+ throw new ArgumentException(
+ string.Format(
+ CultureInfo.CurrentCulture,
+ "Invalid combination: {0} on message type {1} is a non-nullable value type but is marked as optional.",
+ member.Name,
+ member.DeclaringType));
+ }
+ }
}
}
diff --git a/src/DotNetOAuth/Messaging/Reflection/ValueMapping.cs b/src/DotNetOAuth/Messaging/Reflection/ValueMapping.cs
index 2371b49..e2d869b 100644
--- a/src/DotNetOAuth/Messaging/Reflection/ValueMapping.cs
+++ b/src/DotNetOAuth/Messaging/Reflection/ValueMapping.cs
@@ -7,10 +7,25 @@
namespace DotNetOAuth.Messaging.Reflection {
using System;
+ /// <summary>
+ /// A pair of conversion functions to map some type to a string and back again.
+ /// </summary>
internal struct ValueMapping {
+ /// <summary>
+ /// The mapping function that converts some custom type to a string.
+ /// </summary>
internal Func<object, string> ValueToString;
+
+ /// <summary>
+ /// The mapping function that converts a string to some custom type.
+ /// </summary>
internal Func<string, object> StringToValue;
+ /// <summary>
+ /// Initializes a new instance of the <see cref="ValueMapping"/> struct.
+ /// </summary>
+ /// <param name="toString">The mapping function that converts some custom type to a string.</param>
+ /// <param name="toValue">The mapping function that converts a string to some custom type.</param>
internal ValueMapping(Func<object, string> toString, Func<string, object> toValue) {
if (toString == null) {
throw new ArgumentNullException("toString");