Added GetHashCode() methods to the AX classes, and fixed their Equals methods.
[dotnetoauth.git] / src / DotNetOpenAuth / OpenId / Extensions / AttributeExchange / StoreRequest.cs
blob41f5a2fdd545ab072e16a0b197d7364e0be935fb
1 //-----------------------------------------------------------------------
2 // <copyright file="StoreRequest.cs" company="Andrew Arnott">
3 // Copyright (c) Andrew Arnott. All rights reserved.
4 // </copyright>
5 //-----------------------------------------------------------------------
7 namespace DotNetOpenAuth.OpenId.Extensions.AttributeExchange {
8 using System;
9 using System.Collections.Generic;
10 using System.Linq;
11 using DotNetOpenAuth.Messaging;
12 using DotNetOpenAuth.OpenId.Messages;
14 /// <summary>
15 /// The Attribute Exchange Store message, request leg.
16 /// </summary>
17 public sealed class StoreRequest : ExtensionBase, IMessageWithEvents {
18 /// <summary>
19 /// The factory method that may be used in deserialization of this message.
20 /// </summary>
21 internal static readonly OpenIdExtensionFactory.CreateDelegate Factory = (typeUri, data, baseMessage) => {
22 if (typeUri == Constants.TypeUri && baseMessage is SignedResponseRequest) {
23 string mode;
24 if (data.TryGetValue("mode", out mode) && mode == Mode) {
25 return new StoreRequest();
29 return null;
32 /// <summary>
33 /// The value of the 'mode' parameter.
34 /// </summary>
35 [MessagePart("mode", IsRequired = true)]
36 private const string Mode = "store_request";
38 /// <summary>
39 /// The list of provided attribute values. This field will never be null.
40 /// </summary>
41 private readonly List<AttributeValues> attributesProvided = new List<AttributeValues>();
43 /// <summary>
44 /// Initializes a new instance of the <see cref="StoreRequest"/> class.
45 /// </summary>
46 public StoreRequest()
47 : base(new Version(1, 0), Constants.TypeUri, null) {
50 /// <summary>
51 /// Gets a list of all the attributes that are included in the store request.
52 /// </summary>
53 public IEnumerable<AttributeValues> Attributes {
54 get { return this.attributesProvided; }
57 /// <summary>
58 /// Adds a given attribute with one or more values to the request for storage.
59 /// Applicable to Relying Parties only.
60 /// </summary>
61 /// <param name="attribute">The attribute values.</param>
62 public void AddAttribute(AttributeValues attribute) {
63 ErrorUtilities.VerifyArgumentNotNull(attribute, "attribute");
64 ErrorUtilities.VerifyArgumentNamed(!this.ContainsAttribute(attribute.TypeUri), "attribute", OpenIdStrings.AttributeAlreadyAdded, attribute.TypeUri);
65 this.attributesProvided.Add(attribute);
68 /// <summary>
69 /// Adds a given attribute with one or more values to the request for storage.
70 /// Applicable to Relying Parties only.
71 /// </summary>
72 /// <param name="typeUri">The type URI of the attribute.</param>
73 /// <param name="values">The attribute values.</param>
74 public void AddAttribute(string typeUri, params string[] values) {
75 this.AddAttribute(new AttributeValues(typeUri, values));
78 /// <summary>
79 /// Gets the value(s) associated with a given attribute that should be stored.
80 /// Applicable to Providers only.
81 /// </summary>
82 /// <param name="attributeTypeUri">The type URI of the attribute whose values are being sought.</param>
83 /// <returns>The attribute values.</returns>
84 public AttributeValues GetAttribute(string attributeTypeUri) {
85 return this.attributesProvided.SingleOrDefault(attribute => string.Equals(attribute.TypeUri, attributeTypeUri, StringComparison.Ordinal));
88 #region IMessageWithEvents Members
90 /// <summary>
91 /// Called when the message is about to be transmitted,
92 /// before it passes through the channel binding elements.
93 /// </summary>
94 void IMessageWithEvents.OnSending() {
95 var fields = ((IMessage)this).ExtraData;
96 fields.Clear();
98 AXUtilities.SerializeAttributes(fields, this.attributesProvided);
101 /// <summary>
102 /// Called when the message has been received,
103 /// after it passes through the channel binding elements.
104 /// </summary>
105 void IMessageWithEvents.OnReceiving() {
106 var fields = ((IMessage)this).ExtraData;
107 foreach (var att in AXUtilities.DeserializeAttributes(fields)) {
108 this.AddAttribute(att);
112 #endregion
114 /// <summary>
115 /// Determines whether the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>.
116 /// </summary>
117 /// <param name="obj">The <see cref="T:System.Object"/> to compare with the current <see cref="T:System.Object"/>.</param>
118 /// <returns>
119 /// true if the specified <see cref="T:System.Object"/> is equal to the current <see cref="T:System.Object"/>; otherwise, false.
120 /// </returns>
121 /// <exception cref="T:System.NullReferenceException">
122 /// The <paramref name="obj"/> parameter is null.
123 /// </exception>
124 public override bool Equals(object obj) {
125 var other = obj as StoreRequest;
126 if (other == null) {
127 return false;
130 if (this.Version != other.Version) {
131 return false;
134 if (!MessagingUtilities.AreEquivalentUnordered(this.Attributes.ToList(), other.Attributes.ToList())) {
135 return false;
138 return true;
141 /// <summary>
142 /// Serves as a hash function for a particular type.
143 /// </summary>
144 /// <returns>
145 /// A hash code for the current <see cref="T:System.Object"/>.
146 /// </returns>
147 public override int GetHashCode() {
148 unchecked {
149 int hashCode = this.Version.GetHashCode();
150 foreach (AttributeValues att in this.Attributes) {
151 hashCode += att.GetHashCode();
153 return hashCode;
157 /// <summary>
158 /// Determines whether some attribute has values in this store request.
159 /// </summary>
160 /// <param name="typeUri">The type URI of the attribute in question.</param>
161 /// <returns>
162 /// <c>true</c> if the specified attribute appears in the store request; otherwise, <c>false</c>.
163 /// </returns>
164 private bool ContainsAttribute(string typeUri) {
165 return this.GetAttribute(typeUri) != null;