PageRenderTime 37ms CodeModel.GetById 21ms RepoModel.GetById 0ms app.codeStats 0ms

/source/dotnetopenid/src/DotNetOpenAuth/Messaging/Reflection/MessageDictionary.cs

http://myspaceid-csharp-sdk.googlecode.com/
C# | 321 lines | 170 code | 40 blank | 111 comment | 30 complexity | 3300ba58a1ba3b9d14c214b8f408fb7d MD5 | raw file
  1. //-----------------------------------------------------------------------
  2. // <copyright file="MessageDictionary.cs" company="Andrew Arnott">
  3. // Copyright (c) Andrew Arnott. All rights reserved.
  4. // </copyright>
  5. //-----------------------------------------------------------------------
  6. namespace DotNetOpenAuth.Messaging.Reflection {
  7. using System;
  8. using System.Collections;
  9. using System.Collections.Generic;
  10. using System.Diagnostics;
  11. /// <summary>
  12. /// Wraps an <see cref="IMessage"/> instance in a dictionary that
  13. /// provides access to both well-defined message properties and "extra"
  14. /// name/value pairs that have no properties associated with them.
  15. /// </summary>
  16. internal class MessageDictionary : IDictionary<string, string> {
  17. /// <summary>
  18. /// The <see cref="IMessage"/> instance manipulated by this dictionary.
  19. /// </summary>
  20. private IMessage message;
  21. /// <summary>
  22. /// The <see cref="MessageDescription"/> instance that describes the message type.
  23. /// </summary>
  24. private MessageDescription description;
  25. /// <summary>
  26. /// Initializes a new instance of the <see cref="MessageDictionary"/> class.
  27. /// </summary>
  28. /// <param name="message">The message instance whose values will be manipulated by this dictionary.</param>
  29. internal MessageDictionary(IMessage message) {
  30. if (message == null) {
  31. throw new ArgumentNullException("message");
  32. }
  33. this.message = message;
  34. this.description = MessageDescription.Get(message.GetType(), message.Version);
  35. }
  36. #region ICollection<KeyValuePair<string,string>> Properties
  37. /// <summary>
  38. /// Gets the number of explicitly set values in the message.
  39. /// </summary>
  40. public int Count {
  41. get { return this.Keys.Count; }
  42. }
  43. /// <summary>
  44. /// Gets a value indicating whether this message is read only.
  45. /// </summary>
  46. bool ICollection<KeyValuePair<string, string>>.IsReadOnly {
  47. get { return false; }
  48. }
  49. #endregion
  50. #region IDictionary<string,string> Properties
  51. /// <summary>
  52. /// Gets all the keys that have values associated with them.
  53. /// </summary>
  54. public ICollection<string> Keys {
  55. get {
  56. List<string> keys = new List<string>(this.message.ExtraData.Count + this.description.Mapping.Count);
  57. keys.AddRange(this.DeclaredKeys);
  58. keys.AddRange(this.AdditionalKeys);
  59. return keys.AsReadOnly();
  60. }
  61. }
  62. /// <summary>
  63. /// Gets the set of official OAuth keys that have non-null values associated with them.
  64. /// </summary>
  65. public ICollection<string> DeclaredKeys {
  66. get {
  67. List<string> keys = new List<string>(this.description.Mapping.Count);
  68. foreach (var pair in this.description.Mapping) {
  69. // Don't include keys with null values, but default values for structs is ok
  70. if (pair.Value.GetValue(this.message) != null) {
  71. keys.Add(pair.Key);
  72. }
  73. }
  74. return keys.AsReadOnly();
  75. }
  76. }
  77. /// <summary>
  78. /// Gets the keys that are in the message but not declared as official OAuth properties.
  79. /// </summary>
  80. public ICollection<string> AdditionalKeys {
  81. get { return this.message.ExtraData.Keys; }
  82. }
  83. /// <summary>
  84. /// Gets all the values.
  85. /// </summary>
  86. public ICollection<string> Values {
  87. get {
  88. List<string> values = new List<string>(this.message.ExtraData.Count + this.description.Mapping.Count);
  89. foreach (MessagePart part in this.description.Mapping.Values) {
  90. if (part.GetValue(this.message) != null) {
  91. values.Add(part.GetValue(this.message));
  92. }
  93. }
  94. foreach (string value in this.message.ExtraData.Values) {
  95. Debug.Assert(value != null, "Null values should never be allowed in the extra data dictionary.");
  96. values.Add(value);
  97. }
  98. return values.AsReadOnly();
  99. }
  100. }
  101. /// <summary>
  102. /// Gets or sets a value for some named value.
  103. /// </summary>
  104. /// <param name="key">The serialized form of a name for the value to read or write.</param>
  105. /// <returns>The named value.</returns>
  106. /// <remarks>
  107. /// If the key matches a declared property or field on the message type,
  108. /// that type member is set. Otherwise the key/value is stored in a
  109. /// dictionary for extra (weakly typed) strings.
  110. /// </remarks>
  111. /// <exception cref="ArgumentException">Thrown when setting a value that is not allowed for a given <paramref name="key"/>.</exception>
  112. public string this[string key] {
  113. get {
  114. MessagePart part;
  115. if (this.description.Mapping.TryGetValue(key, out part)) {
  116. // Never throw KeyNotFoundException for declared properties.
  117. return part.GetValue(this.message);
  118. } else {
  119. return this.message.ExtraData[key];
  120. }
  121. }
  122. set {
  123. MessagePart part;
  124. if (this.description.Mapping.TryGetValue(key, out part)) {
  125. part.SetValue(this.message, value);
  126. } else {
  127. if (value == null) {
  128. this.message.ExtraData.Remove(key);
  129. } else {
  130. this.message.ExtraData[key] = value;
  131. }
  132. }
  133. }
  134. }
  135. #endregion
  136. #region IDictionary<string,string> Methods
  137. /// <summary>
  138. /// Adds a named value to the message.
  139. /// </summary>
  140. /// <param name="key">The serialized form of the name whose value is being set.</param>
  141. /// <param name="value">The serialized form of the value.</param>
  142. /// <exception cref="ArgumentException">
  143. /// Thrown if <paramref name="key"/> already has a set value in this message.
  144. /// </exception>
  145. /// <exception cref="ArgumentNullException">
  146. /// Thrown if <paramref name="value"/> is null.
  147. /// </exception>
  148. public void Add(string key, string value) {
  149. if (value == null) {
  150. throw new ArgumentNullException("value");
  151. }
  152. MessagePart part;
  153. if (this.description.Mapping.TryGetValue(key, out part)) {
  154. if (part.IsNondefaultValueSet(this.message)) {
  155. throw new ArgumentException(MessagingStrings.KeyAlreadyExists);
  156. }
  157. part.SetValue(this.message, value);
  158. } else {
  159. this.message.ExtraData.Add(key, value);
  160. }
  161. }
  162. /// <summary>
  163. /// Checks whether some named parameter has a value set in the message.
  164. /// </summary>
  165. /// <param name="key">The serialized form of the message part's name.</param>
  166. /// <returns>True if the parameter by the given name has a set value. False otherwise.</returns>
  167. public bool ContainsKey(string key) {
  168. return this.message.ExtraData.ContainsKey(key) ||
  169. (this.description.Mapping.ContainsKey(key) && this.description.Mapping[key].GetValue(this.message) != null);
  170. }
  171. /// <summary>
  172. /// Removes a name and value from the message given its name.
  173. /// </summary>
  174. /// <param name="key">The serialized form of the name to remove.</param>
  175. /// <returns>True if a message part by the given name was found and removed. False otherwise.</returns>
  176. public bool Remove(string key) {
  177. if (this.message.ExtraData.Remove(key)) {
  178. return true;
  179. } else {
  180. MessagePart part;
  181. if (this.description.Mapping.TryGetValue(key, out part)) {
  182. if (part.GetValue(this.message) != null) {
  183. part.SetValue(this.message, null);
  184. return true;
  185. }
  186. }
  187. return false;
  188. }
  189. }
  190. /// <summary>
  191. /// Gets some named value if the key has a value.
  192. /// </summary>
  193. /// <param name="key">The name (in serialized form) of the value being sought.</param>
  194. /// <param name="value">The variable where the value will be set.</param>
  195. /// <returns>True if the key was found and <paramref name="value"/> was set. False otherwise.</returns>
  196. public bool TryGetValue(string key, out string value) {
  197. MessagePart part;
  198. if (this.description.Mapping.TryGetValue(key, out part)) {
  199. value = part.GetValue(this.message);
  200. return true;
  201. }
  202. return this.message.ExtraData.TryGetValue(key, out value);
  203. }
  204. #endregion
  205. #region ICollection<KeyValuePair<string,string>> Methods
  206. /// <summary>
  207. /// Sets a named value in the message.
  208. /// </summary>
  209. /// <param name="item">The name-value pair to add. The name is the serialized form of the key.</param>
  210. public void Add(KeyValuePair<string, string> item) {
  211. this.Add(item.Key, item.Value);
  212. }
  213. /// <summary>
  214. /// Removes all values in the message.
  215. /// </summary>
  216. public void Clear() {
  217. foreach (string key in this.Keys) {
  218. this.Remove(key);
  219. }
  220. }
  221. /// <summary>
  222. /// Checks whether a named value has been set on the message.
  223. /// </summary>
  224. /// <param name="item">The name/value pair.</param>
  225. /// <returns>True if the key exists and has the given value. False otherwise.</returns>
  226. public bool Contains(KeyValuePair<string, string> item) {
  227. MessagePart part;
  228. if (this.description.Mapping.TryGetValue(item.Key, out part)) {
  229. return string.Equals(part.GetValue(this.message), item.Value, StringComparison.Ordinal);
  230. } else {
  231. return this.message.ExtraData.Contains(item);
  232. }
  233. }
  234. /// <summary>
  235. /// Copies all the serializable data from the message to a key/value array.
  236. /// </summary>
  237. /// <param name="array">The array to copy to.</param>
  238. /// <param name="arrayIndex">The index in the <paramref name="array"/> to begin copying to.</param>
  239. void ICollection<KeyValuePair<string, string>>.CopyTo(KeyValuePair<string, string>[] array, int arrayIndex) {
  240. foreach (var pair in (IDictionary<string, string>)this) {
  241. array[arrayIndex++] = pair;
  242. }
  243. }
  244. /// <summary>
  245. /// Removes a named value from the message if it exists.
  246. /// </summary>
  247. /// <param name="item">The serialized form of the name and value to remove.</param>
  248. /// <returns>True if the name/value was found and removed. False otherwise.</returns>
  249. public bool Remove(KeyValuePair<string, string> item) {
  250. // We use contains because that checks that the value is equal as well.
  251. if (((ICollection<KeyValuePair<string, string>>)this).Contains(item)) {
  252. ((IDictionary<string, string>)this).Remove(item.Key);
  253. return true;
  254. }
  255. return false;
  256. }
  257. #endregion
  258. #region IEnumerable<KeyValuePair<string,string>> Members
  259. /// <summary>
  260. /// Gets an enumerator that generates KeyValuePair&lt;string, string&gt; instances
  261. /// for all the key/value pairs that are set in the message.
  262. /// </summary>
  263. /// <returns>The enumerator that can generate the name/value pairs.</returns>
  264. public IEnumerator<KeyValuePair<string, string>> GetEnumerator() {
  265. foreach (string key in this.Keys) {
  266. yield return new KeyValuePair<string, string>(key, this[key]);
  267. }
  268. }
  269. #endregion
  270. #region IEnumerable Members
  271. /// <summary>
  272. /// Gets an enumerator that generates KeyValuePair&lt;string, string&gt; instances
  273. /// for all the key/value pairs that are set in the message.
  274. /// </summary>
  275. /// <returns>The enumerator that can generate the name/value pairs.</returns>
  276. IEnumerator System.Collections.IEnumerable.GetEnumerator() {
  277. return ((IEnumerable<KeyValuePair<string, string>>)this).GetEnumerator();
  278. }
  279. #endregion
  280. }
  281. }