summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj1
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Reflection/DefaultEncoderAttribute.cs33
-rw-r--r--src/DotNetOpenAuth.Core/Messaging/Reflection/MessagePart.cs98
-rw-r--r--src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/OpenIdProvider.cs12
-rw-r--r--src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/OpenIdRelyingParty.cs12
-rw-r--r--src/DotNetOpenAuth.OpenId/OpenId/Identifier.cs50
-rw-r--r--src/DotNetOpenAuth.OpenId/OpenId/Realm.cs48
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/IdentifierTests.cs17
-rw-r--r--src/DotNetOpenAuth.Test/OpenId/RealmTests.cs17
9 files changed, 206 insertions, 82 deletions
diff --git a/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj b/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj
index c46e6b8..447a3c5 100644
--- a/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj
+++ b/src/DotNetOpenAuth.Core/DotNetOpenAuth.Core.csproj
@@ -57,6 +57,7 @@
<Compile Include="Messaging\Reflection\IMessagePartNullEncoder.cs" />
<Compile Include="Messaging\Reflection\IMessagePartOriginalEncoder.cs" />
<Compile Include="Messaging\Reflection\MessageDescriptionCollection.cs" />
+ <Compile Include="Messaging\Reflection\DefaultEncoderAttribute.cs" />
<Compile Include="Messaging\StandardMessageFactory.cs" />
<Compile Include="Messaging\IDataBagFormatter.cs" />
<Compile Include="Messaging\UriStyleMessageFormatter.cs" />
diff --git a/src/DotNetOpenAuth.Core/Messaging/Reflection/DefaultEncoderAttribute.cs b/src/DotNetOpenAuth.Core/Messaging/Reflection/DefaultEncoderAttribute.cs
new file mode 100644
index 0000000..d827972
--- /dev/null
+++ b/src/DotNetOpenAuth.Core/Messaging/Reflection/DefaultEncoderAttribute.cs
@@ -0,0 +1,33 @@
+//-----------------------------------------------------------------------
+// <copyright file="DefaultEncoderAttribute.cs" company="Andrew Arnott">
+// Copyright (c) Andrew Arnott. All rights reserved.
+// </copyright>
+//-----------------------------------------------------------------------
+
+namespace DotNetOpenAuth.Messaging.Reflection {
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+
+ /// <summary>
+ /// Allows a custom class or struct to be serializable between itself and a string representation.
+ /// </summary>
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Struct, Inherited = false, AllowMultiple = false)]
+ internal sealed class DefaultEncoderAttribute : Attribute {
+ /// <summary>
+ /// Initializes a new instance of the <see cref="DefaultEncoderAttribute"/> class.
+ /// </summary>
+ /// <param name="converterType">The <see cref="IMessagePartEncoder"/> implementing type to use for serializing this type.</param>
+ public DefaultEncoderAttribute(Type converterType) {
+ Requires.NotNull(converterType, "converterType");
+ Requires.True(typeof(IMessagePartEncoder).IsAssignableFrom(converterType), "Argument must be a type that implements {0}.", typeof(IMessagePartEncoder).Name);
+ this.Encoder = (IMessagePartEncoder)Activator.CreateInstance(converterType);
+ }
+
+ /// <summary>
+ /// Gets the default encoder to use for the declaring class.
+ /// </summary>
+ public IMessagePartEncoder Encoder { get; private set; }
+ }
+}
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);
+ }
}
}
diff --git a/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/OpenIdProvider.cs b/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/OpenIdProvider.cs
index 72fdc80..05a33bc 100644
--- a/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/OpenIdProvider.cs
+++ b/src/DotNetOpenAuth.OpenId.Provider/OpenId/Provider/OpenIdProvider.cs
@@ -46,18 +46,6 @@ namespace DotNetOpenAuth.OpenId.Provider {
private readonly IdentifierDiscoveryServices discoveryServices;
/// <summary>
- /// A type initializer that ensures that another type initializer runs in order to guarantee that
- /// types are serializable.
- /// </summary>
- private static Identifier dummyIdentifierToInvokeStaticCtor = "http://localhost/";
-
- /// <summary>
- /// A type initializer that ensures that another type initializer runs in order to guarantee that
- /// types are serializable.
- /// </summary>
- private static Realm dummyRealmToInvokeStaticCtor = "http://localhost/";
-
- /// <summary>
/// Backing field for the <see cref="SecuritySettings"/> property.
/// </summary>
private ProviderSecuritySettings securitySettings;
diff --git a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/OpenIdRelyingParty.cs b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/OpenIdRelyingParty.cs
index 6e991d2..4dba624 100644
--- a/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/OpenIdRelyingParty.cs
+++ b/src/DotNetOpenAuth.OpenId.RelyingParty/OpenId/RelyingParty/OpenIdRelyingParty.cs
@@ -59,18 +59,6 @@ namespace DotNetOpenAuth.OpenId.RelyingParty {
private readonly IdentifierDiscoveryServices discoveryServices;
/// <summary>
- /// A type initializer that ensures that another type initializer runs in order to guarantee that
- /// types are serializable.
- /// </summary>
- private static Identifier dummyIdentifierToInvokeStaticCtor = "http://localhost/";
-
- /// <summary>
- /// A type initializer that ensures that another type initializer runs in order to guarantee that
- /// types are serializable.
- /// </summary>
- private static Realm dummyRealmToInvokeStaticCtor = "http://localhost/";
-
- /// <summary>
/// Backing field for the <see cref="NonVerifyingRelyingParty"/> property.
/// </summary>
private OpenIdRelyingParty nonVerifyingRelyingParty;
diff --git a/src/DotNetOpenAuth.OpenId/OpenId/Identifier.cs b/src/DotNetOpenAuth.OpenId/OpenId/Identifier.cs
index 03595d3..3cdd0f3 100644
--- a/src/DotNetOpenAuth.OpenId/OpenId/Identifier.cs
+++ b/src/DotNetOpenAuth.OpenId/OpenId/Identifier.cs
@@ -20,20 +20,9 @@ namespace DotNetOpenAuth.OpenId {
[ContractVerification(true)]
[Pure]
[ContractClass(typeof(IdentifierContract))]
+ [DefaultEncoder(typeof(IdentifierEncoder))]
public abstract class Identifier {
/// <summary>
- /// Initializes static members of the <see cref="Identifier"/> class.
- /// </summary>
- static Identifier() {
- Func<string, Identifier> safeIdentifier = str => {
- Contract.Assume(str != null);
- ErrorUtilities.VerifyFormat(str.Length > 0, MessagingStrings.NonEmptyStringExpected);
- return Identifier.Parse(str, true);
- };
- MessagePart.Map<Identifier>(id => id.SerializedString, id => id.OriginalString, safeIdentifier);
- }
-
- /// <summary>
/// Initializes a new instance of the <see cref="Identifier"/> class.
/// </summary>
/// <param name="originalString">The original string before any normalization.</param>
@@ -306,5 +295,42 @@ namespace DotNetOpenAuth.OpenId {
/// False if the Identifier was originally created with an explicit HTTP scheme.
/// </returns>
internal abstract bool TryRequireSsl(out Identifier secureIdentifier);
+
+ /// <summary>
+ /// Provides conversions to and from strings for messages that include members of this type.
+ /// </summary>
+ private class IdentifierEncoder : IMessagePartOriginalEncoder {
+ /// <summary>
+ /// Encodes the specified value as the original value that was formerly decoded.
+ /// </summary>
+ /// <param name="value">The value. Guaranteed to never be null.</param>
+ /// <returns>The <paramref name="value"/> in string form, ready for message transport.</returns>
+ public string EncodeAsOriginalString(object value) {
+ Requires.NotNull(value, "value");
+ return ((Identifier)value).OriginalString;
+ }
+
+ /// <summary>
+ /// Encodes the specified value.
+ /// </summary>
+ /// <param name="value">The value. Guaranteed to never be null.</param>
+ /// <returns>The <paramref name="value"/> in string form, ready for message transport.</returns>
+ public string Encode(object value) {
+ Requires.NotNull(value, "value");
+ return ((Identifier)value).SerializedString;
+ }
+
+ /// <summary>
+ /// Decodes the specified value.
+ /// </summary>
+ /// <param name="value">The string value carried by the transport. Guaranteed to never be null, although it may be empty.</param>
+ /// <returns>The deserialized form of the given string.</returns>
+ /// <exception cref="FormatException">Thrown when the string value given cannot be decoded into the required object type.</exception>
+ public object Decode(string value) {
+ Requires.NotNull(value, "value");
+ ErrorUtilities.VerifyFormat(value.Length > 0, MessagingStrings.NonEmptyStringExpected);
+ return Identifier.Parse(value, true);
+ }
+ }
}
}
diff --git a/src/DotNetOpenAuth.OpenId/OpenId/Realm.cs b/src/DotNetOpenAuth.OpenId/OpenId/Realm.cs
index d682542..28e4df0 100644
--- a/src/DotNetOpenAuth.OpenId/OpenId/Realm.cs
+++ b/src/DotNetOpenAuth.OpenId/OpenId/Realm.cs
@@ -29,6 +29,7 @@ namespace DotNetOpenAuth.OpenId {
/// </remarks>
[Serializable]
[Pure]
+ [DefaultEncoder(typeof(MessagePartRealmConverter))]
public class Realm {
/// <summary>
/// A regex used to detect a wildcard that is being used in the realm.
@@ -59,17 +60,6 @@ namespace DotNetOpenAuth.OpenId {
private Uri uri;
/// <summary>
- /// Initializes static members of the <see cref="Realm"/> class.
- /// </summary>
- static Realm() {
- Func<string, Realm> safeRealm = str => {
- Contract.Assume(str != null);
- return new Realm(str);
- };
- MessagePart.Map<Realm>(realm => realm.ToString(), realm => realm.OriginalString, safeRealm);
- }
-
- /// <summary>
/// Initializes a new instance of the <see cref="Realm"/> class.
/// </summary>
/// <param name="realmUrl">The realm URL to use in the new instance.</param>
@@ -508,5 +498,41 @@ namespace DotNetOpenAuth.OpenId {
Contract.Invariant(this.uri.AbsoluteUri != null);
}
#endif
+
+ /// <summary>
+ /// Provides conversions to and from strings for messages that include members of this type.
+ /// </summary>
+ private class MessagePartRealmConverter : IMessagePartOriginalEncoder {
+ /// <summary>
+ /// Encodes the specified value.
+ /// </summary>
+ /// <param name="value">The value. Guaranteed to never be null.</param>
+ /// <returns>The <paramref name="value"/> in string form, ready for message transport.</returns>
+ public string Encode(object value) {
+ Requires.NotNull(value, "value");
+ return value.ToString();
+ }
+
+ /// <summary>
+ /// Decodes the specified value.
+ /// </summary>
+ /// <param name="value">The string value carried by the transport. Guaranteed to never be null, although it may be empty.</param>
+ /// <returns>The deserialized form of the given string.</returns>
+ /// <exception cref="FormatException">Thrown when the string value given cannot be decoded into the required object type.</exception>
+ public object Decode(string value) {
+ Requires.NotNull(value, "value");
+ return new Realm(value);
+ }
+
+ /// <summary>
+ /// Encodes the specified value as the original value that was formerly decoded.
+ /// </summary>
+ /// <param name="value">The value. Guaranteed to never be null.</param>
+ /// <returns>The <paramref name="value"/> in string form, ready for message transport.</returns>
+ public string EncodeAsOriginalString(object value) {
+ Requires.NotNull(value, "value");
+ return ((Realm)value).OriginalString;
+ }
+ }
}
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/IdentifierTests.cs b/src/DotNetOpenAuth.Test/OpenId/IdentifierTests.cs
index f44d23e..e83beb1 100644
--- a/src/DotNetOpenAuth.Test/OpenId/IdentifierTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/IdentifierTests.cs
@@ -8,7 +8,10 @@ namespace DotNetOpenAuth.Test.OpenId {
using System;
using System.Collections.Generic;
using System.Linq;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Reflection;
using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.Test.Mocks;
using NUnit.Framework;
[TestFixture]
@@ -84,5 +87,19 @@ namespace DotNetOpenAuth.Test.OpenId {
public void ParseEmpty() {
Identifier.Parse(string.Empty);
}
+
+ [Test]
+ public void MessagePartConvertibility() {
+ var message = new MessageWithIdentifier();
+ var messageDescription = new MessageDescription(message.GetType(), new Version(1, 0));
+ var messageDictionary = new MessageDictionary(message, messageDescription, false);
+ messageDictionary["Identifier"] = OpenId.OpenIdTestBase.IdentifierSelect;
+ Assert.That(messageDictionary["Identifier"], Is.EqualTo(OpenId.OpenIdTestBase.IdentifierSelect));
+ }
+
+ private class MessageWithIdentifier : TestMessage {
+ [MessagePart]
+ internal Identifier Identifier { get; set; }
+ }
}
}
diff --git a/src/DotNetOpenAuth.Test/OpenId/RealmTests.cs b/src/DotNetOpenAuth.Test/OpenId/RealmTests.cs
index 20f9fe2..764a795 100644
--- a/src/DotNetOpenAuth.Test/OpenId/RealmTests.cs
+++ b/src/DotNetOpenAuth.Test/OpenId/RealmTests.cs
@@ -6,7 +6,10 @@
namespace DotNetOpenAuth.Test {
using System;
+ using DotNetOpenAuth.Messaging;
+ using DotNetOpenAuth.Messaging.Reflection;
using DotNetOpenAuth.OpenId;
+ using DotNetOpenAuth.Test.Mocks;
using NUnit.Framework;
[TestFixture]
@@ -207,5 +210,19 @@ namespace DotNetOpenAuth.Test {
Assert.AreNotEqual(testRealm1a, testRealm1a.ToString(), "Although the URLs are equal, different object types shouldn't be equal.");
Assert.AreNotEqual(testRealm3, testRealm1a, "Wildcard difference ignored by Equals");
}
+
+ [Test]
+ public void MessagePartConvertibility() {
+ var message = new MessageWithRealm();
+ var messageDescription = new MessageDescription(message.GetType(), new Version(1, 0));
+ var messageDictionary = new MessageDictionary(message, messageDescription, false);
+ messageDictionary["Realm"] = OpenId.OpenIdTestBase.RPRealmUri.AbsoluteUri;
+ Assert.That(messageDictionary["Realm"], Is.EqualTo(OpenId.OpenIdTestBase.RPRealmUri.AbsoluteUri));
+ }
+
+ private class MessageWithRealm : TestMessage {
+ [MessagePart]
+ internal Realm Realm { get; set; }
+ }
}
}