PageRenderTime 50ms CodeModel.GetById 10ms RepoModel.GetById 0ms app.codeStats 0ms

/Utilities/Collections/MultiDictionaryBase.cs

#
C# | 1336 lines | 737 code | 124 blank | 475 comment | 41 complexity | 1771ac5b4c1693546db31145a7a7fb25 MD5 | raw file
Possible License(s): Apache-2.0
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Text;
  5. using Delta.Utilities.Helpers;
  6. namespace Delta.Utilities.Collections
  7. {
  8. /// <summary>
  9. /// MultiDictionaryBase is a base class that can be used to more easily
  10. /// implement a class that associates multiple values to a single key.
  11. /// The class implements the generic
  12. /// IDictionary&lt;TKey, ICollection&lt;TValue&gt;&gt; interface.
  13. /// </summary>
  14. /// <remarks>
  15. /// <para>To use MultiDictionaryBase as a base class, the derived class must
  16. /// override Count, Clear, Add, Remove(TKey), Remove(TKey,TValue),
  17. /// Contains(TKey,TValue), EnumerateKeys, and TryEnumerateValuesForKey.
  18. /// </para>
  19. /// <para>It may wish consider overriding CountValues, CountAllValues,
  20. /// ContainsKey, and EqualValues, but these are not required.
  21. /// </para>
  22. /// <para>For unit tests see MultiDictionary class.</para>
  23. /// </remarks>
  24. /// <typeparam name="TKey">Key type</typeparam>
  25. /// <typeparam name="TValue">Value type</typeparam>
  26. [DebuggerDisplay("{DebuggerToString()}")]
  27. public abstract class MultiDictionaryBase<TKey, TValue>
  28. : CollectionBase<KeyValuePair<TKey, ICollection<TValue>>>,
  29. IDictionary<TKey, ICollection<TValue>>
  30. {
  31. #region ValuesForKeyCollection Class
  32. /// <summary>
  33. /// A private class that provides the ICollection&lt;TValue&gt; for a
  34. /// particular key. This is the collection that is returned from the
  35. /// indexer. The collections is read-write, live, and can be used to add,
  36. /// remove, etc. values from the multi-dictionary.
  37. /// </summary>
  38. private sealed class ValuesForKeyCollection : CollectionBase<TValue>
  39. {
  40. #region Count (Public)
  41. /// <summary>
  42. /// Get the number of values associated with the key.
  43. /// </summary>
  44. public override int Count
  45. {
  46. get
  47. {
  48. return myDictionary.CountValues(key);
  49. }
  50. }
  51. #endregion
  52. #region Private
  53. #region myDictionary (Private)
  54. /// <summary>
  55. /// My dictionary
  56. /// </summary>
  57. private readonly MultiDictionaryBase<TKey, TValue> myDictionary;
  58. #endregion
  59. #region key (Private)
  60. /// <summary>
  61. /// Key
  62. /// </summary>
  63. private readonly TKey key;
  64. #endregion
  65. #endregion
  66. #region Constructors
  67. /// <summary>
  68. /// Constructor. Initializes this collection.
  69. /// </summary>
  70. /// <param name="myDictionary">Dictionary we're using.</param>
  71. /// <param name="key">The key we're looking at.</param>
  72. public ValuesForKeyCollection(
  73. MultiDictionaryBase<TKey, TValue> myDictionary, TKey key)
  74. {
  75. this.myDictionary = myDictionary;
  76. this.key = key;
  77. }
  78. #endregion
  79. #region Clear (Public)
  80. /// <summary>
  81. /// Remove the key and all values associated with it.
  82. /// </summary>
  83. public override void Clear()
  84. {
  85. myDictionary.Remove(key);
  86. }
  87. #endregion
  88. #region Add (Public)
  89. /// <summary>
  90. /// Add a new values to this key.
  91. /// </summary>
  92. /// <param name="item">New values to add.</param>
  93. public override void Add(TValue item)
  94. {
  95. myDictionary.Add(key, item);
  96. }
  97. #endregion
  98. #region Remove (Public)
  99. /// <summary>
  100. /// Remove a value currently associated with key.
  101. /// </summary>
  102. /// <param name="item">Value to remove.</param>
  103. /// <returns>True if item was assocaited with key, false otherwise.
  104. /// </returns>
  105. public override bool Remove(TValue item)
  106. {
  107. return myDictionary.Remove(key, item);
  108. }
  109. #endregion
  110. #region GetEnumerator (Public)
  111. /// <summary>
  112. /// Enumerate all the values associated with key.
  113. /// </summary>
  114. /// <returns>An IEnumerator&lt;TValue&gt; that enumerates all the
  115. /// values associated with key.</returns>
  116. public override IEnumerator<TValue> GetEnumerator()
  117. {
  118. IEnumerator<TValue> values;
  119. if (myDictionary.TryEnumerateValuesForKey(key, out values))
  120. {
  121. return values;
  122. }
  123. else
  124. {
  125. return NoValues();
  126. }
  127. }
  128. #endregion
  129. #region Contains (Public)
  130. /// <summary>
  131. /// Determines if the given values is associated with key.
  132. /// </summary>
  133. /// <param name="item">Value to check for.</param>
  134. /// <returns>True if value is associated with key, false otherwise.
  135. /// </returns>
  136. public override bool Contains(TValue item)
  137. {
  138. return myDictionary.Contains(key, item);
  139. }
  140. #endregion
  141. #region Methods (Private)
  142. #region NoValues
  143. /// <summary>
  144. /// A simple function that returns an IEnumerator&lt;TValue&gt; that
  145. /// doesn't yield any values. A helper.
  146. /// </summary>
  147. /// <returns>An IEnumerator&lt;TValue&gt; that yields no values.
  148. /// </returns>
  149. private IEnumerator<TValue> NoValues()
  150. {
  151. yield break;
  152. }
  153. #endregion
  154. #endregion
  155. }
  156. #endregion
  157. #region KeysCollection Class
  158. /// <summary>
  159. /// A private class that implements ICollection&lt;TKey&gt; and
  160. /// ICollection for the Keys collection. The collection is read-only.
  161. /// </summary>
  162. private sealed class KeysCollection : CollectionBase<TKey>
  163. {
  164. #region Count (Public)
  165. /// <summary>
  166. /// Count
  167. /// </summary>
  168. /// <returns>Int</returns>
  169. public override int Count
  170. {
  171. get
  172. {
  173. return myDictionary.Count;
  174. } // get
  175. }
  176. #endregion
  177. #region Private
  178. #region myDictionary (Private)
  179. /// <summary>
  180. /// My dictionary
  181. /// </summary>
  182. private readonly MultiDictionaryBase<TKey, TValue> myDictionary;
  183. #endregion
  184. #endregion
  185. #region Constructors
  186. /// <summary>
  187. /// Constructor.
  188. /// </summary>
  189. /// <param name="myDictionary">The dictionary this is associated with.
  190. /// </param>
  191. public KeysCollection(MultiDictionaryBase<TKey, TValue> myDictionary)
  192. {
  193. this.myDictionary = myDictionary;
  194. }
  195. #endregion
  196. #region GetEnumerator (Public)
  197. /// <summary>
  198. /// Get enumerator
  199. /// </summary>
  200. /// <returns>IEnumerator</returns>
  201. public override IEnumerator<TKey> GetEnumerator()
  202. {
  203. return myDictionary.EnumerateKeys();
  204. }
  205. #endregion
  206. #region Contains (Public)
  207. /// <summary>
  208. /// Contains
  209. /// </summary>
  210. /// <param name="key">Key</param>
  211. /// <returns>True if the key was found, false otherwise.</returns>
  212. public override bool Contains(TKey key)
  213. {
  214. return myDictionary.ContainsKey(key);
  215. }
  216. #endregion
  217. #region Clear (Public)
  218. /// <summary>
  219. /// Clear
  220. /// </summary>
  221. public override void Clear()
  222. {
  223. throw new Exception("The method or operation is not supported!");
  224. }
  225. #endregion
  226. #region Remove (Public)
  227. /// <summary>
  228. /// Remove. Do not call, this method is not implemented!
  229. /// </summary>
  230. /// <param name="item">Item</param>
  231. /// <returns>True if removing the item succeeded.</returns>
  232. public override bool Remove(TKey item)
  233. {
  234. throw new Exception("The method or operation is not supported!");
  235. }
  236. #endregion
  237. }
  238. #endregion
  239. #region ValuesCollection Class
  240. /// <summary>
  241. /// A private class that implements ICollection&lt;TValue&gt; and
  242. /// ICollection for the Values collection. The collection is read-only.
  243. /// </summary>
  244. private sealed class ValuesCollection : CollectionBase<TValue>
  245. {
  246. #region Count (Public)
  247. /// <summary>
  248. /// Count
  249. /// </summary>
  250. /// <returns>Int</returns>
  251. public override int Count
  252. {
  253. get
  254. {
  255. return myDictionary.CountAllValues();
  256. } // get
  257. }
  258. #endregion
  259. #region Private
  260. #region myDictionary (Private)
  261. /// <summary>
  262. /// My dictionary
  263. /// </summary>
  264. private readonly MultiDictionaryBase<TKey, TValue> myDictionary;
  265. #endregion
  266. #endregion
  267. #region Constructors
  268. /// <summary>
  269. /// Create values collection
  270. /// </summary>
  271. /// <param name="myDictionary">My dictionary</param>
  272. public ValuesCollection(MultiDictionaryBase<TKey, TValue> myDictionary)
  273. {
  274. this.myDictionary = myDictionary;
  275. }
  276. #endregion
  277. #region GetEnumerator (Public)
  278. /// <summary>
  279. /// Get enumerator
  280. /// </summary>
  281. /// <returns>IEnumerator</returns>
  282. public override IEnumerator<TValue> GetEnumerator()
  283. {
  284. using (IEnumerator<TKey> enumKeys = myDictionary.EnumerateKeys())
  285. {
  286. while (enumKeys.MoveNext())
  287. {
  288. TKey key = enumKeys.Current;
  289. IEnumerator<TValue> enumValues;
  290. if (myDictionary.TryEnumerateValuesForKey(key, out enumValues))
  291. {
  292. using (enumValues)
  293. {
  294. while (enumValues.MoveNext())
  295. {
  296. yield return enumValues.Current;
  297. }
  298. }
  299. }
  300. }
  301. }
  302. }
  303. #endregion
  304. #region Contains (Public)
  305. /// <summary>
  306. /// Contains
  307. /// </summary>
  308. /// <param name="value">Value</param>
  309. /// <returns>True if the value was found in the collection.</returns>
  310. public override bool Contains(TValue value)
  311. {
  312. foreach (TValue v in this)
  313. {
  314. if (myDictionary.EqualValues(v, value))
  315. {
  316. return true;
  317. }
  318. }
  319. return false;
  320. }
  321. #endregion
  322. #region Clear (Public)
  323. /// <summary>
  324. /// Clear
  325. /// </summary>
  326. public override void Clear()
  327. {
  328. throw new Exception("The method or operation is not supported!");
  329. }
  330. #endregion
  331. #region Remove (Public)
  332. /// <summary>
  333. /// Remove. Do not call, this method is not implemented!
  334. /// </summary>
  335. /// <param name="value">Value</param>
  336. /// <returns>True if the removal succeeded.</returns>
  337. public override bool Remove(TValue value)
  338. {
  339. throw new Exception("The method or operation is not supported!");
  340. }
  341. #endregion
  342. }
  343. #endregion
  344. #region EnumerableValuesCollection Class
  345. /// <summary>
  346. /// A private class that implements
  347. /// ICollection&lt;ICollection&lt;TValue&gt;&gt; and ICollection for the
  348. /// Values collection on IDictionary. The collection is read-only.
  349. /// </summary>
  350. private sealed class EnumerableValuesCollection
  351. : CollectionBase<ICollection<TValue>>
  352. {
  353. #region Count (Public)
  354. /// <summary>
  355. /// Count
  356. /// </summary>
  357. /// <returns>Int</returns>
  358. public override int Count
  359. {
  360. get
  361. {
  362. return myDictionary.Count;
  363. } // get
  364. }
  365. #endregion
  366. #region Private
  367. #region myDictionary (Private)
  368. /// <summary>
  369. /// My dictionary
  370. /// </summary>
  371. private readonly MultiDictionaryBase<TKey, TValue> myDictionary;
  372. #endregion
  373. #endregion
  374. #region Constructors
  375. /// <summary>
  376. /// Create enumerable values collection
  377. /// </summary>
  378. /// <param name="myDictionary">My dictionary</param>
  379. public EnumerableValuesCollection(
  380. MultiDictionaryBase<TKey, TValue> myDictionary)
  381. {
  382. this.myDictionary = myDictionary;
  383. }
  384. #endregion
  385. #region GetEnumerator (Public)
  386. /// <summary>
  387. /// Get enumerator
  388. /// </summary>
  389. /// <returns>IEnumerator</returns>
  390. public override IEnumerator<ICollection<TValue>> GetEnumerator()
  391. {
  392. using (IEnumerator<TKey> enumKeys = myDictionary.EnumerateKeys())
  393. {
  394. while (enumKeys.MoveNext())
  395. {
  396. TKey key = enumKeys.Current;
  397. yield return new ValuesForKeyCollection(myDictionary, key);
  398. }
  399. }
  400. }
  401. #endregion
  402. #region Contains (Public)
  403. /// <summary>
  404. /// Contains
  405. /// </summary>
  406. /// <param name="values">Values</param>
  407. /// <returns>True if all the values were found.</returns>
  408. public override bool Contains(ICollection<TValue> values)
  409. {
  410. if (values == null)
  411. {
  412. return false;
  413. }
  414. TValue[] valueArray = ArrayHelper.ToArray(values);
  415. foreach (ICollection<TValue> v in this)
  416. {
  417. if (v.Count !=
  418. valueArray.Length)
  419. {
  420. continue;
  421. }
  422. // First check in order for efficiency.
  423. if (ArrayHelper.AreCollectionsEqual(v, values,
  424. //myDictionary.EqualValues))
  425. myDictionary.valueEqualityComparer))
  426. {
  427. return true;
  428. }
  429. // Now check not in order. We can't use Algorithms.EqualSets, because
  430. // we don't have an IEqualityComparer, just the ability to compare
  431. // for equality. Unfortunately this is N squared, but there isn't a
  432. // good choice here. We don't really expect this method to be used
  433. // much.
  434. bool[] found = new bool[valueArray.Length];
  435. foreach (TValue x in v)
  436. {
  437. for (int i = 0; i < valueArray.Length; ++i)
  438. {
  439. if (!found[i] &&
  440. myDictionary.EqualValues(x, valueArray[i]))
  441. {
  442. found[i] = true;
  443. }
  444. }
  445. }
  446. // Every item was found. The sets must be equal.
  447. if (Array.IndexOf(found, false) < 0)
  448. {
  449. return true;
  450. }
  451. }
  452. return false;
  453. }
  454. #endregion
  455. #region Clear (Public)
  456. /// <summary>
  457. /// Clear
  458. /// </summary>
  459. public override void Clear()
  460. {
  461. throw new Exception("The method or operation is not supported!");
  462. }
  463. #endregion
  464. #region Remove (Public)
  465. /// <summary>
  466. /// Remove. Do not call, this method is not implemented!
  467. /// </summary>
  468. /// <param name="item">Item</param>
  469. /// <returns>True if the removal succeeded.</returns>
  470. public override bool Remove(ICollection<TValue> item)
  471. {
  472. throw new Exception("The method or operation is not supported!");
  473. }
  474. #endregion
  475. }
  476. #endregion
  477. #region KeyValuePairsCollection Class
  478. /// <summary>
  479. /// A private class that implements
  480. /// ICollection&lt;KeyValuePair&lt;TKey,TValue&gt;&gt; and ICollection
  481. /// for the KeyValuePairs collection. The collection is read-only.
  482. /// </summary>
  483. private sealed class KeyValuePairsCollection
  484. : CollectionBase<KeyValuePair<TKey, TValue>>
  485. {
  486. #region Count (Public)
  487. /// <summary>
  488. /// Count
  489. /// </summary>
  490. /// <returns>Int</returns>
  491. public override int Count
  492. {
  493. get
  494. {
  495. return myDictionary.CountAllValues();
  496. } // get
  497. }
  498. #endregion
  499. #region Private
  500. #region myDictionary (Private)
  501. /// <summary>
  502. /// My dictionary
  503. /// </summary>
  504. private readonly MultiDictionaryBase<TKey, TValue> myDictionary;
  505. #endregion
  506. #endregion
  507. #region Constructors
  508. /// <summary>
  509. /// Create key value pairs collection
  510. /// </summary>
  511. /// <param name="myDictionary">My dictionary</param>
  512. public KeyValuePairsCollection(
  513. MultiDictionaryBase<TKey, TValue> myDictionary)
  514. {
  515. this.myDictionary = myDictionary;
  516. }
  517. #endregion
  518. #region GetEnumerator (Public)
  519. /// <summary>
  520. /// Get enumerator
  521. /// </summary>
  522. /// <returns>IEnumerator</returns>
  523. public override IEnumerator<KeyValuePair<TKey, TValue>> GetEnumerator()
  524. {
  525. using (IEnumerator<TKey> enumKeys = myDictionary.EnumerateKeys())
  526. {
  527. while (enumKeys.MoveNext())
  528. {
  529. TKey key = enumKeys.Current;
  530. IEnumerator<TValue> enumValues;
  531. if (myDictionary.TryEnumerateValuesForKey(key, out enumValues))
  532. {
  533. using (enumValues)
  534. {
  535. while (enumValues.MoveNext())
  536. {
  537. yield return new KeyValuePair<TKey, TValue>(
  538. key, enumValues.Current);
  539. }
  540. }
  541. }
  542. }
  543. }
  544. }
  545. #endregion
  546. #region Contains (Public)
  547. /// <summary>
  548. /// Contains
  549. /// </summary>
  550. /// <param name="pair">Pair</param>
  551. /// <returns>True if the pair was found, false otherwise.</returns>
  552. public override bool Contains(KeyValuePair<TKey, TValue> pair)
  553. {
  554. return myDictionary[pair.Key].Contains(pair.Value);
  555. }
  556. #endregion
  557. #region Clear (Public)
  558. /// <summary>
  559. /// Clear
  560. /// </summary>
  561. public override void Clear()
  562. {
  563. throw new Exception("The method or operation is not supported!");
  564. }
  565. #endregion
  566. #region Remove (Public)
  567. /// <summary>
  568. /// Remove. Do not call, this method is not implemented!
  569. /// </summary>
  570. /// <param name="item">Item</param>
  571. /// <returns>True if the removal succeeded, false otherwise.</returns>
  572. public override bool Remove(KeyValuePair<TKey, TValue> item)
  573. {
  574. throw new Exception("The method or operation is not supported!");
  575. }
  576. #endregion
  577. }
  578. #endregion
  579. #region Keys (Public)
  580. /// <summary>
  581. /// Gets a read-only collection all the keys in this dictionary.
  582. /// </summary>
  583. /// <value>An readonly ICollection&lt;TKey&gt; of all the keys in this
  584. /// dictionary.</value>
  585. /// <typeparam name="TKey">TKey</typeparam>
  586. /// <typeparam name="TValue">TValue</typeparam>
  587. public virtual ICollection<TKey> Keys
  588. {
  589. get
  590. {
  591. return new KeysCollection(this);
  592. } // get
  593. }
  594. #endregion
  595. #region Values (Public)
  596. /// <summary>
  597. /// Gets a read-only collection of all the values in the dictionary.
  598. /// </summary>
  599. /// <returns>A read-only ICollection&lt;TValue&gt; of all the values
  600. /// in the dictionary.</returns>
  601. /// <typeparam name="TKey">TKey</typeparam>
  602. /// <typeparam name="TValue">TValue</typeparam>
  603. public virtual ICollection<TValue> Values
  604. {
  605. get
  606. {
  607. return new ValuesCollection(this);
  608. } // get
  609. }
  610. #endregion
  611. #region KeyValuePairs (Public)
  612. /// <summary>
  613. /// Gets a read-only collection of all key-value pairs in the dictionary.
  614. /// If a key has multiple values associated with it, then a key-value
  615. /// pair is present for each value associated with the key.
  616. /// </summary>
  617. /// <typeparam name="TKey">TKey</typeparam>
  618. /// <typeparam name="TValue">TValue</typeparam>
  619. public virtual ICollection<KeyValuePair<TKey, TValue>> KeyValuePairs
  620. {
  621. get
  622. {
  623. return new KeyValuePairsCollection(this);
  624. } // get
  625. }
  626. #endregion
  627. #region Item (Public)
  628. /// <summary>
  629. /// Returns a collection of all of the values in the dictionary associated
  630. /// with <paramref name="key"/>, or changes the set of values associated
  631. /// with <paramref name="key"/>. If the key is not present in the
  632. /// dictionary, an ICollection enumerating no values is returned. The
  633. /// returned collection of values is read-write, and can be used to
  634. /// modify the collection of values associated with the key.
  635. /// </summary>
  636. /// <param name="key">The key to get the values associated with.</param>
  637. /// <value>An ICollection&lt;TValue&gt; with all the values associated
  638. /// with <paramref name="key"/>.</value>
  639. public virtual ICollection<TValue> this[TKey key]
  640. {
  641. get
  642. {
  643. return new ValuesForKeyCollection(this, key);
  644. }
  645. set
  646. {
  647. ReplaceMany(key, value);
  648. }
  649. }
  650. #endregion
  651. #region Count (Public)
  652. /// <summary>
  653. /// Gets the number of keys in the dictionary.
  654. /// This property must be overridden in the derived class.
  655. /// </summary>
  656. /// <typeparam name="TKey">TKey</typeparam>
  657. /// <typeparam name="TValue">TValue</typeparam>
  658. public abstract override int Count
  659. {
  660. get;
  661. }
  662. #endregion
  663. #region Private
  664. #region Item (Private)
  665. /// <summary>
  666. /// Gets a collection of all the values in the dictionary associated with
  667. /// <paramref name="key"/>, or changes the set of values associated with
  668. /// <paramref name="key"/>. If the key is not present in the dictionary,
  669. /// a KeyNotFound exception is thrown.
  670. /// </summary>
  671. /// <param name="key">The key to get the values associated with.</param>
  672. /// <value>An IEnumerable&lt;TValue&gt; that enumerates all the values
  673. /// associated with <paramref name="key"/>.</value>
  674. /// <exception cref="KeyNotFoundException">The given key is not present
  675. /// in the dictionary.</exception>
  676. ICollection<TValue> IDictionary<TKey, ICollection<TValue>>.this[TKey key]
  677. {
  678. get
  679. {
  680. if (ContainsKey(key))
  681. {
  682. return new ValuesForKeyCollection(this, key);
  683. }
  684. else
  685. {
  686. throw new KeyNotFoundException(
  687. "The key was not found in the collection.");
  688. }
  689. }
  690. set
  691. {
  692. ReplaceMany(key, value);
  693. }
  694. }
  695. #endregion
  696. #region Values (Private)
  697. /// <summary>
  698. /// Gets a read-only collection of all the value collections in the
  699. /// dictionary.
  700. /// </summary>
  701. /// <returns>A read-only ICollection&lt;IEnumerable&lt;TValue&gt;&gt;
  702. /// of all the values in the dictionary.</returns>
  703. ICollection<ICollection<TValue>>
  704. IDictionary<TKey, ICollection<TValue>>.Values
  705. {
  706. get
  707. {
  708. return new EnumerableValuesCollection(this);
  709. } // get
  710. }
  711. #endregion
  712. #region valueEqualityComparer (Private)
  713. /// <summary>
  714. /// Cache the equality comparer after we get it the first time.
  715. /// </summary>
  716. /// <typeparam name="TKey">TKey</typeparam>
  717. /// <typeparam name="TValue">TValue</typeparam>
  718. private volatile IEqualityComparer<TValue> valueEqualityComparer;
  719. #endregion
  720. #endregion
  721. #region ICollection<KeyValuePair<TKey,ICollection<TValue>>> Members
  722. /// <summary>
  723. /// Adds a key-value pair to the collection. The value part of the pair
  724. /// must be a collection of values to associate with the key. If values
  725. /// are already associated with the given key, the new values are added
  726. /// to the ones associated with that key.
  727. /// </summary>
  728. /// <param name="item">A KeyValuePair contains the Key and Value
  729. /// collection to add.</param>
  730. public override void Add(KeyValuePair<TKey, ICollection<TValue>> item)
  731. {
  732. AddMany(item.Key, item.Value);
  733. }
  734. /// <summary>
  735. /// Clears the dictionary.
  736. /// This method must be overridden in the derived class.
  737. /// </summary>
  738. public abstract override void Clear();
  739. /// <summary>
  740. /// Determines if this dictionary contains the given key and all of the
  741. /// values associated with that key..
  742. /// </summary>
  743. /// <param name="item">A key and collection of values to search for.
  744. /// </param>
  745. /// <returns>True if the dictionary has associated all of the values in
  746. /// <paramref name="item"/>.Value with <paramref name="item"/>.Key.
  747. /// </returns>
  748. public override bool Contains(KeyValuePair<TKey, ICollection<TValue>> item)
  749. {
  750. foreach (TValue val in item.Value)
  751. {
  752. if (!Contains(item.Key, val))
  753. {
  754. return false;
  755. }
  756. }
  757. return true;
  758. }
  759. /// <summary>
  760. /// Removes a set of values from a given key. If all values associated
  761. /// with a key are removed, then the key is removed also.
  762. /// </summary>
  763. /// <param name="item">A KeyValuePair contains a key and a set of values
  764. /// to remove from that key.</param>
  765. /// <returns>True if at least one values was found and removed.</returns>
  766. public override bool Remove(KeyValuePair<TKey, ICollection<TValue>> item)
  767. {
  768. return RemoveMany(item.Key, item.Value) > 0;
  769. }
  770. #endregion
  771. #region IDictionary<TKey,ICollection<TValue>> Members
  772. /// <summary>
  773. /// Determines whether a given key is found in the dictionary.
  774. /// </summary>
  775. /// <remarks>The default implementation simply calls
  776. /// TryEnumerateValuesForKey. It may be appropriate to override this
  777. /// method to provide a more efficient implementation.</remarks>
  778. /// <param name="key">Key to look for in the dictionary.</param>
  779. /// <returns>True if the key is present in the dictionary.</returns>
  780. public virtual bool ContainsKey(TKey key)
  781. {
  782. IEnumerator<TValue> values;
  783. return TryEnumerateValuesForKey(key, out values);
  784. }
  785. /// <summary>
  786. /// Removes a key from the dictionary.
  787. /// This method must be overridden in the derived class.
  788. /// </summary>
  789. /// <param name="key">Key to remove from the dictionary.</param>
  790. /// <returns>True if the key was found, false otherwise.</returns>
  791. public abstract bool Remove(TKey key);
  792. /// <summary>
  793. /// Implements IDictionary&lt;TKey, IEnumerable&lt;TValue&gt;&gt;.Add.
  794. /// If the key is already present, and ArgumentException is thrown.
  795. /// Otherwise, a new key is added, and new values are associated with
  796. /// that key.
  797. /// </summary>
  798. /// <param name="key">Key to add.</param>
  799. /// <param name="values">Values to associate with that key.</param>
  800. /// <exception cref="ArgumentException">The key is already present in the
  801. /// dictionary.</exception>
  802. void IDictionary<TKey, ICollection<TValue>>.Add(
  803. TKey key, ICollection<TValue> values)
  804. {
  805. if (ContainsKey(key))
  806. {
  807. throw new ArgumentException(
  808. "The key was already present in the dictionary.", "key");
  809. }
  810. else
  811. {
  812. AddMany(key, values);
  813. }
  814. }
  815. /// <summary>
  816. /// Determines if this dictionary contains a key equal to
  817. /// <paramref name="key"/>. If so, all the values associated with that key
  818. /// are returned through the values parameter. This method must be
  819. /// overridden by the derived class.
  820. /// </summary>
  821. /// <param name="key">The key to search for.</param>
  822. /// <param name="values">Returns all values associated with key, if true
  823. /// was returned.</param>
  824. /// <returns>True if the dictionary contains key. False if the dictionary
  825. /// does not contain key.</returns>
  826. bool IDictionary<TKey, ICollection<TValue>>.TryGetValue(
  827. TKey key, out ICollection<TValue> values)
  828. {
  829. if (ContainsKey(key))
  830. {
  831. values = this[key];
  832. return true;
  833. }
  834. else
  835. {
  836. values = null;
  837. return false;
  838. }
  839. }
  840. #endregion
  841. #region IEnumerable<KeyValuePair<TKey,ICollection<TValue>>> Members
  842. /// <summary>
  843. /// Enumerate all the keys in the dictionary, and for each key, the
  844. /// collection of values for that key.
  845. /// </summary>
  846. /// <returns>An enumerator to enumerate all the key,
  847. /// ICollection&lt;value&gt; pairs in the dictionary.</returns>
  848. public override IEnumerator<KeyValuePair<TKey, ICollection<TValue>>>
  849. GetEnumerator()
  850. {
  851. using (IEnumerator<TKey> enumKeys = EnumerateKeys())
  852. {
  853. while (enumKeys.MoveNext())
  854. {
  855. TKey key = enumKeys.Current;
  856. yield return new KeyValuePair<TKey, ICollection<TValue>>(
  857. key, new ValuesForKeyCollection(this, key));
  858. }
  859. }
  860. }
  861. #endregion
  862. #region Add (Public)
  863. /// <summary>
  864. /// Adds a new key-value pair to the dictionary.
  865. /// This method must be overridden in the derived class.
  866. /// </summary>
  867. /// <param name="key">Key to add.</param>
  868. /// <param name="value">Value to associated with the key.</param>
  869. /// <exception cref="ArgumentException">key is already present in the
  870. /// dictionary</exception>
  871. public abstract void Add(TKey key, TValue value);
  872. #endregion
  873. #region Remove (Public)
  874. /// <summary>
  875. /// Removes a key-value pair from the dictionary.
  876. /// This method must be overridden in the derived class.
  877. /// </summary>
  878. /// <param name="key">Key to remove from the dictionary.</param>
  879. /// <param name="value">Associated value to remove from the dictionary.</param>
  880. /// <returns>True if the key-value pair was found, false otherwise.</returns>
  881. public abstract bool Remove(TKey key, TValue value);
  882. #endregion
  883. #region AddMany (Public)
  884. /// <summary>
  885. /// <para>Adds new values to be associated with a key. If duplicate values
  886. /// are permitted, this method always adds new key-value pairs to the
  887. /// dictionary.</para>
  888. /// <para>If duplicate values are not permitted, and <paramref name="key"/>
  889. /// already has a value equal to one of <paramref name="values"/>
  890. /// associated with it, then that value is replaced, and the number of
  891. /// values associate with <paramref name="key"/> is unchanged.</para>
  892. /// </summary>
  893. /// <param name="key">The key to associate with.</param>
  894. /// <param name="values">A collection of values to associate with
  895. /// <paramref name="key"/>.</param>
  896. public virtual void AddMany(TKey key, IEnumerable<TValue> values)
  897. {
  898. foreach (TValue value in values)
  899. {
  900. Add(key, value);
  901. }
  902. }
  903. #endregion
  904. #region RemoveMany (Public)
  905. /// <summary>
  906. /// Removes a collection of values from the values associated with a key.
  907. /// If the last value is removed from a key, the key is removed also.
  908. /// </summary>
  909. /// <param name="key">A key to remove values from.</param>
  910. /// <param name="values">A collection of values to remove.</param>
  911. /// <returns>The number of values that were present and removed. </returns>
  912. public virtual int RemoveMany(TKey key, IEnumerable<TValue> values)
  913. {
  914. int countRemoved = 0;
  915. foreach (TValue val in values)
  916. {
  917. if (Remove(key, val))
  918. {
  919. ++countRemoved;
  920. }
  921. }
  922. return countRemoved;
  923. }
  924. /// <summary>
  925. /// Remove all of the keys (and any associated values) in a collection
  926. /// of keys. If a key is not present in the dictionary, nothing happens.
  927. /// </summary>
  928. /// <param name="keyCollection">A collection of key values to remove.
  929. /// </param>
  930. /// <returns>The number of keys from the collection that were present and
  931. /// removed.</returns>
  932. public int RemoveMany(IEnumerable<TKey> keyCollection)
  933. {
  934. int count = 0;
  935. foreach (TKey key in keyCollection)
  936. {
  937. if (Remove(key))
  938. {
  939. ++count;
  940. }
  941. }
  942. return count;
  943. }
  944. #endregion
  945. #region Contains (Public)
  946. /// <summary>
  947. /// Determines if this dictionary contains a key-value pair equal to
  948. /// <paramref name="key"/> and <paramref name="value"/>. The dictionary
  949. /// is not changed. This method must be overridden in the derived class.
  950. /// </summary>
  951. /// <param name="key">The key to search for.</param>
  952. /// <param name="value">The value to search for.</param>
  953. /// <returns>True if the dictionary has associated <paramref name="value"/>
  954. /// with <paramref name="key"/>.</returns>
  955. public abstract bool Contains(TKey key, TValue value);
  956. #endregion
  957. #region Replace (Public)
  958. /// <summary>
  959. /// Replaces all values associated with <paramref name="key"/> with the
  960. /// single value <paramref name="value"/>.
  961. /// </summary>
  962. /// <remarks>This implementation simply calls Remove, followed by Add.
  963. /// </remarks>
  964. /// <param name="key">The key to associate with.</param>
  965. /// <param name="value">The new values to be associated with
  966. /// <paramref name="key"/>.</param>
  967. /// <returns>Returns true if some values were removed. Returns false if
  968. /// <paramref name="key"/> was not present in the dictionary before Replace
  969. /// was called.</returns>
  970. public virtual bool Replace(TKey key, TValue value)
  971. {
  972. bool removed = Remove(key);
  973. Add(key, value);
  974. return removed;
  975. }
  976. #endregion
  977. #region ReplaceMany (Public)
  978. /// <summary>
  979. /// Replaces all values associated with <paramref name="key"/> with a new
  980. /// collection of values. If the collection does not permit duplicate
  981. /// values, and <paramref name="values"/> has duplicate items, then only
  982. /// the last of duplicates is added.
  983. /// </summary>
  984. /// <param name="key">The key to associate with.</param>
  985. /// <param name="values">The new values to be associated with
  986. /// <paramref name="key"/>.</param>
  987. /// <returns>Returns true if some values were removed. Returns false if
  988. /// <paramref name="key"/> was not
  989. /// present in the dictionary before Replace was called.</returns>
  990. public bool ReplaceMany(TKey key, IEnumerable<TValue> values)
  991. {
  992. bool removed = Remove(key);
  993. AddMany(key, values);
  994. return removed;
  995. }
  996. #endregion
  997. #region ToString (Public)
  998. /// <summary>
  999. /// Shows the string representation of the dictionary. The string
  1000. /// representation contains a list of the mappings in the dictionary.
  1001. /// </summary>
  1002. /// <returns>The string representation of the dictionary.</returns>
  1003. public override string ToString()
  1004. {
  1005. bool firstItem = true;
  1006. StringBuilder builder = new StringBuilder();
  1007. builder.Append("{");
  1008. // Call ToString on each item and put it in.
  1009. foreach (KeyValuePair<TKey, ICollection<TValue>> pair in this)
  1010. {
  1011. if (!firstItem)
  1012. {
  1013. builder.Append(", ");
  1014. }
  1015. if (pair.Key == null)
  1016. {
  1017. builder.Append("null");
  1018. }
  1019. else
  1020. {
  1021. builder.Append(pair.Key.ToString());
  1022. }
  1023. builder.Append("->");
  1024. // Put all values in a parenthesized list.
  1025. builder.Append('(');
  1026. bool firstValue = true;
  1027. foreach (TValue val in pair.Value)
  1028. {
  1029. if (!firstValue)
  1030. {
  1031. builder.Append(",");
  1032. }
  1033. if (val == null)
  1034. {
  1035. builder.Append("null");
  1036. }
  1037. else
  1038. {
  1039. builder.Append(val.ToString());
  1040. }
  1041. firstValue = false;
  1042. }
  1043. builder.Append(')');
  1044. firstItem = false;
  1045. }
  1046. builder.Append("}");
  1047. return builder.ToString();
  1048. }
  1049. #endregion
  1050. #region Methods (Private)
  1051. #region EnumerateKeys
  1052. /// <summary>
  1053. /// Enumerate all the keys in the dictionary.
  1054. /// This method must be overridden by a derived class.
  1055. /// </summary>
  1056. /// <returns>An IEnumerator&lt;TKey&gt; that enumerates all of the keys
  1057. /// in the collection that have at least one value associated with them.
  1058. /// </returns>
  1059. protected abstract IEnumerator<TKey> EnumerateKeys();
  1060. #endregion
  1061. #region TryEnumerateValuesForKey
  1062. /// <summary>
  1063. /// Enumerate all of the values associated with a given key. This method
  1064. /// must be overridden by the derived class. If the key exists and has
  1065. /// values associated with it, an enumerator for those values is returned
  1066. /// throught <paramref name="values"/>. If the key does not exist, false
  1067. /// is returned.
  1068. /// </summary>
  1069. /// <param name="key">The key to get values for.</param>
  1070. /// <param name="values">If true is returned, this parameter receives an
  1071. /// enumerators that enumerates the values associated with that key.
  1072. /// </param>
  1073. /// <returns>True if the key exists and has values associated with it.
  1074. /// False otherwise.</returns>
  1075. protected abstract bool TryEnumerateValuesForKey(
  1076. TKey key, out IEnumerator<TValue> values);
  1077. #endregion
  1078. #region EqualValues
  1079. /// <summary>
  1080. /// If the derived class does not use the default comparison for values,
  1081. /// this methods should be overridden to compare two values for equality.
  1082. /// This is used for the correct implementation of ICollection.Contains
  1083. /// on the Values and KeyValuePairs collections.
  1084. /// </summary>
  1085. /// <param name="value1">First value to compare.</param>
  1086. /// <param name="value2">Second value to compare.</param>
  1087. /// <returns>True if the values are equal.</returns>
  1088. protected virtual bool EqualValues(TValue value1, TValue value2)
  1089. {
  1090. if (valueEqualityComparer == null)
  1091. {
  1092. valueEqualityComparer = EqualityComparer<TValue>.Default;
  1093. }
  1094. return valueEqualityComparer.Equals(value1, value2);
  1095. }
  1096. #endregion
  1097. #region CountValues
  1098. /// <summary>
  1099. /// Gets a count of the number of values associated with a key. The
  1100. /// default implementation is slow; it enumerators all of the values
  1101. /// (using TryEnumerateValuesForKey) to count them. A derived class
  1102. /// may be able to supply a more efficient implementation.
  1103. /// </summary>
  1104. /// <param name="key">The key to count values for.</param>
  1105. /// <returns>The number of values associated with
  1106. /// <paramref name="key"/>.</returns>
  1107. protected virtual int CountValues(TKey key)
  1108. {
  1109. int count = 0;
  1110. IEnumerator<TValue> enumValues;
  1111. if (TryEnumerateValuesForKey(key, out enumValues))
  1112. {
  1113. using (enumValues)
  1114. {
  1115. while (enumValues.MoveNext())
  1116. {
  1117. count += 1;
  1118. }
  1119. }
  1120. }
  1121. return count;
  1122. }
  1123. #endregion
  1124. #region CountAllValues
  1125. /// <summary>
  1126. /// Gets a total count of values in the collection. This default
  1127. /// implementation is slow; it enumerates all of the keys in the dictionary
  1128. /// and calls CountValues on each. A derived class may be able to supply
  1129. /// a more efficient implementation.
  1130. /// </summary>
  1131. /// <returns>The total number of values associated with all keys in the
  1132. /// dictionary.</returns>
  1133. protected virtual int CountAllValues()
  1134. {
  1135. int count = 0;
  1136. using (IEnumerator<TKey> enumKeys = EnumerateKeys())
  1137. {
  1138. while (enumKeys.MoveNext())
  1139. {
  1140. TKey key = enumKeys.Current;
  1141. count += CountValues(key);
  1142. }
  1143. }
  1144. return count;
  1145. }
  1146. #endregion
  1147. #region DebuggerToString
  1148. /// <summary>
  1149. /// Display the contents of the dictionary in the debugger. This is
  1150. /// intentionally private, it is called only from the debugger due to the
  1151. /// presence of the DebuggerDisplay attribute. It is similar format to
  1152. /// ToString(), but is limited to 250-300 characters or so, so as not to
  1153. /// overload the debugger.
  1154. /// </summary>
  1155. /// <returns>The string representation of the items in the collection,
  1156. /// similar in format to ToString().</returns>
  1157. internal new string DebuggerToString()
  1158. {
  1159. const int MAXLENGTH = 250;
  1160. bool firstItem = true;
  1161. StringBuilder builder = new StringBuilder();
  1162. builder.Append("{");
  1163. // Call ToString on each item and put it in.
  1164. foreach (KeyValuePair<TKey, ICollection<TValue>> pair in this)
  1165. {
  1166. if (builder.Length >= MAXLENGTH)
  1167. {
  1168. builder.Append(", ...");
  1169. break;
  1170. }
  1171. if (!firstItem)
  1172. {
  1173. builder.Append(", ");
  1174. }
  1175. if (pair.Key == null)
  1176. {
  1177. builder.Append("null");
  1178. }
  1179. else
  1180. {
  1181. builder.Append(pair.Key.ToString());
  1182. }
  1183. builder.Append("->");
  1184. // Put all values in a parenthesized list.
  1185. builder.Append('(');
  1186. bool firstValue = true;
  1187. foreach (TValue val in pair.Value)
  1188. {
  1189. if (!firstValue)
  1190. {
  1191. builder.Append(",");
  1192. }
  1193. if (val == null)
  1194. {
  1195. builder.Append("null");
  1196. }
  1197. else
  1198. {
  1199. builder.Append(val.ToString());
  1200. }
  1201. firstValue = false;
  1202. }
  1203. builder.Append(')');
  1204. firstItem = false;
  1205. }
  1206. builder.Append("}");
  1207. return builder.ToString();
  1208. }
  1209. #endregion
  1210. #endregion
  1211. }
  1212. }