summaryrefslogtreecommitdiffstats
path: root/src/DotNetOAuth/Messaging/Reflection/MessagePart.cs
diff options
context:
space:
mode:
Diffstat (limited to 'src/DotNetOAuth/Messaging/Reflection/MessagePart.cs')
-rw-r--r--src/DotNetOAuth/Messaging/Reflection/MessagePart.cs187
1 files changed, 144 insertions, 43 deletions
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));
+ }
+ }
}
}