//-----------------------------------------------------------------------
//
// Copyright (c) Andrew Arnott. All rights reserved.
//
//-----------------------------------------------------------------------
namespace DotNetOAuth.Messaging.Reflection {
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Globalization;
using System.Net.Security;
using System.Reflection;
using System.Xml;
///
/// Describes an individual member of a message and assists in its serialization.
///
internal class MessagePart {
///
/// A map of converters that help serialize custom objects to string values and back again.
///
private static readonly Dictionary converters = new Dictionary();
///
/// The string-object conversion routines to use for this individual message part.
///
private ValueMapping converter;
///
/// The property that this message part is associated with, if aplicable.
///
private PropertyInfo property;
///
/// The field that this message part is associated with, if aplicable.
///
private FieldInfo field;
///
/// The type of the message part. (Not the type of the message itself).
///
private Type memberDeclaredType;
///
/// The default (uninitialized) value of the member inherent in its type.
///
private object defaultMemberValue;
///
/// Initializes static members of the class.
///
[SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline", Justification = "Much more efficient initialization when we can call methods.")]
static MessagePart() {
Map(uri => uri.AbsoluteUri, str => new Uri(str));
Map(dt => XmlConvert.ToString(dt, XmlDateTimeSerializationMode.Utc), str => XmlConvert.ToDateTime(str, XmlDateTimeSerializationMode.Utc));
}
///
/// Initializes a new instance of the class.
///
///
/// A property or field of an implementing type
/// that has a attached to it.
///
///
/// The attribute discovered on that describes the
/// serialization requirements of the message part.
///
internal MessagePart(MemberInfo member, MessagePartAttribute attribute) {
if (member == null) {
throw new ArgumentNullException("member");
}
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");
}
if (attribute == null) {
throw new ArgumentNullException("attribute");
}
this.Name = attribute.Name ?? member.Name;
this.RequiredProtection = attribute.RequiredProtection;
this.IsRequired = attribute.IsRequired;
this.memberDeclaredType = (this.field != null) ? this.field.FieldType : this.property.PropertyType;
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, this.memberDeclaredType, CultureInfo.InvariantCulture) : null);
}
// Validate a sane combination of settings
this.ValidateSettings();
}
///
/// Gets or sets the name to use when serializing or deserializing this parameter in a message.
///
internal string Name { get; set; }
///
/// Gets or sets whether this message part must be signed.
///
internal ProtectionLevel RequiredProtection { get; set; }
///
/// Gets or sets a value indicating whether this message part is required for the
/// containing message to be valid.
///
internal bool IsRequired { get; set; }
///
/// Sets the member of a given message to some given value.
/// Used in deserialization.
///
/// The message instance containing the member whose value should be set.
/// The string representation of the value to set.
internal void SetValue(IProtocolMessage message, string value) {
if (this.property != null) {
this.property.SetValue(message, this.ToValue(value), null);
} else {
this.field.SetValue(message, this.ToValue(value));
}
}
///
/// Gets the value of a member of a given message.
/// Used in serialization.
///
/// The message instance to read the value from.
/// The string representation of the member's value.
internal string GetValue(IProtocolMessage message) {
return this.ToString(this.GetValueAsObject(message));
}
///
/// Gets whether the value has been set to something other than its CLR type default value.
///
/// The message instance to check the value on.
/// True if the value is not the CLR default value.
internal bool IsNondefaultValueSet(IProtocolMessage message) {
if (this.memberDeclaredType.IsValueType) {
return !this.GetValueAsObject(message).Equals(this.defaultMemberValue);
} else {
return this.defaultMemberValue != this.GetValueAsObject(message);
}
}
///
/// Figures out the CLR default value for a given type.
///
/// The type whose default value is being sought.
/// Either null, or some default value like 0 or 0.0.
private static object DeriveDefaultValue(Type type) {
if (type.IsValueType) {
return Activator.CreateInstance(type);
} else {
return null;
}
}
///
/// Adds a pair of type conversion functions to the static converstion map.
///
/// The custom type to convert to and from strings.
/// The function to convert the custom type to a string.
/// The function to convert a string to the custom type.
private static void Map(Func toString, Func toValue) {
Func