diff options
Diffstat (limited to 'src/DotNetOpenAuth.Core/Messaging/Reflection/MessagePart.cs')
-rw-r--r-- | src/DotNetOpenAuth.Core/Messaging/Reflection/MessagePart.cs | 98 |
1 files changed, 63 insertions, 35 deletions
diff --git a/src/DotNetOpenAuth.Core/Messaging/Reflection/MessagePart.cs b/src/DotNetOpenAuth.Core/Messaging/Reflection/MessagePart.cs index 8f40d6d..b2c4664 100644 --- a/src/DotNetOpenAuth.Core/Messaging/Reflection/MessagePart.cs +++ b/src/DotNetOpenAuth.Core/Messaging/Reflection/MessagePart.cs @@ -132,16 +132,10 @@ namespace DotNetOpenAuth.Messaging.Reflection { null, str => str != null ? underlyingMapping.StringToValue(str) : null); } else { - this.converter = new ValueMapping( - obj => obj != null ? obj.ToString() : null, - null, - str => str != null ? Convert.ChangeType(str, underlyingType, CultureInfo.InvariantCulture) : null); + this.converter = GetDefaultEncoder(underlyingType); } } else { - this.converter = new ValueMapping( - obj => obj != null ? obj.ToString() : null, - null, - str => str != null ? Convert.ChangeType(str, this.memberDeclaredType, CultureInfo.InvariantCulture) : null); + this.converter = GetDefaultEncoder(this.memberDeclaredType); } } } else { @@ -212,28 +206,6 @@ namespace DotNetOpenAuth.Messaging.Reflection { } /// <summary> - /// Adds a pair of type conversion functions to the static conversion 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="toOriginalString">The mapping function that converts some custom value to its original (non-normalized) string. May be null if the same as the <paramref name="toString"/> function.</param> - /// <param name="toValue">The function to convert a string to the custom type.</param> - [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Diagnostics.Contracts.__ContractsRuntime.Requires<System.ArgumentNullException>(System.Boolean,System.String,System.String)", Justification = "Code contracts"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "toString", Justification = "Code contracts"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "toValue", Justification = "Code contracts")] - internal static void Map<T>(Func<T, string> toString, Func<T, string> toOriginalString, Func<string, T> toValue) { - Requires.NotNull(toString, "toString"); - Requires.NotNull(toValue, "toValue"); - - if (toOriginalString == null) { - toOriginalString = toString; - } - - Func<object, string> safeToString = obj => obj != null ? toString((T)obj) : null; - Func<object, string> safeToOriginalString = obj => obj != null ? toOriginalString((T)obj) : null; - Func<string, object> safeToT = str => str != null ? toValue(str) : default(T); - converters.Add(typeof(T), new ValueMapping(safeToString, safeToOriginalString, safeToT)); - } - - /// <summary> /// Sets the member of a given message to some given value. /// Used in deserialization. /// </summary> @@ -308,6 +280,60 @@ namespace DotNetOpenAuth.Messaging.Reflection { } /// <summary> + /// Adds a pair of type conversion functions to the static conversion 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="toOriginalString">The mapping function that converts some custom value to its original (non-normalized) string. May be null if the same as the <paramref name="toString"/> function.</param> + /// <param name="toValue">The function to convert a string to the custom type.</param> + [SuppressMessage("Microsoft.Globalization", "CA1303:Do not pass literals as localized parameters", MessageId = "System.Diagnostics.Contracts.__ContractsRuntime.Requires<System.ArgumentNullException>(System.Boolean,System.String,System.String)", Justification = "Code contracts"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "toString", Justification = "Code contracts"), SuppressMessage("Microsoft.Naming", "CA2204:Literals should be spelled correctly", MessageId = "toValue", Justification = "Code contracts")] + private static void Map<T>(Func<T, string> toString, Func<T, string> toOriginalString, Func<string, T> toValue) { + Requires.NotNull(toString, "toString"); + Requires.NotNull(toValue, "toValue"); + + if (toOriginalString == null) { + toOriginalString = toString; + } + + Func<object, string> safeToString = obj => obj != null ? toString((T)obj) : null; + Func<object, string> safeToOriginalString = obj => obj != null ? toOriginalString((T)obj) : null; + Func<string, object> safeToT = str => str != null ? toValue(str) : default(T); + converters.Add(typeof(T), new ValueMapping(safeToString, safeToOriginalString, safeToT)); + } + + /// <summary> + /// Creates a <see cref="ValueMapping"/> that resorts to <see cref="object.ToString"/> and + /// <see cref="Convert.ChangeType(object, Type, IFormatProvider)"/> for the conversion. + /// </summary> + /// <param name="type">The type to create the mapping for.</param> + /// <returns>The value mapping.</returns> + private static ValueMapping CreateFallbackMapping(Type type) { + Requires.NotNull(type, "type"); + + return new ValueMapping( + obj => obj != null ? obj.ToString() : null, + null, + str => str != null ? Convert.ChangeType(str, type, CultureInfo.InvariantCulture) : null); + } + + /// <summary> + /// Creates the default encoder for a given type. + /// </summary> + /// <param name="type">The type to create a <see cref="ValueMapping"/> for.</param> + /// <returns>A <see cref="ValueMapping"/> struct.</returns> + private static ValueMapping GetDefaultEncoder(Type type) { + Requires.NotNull(type, "type"); + + var converterAttributes = (DefaultEncoderAttribute[])type.GetCustomAttributes(typeof(DefaultEncoderAttribute), false); + ErrorUtilities.VerifyInternal(converterAttributes.Length < 2, "Too many attributes applied."); + if (converterAttributes.Length == 1) { + return new ValueMapping(converterAttributes[0].Encoder); + } + + return CreateFallbackMapping(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> @@ -347,11 +373,13 @@ namespace DotNetOpenAuth.Messaging.Reflection { Contract.Ensures(Contract.Result<IMessagePartEncoder>() != null); IMessagePartEncoder encoder; - if (!encoders.TryGetValue(messagePartEncoder, out encoder)) { - try { - encoder = encoders[messagePartEncoder] = (IMessagePartEncoder)Activator.CreateInstance(messagePartEncoder); - } catch (MissingMethodException ex) { - throw ErrorUtilities.Wrap(ex, MessagingStrings.EncoderInstantiationFailed, messagePartEncoder.FullName); + lock (encoders) { + if (!encoders.TryGetValue(messagePartEncoder, out encoder)) { + try { + encoder = encoders[messagePartEncoder] = (IMessagePartEncoder)Activator.CreateInstance(messagePartEncoder); + } catch (MissingMethodException ex) { + throw ErrorUtilities.Wrap(ex, MessagingStrings.EncoderInstantiationFailed, messagePartEncoder.FullName); + } } } |