//-----------------------------------------------------------------------
//
// Copyright (c) Andrew Arnott. All rights reserved.
//
//-----------------------------------------------------------------------
namespace DotNetOAuth.Messaging.Reflection {
using System;
using System.Collections.Generic;
using System.Net.Security;
using System.Reflection;
using System.Xml;
using System.Globalization;
internal class MessagePart {
private static readonly Dictionary converters = new Dictionary();
private ValueMapping converter;
private PropertyInfo property;
private FieldInfo field;
private Type memberDeclaredType;
private object defaultMemberValue;
static MessagePart() {
Map(uri => uri.AbsoluteUri, str => new Uri(str));
Map(dt => XmlConvert.ToString(dt, XmlDateTimeSerializationMode.Utc), str => XmlConvert.ToDateTime(str, XmlDateTimeSerializationMode.Utc));
}
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.Signed = attribute.Signed;
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, memberDeclaredType) : null);
}
// Validate a sane combination of settings
ValidateSettings();
}
internal string Name { get; set; }
internal ProtectionLevel Signed { get; set; }
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);
}
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));
}
}
internal string GetValue(IProtocolMessage message) {
return this.ToString(this.GetValueAsObject(message));
}
internal bool IsNondefaultValueSet(IProtocolMessage message) {
if (this.memberDeclaredType.IsValueType) {
return !GetValueAsObject(message).Equals(this.defaultMemberValue);
} else {
return this.defaultMemberValue != GetValueAsObject(message);
}
}
private static object deriveDefaultValue(Type type) {
if (type.IsValueType) {
return Activator.CreateInstance(type);
} else {
return null;
}
}
private object GetValueAsObject(IProtocolMessage message) {
if (this.property != null) {
return this.property.GetValue(message, null);
} else {
return this.field.GetValue(message);
}
}
private static void Map(Func toString, Func 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));
}
}
private static bool IsNonNullableValueType(Type type) {
if (!type.IsValueType) {
return false;
}
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Nullable<>)) {
return false;
}
return true;
}
}
}