1 // Copyright (c) Microsoft. All rights reserved.
2 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
3 //-----------------------------------------------------------------------
4 // </copyright>
5 // <summary>A read-only dictionary wrapper which converts values as they are accessed.</summary>
6 //-----------------------------------------------------------------------
7 
8 using System;
9 using System.Collections.Generic;
10 using System.Linq;
11 using System.Text;
12 
13 using Microsoft.Build.Evaluation;
14 using Microsoft.Build.Shared;
15 
16 namespace Microsoft.Build.Collections
17 {
18     /// <summary>
19     /// Implementation of a dictionary which acts as a read-only wrapper on another dictionary, but
20     /// converts values as they are accessed to another type.
21     /// </summary>
22     /// <typeparam name="K">The backing dictionary's key type.</typeparam>
23     /// <typeparam name="V">The backing dictionary's value type.</typeparam>
24     /// <typeparam name="N">The desired value type.</typeparam>
25     internal class ReadOnlyConvertingDictionary<K, V, N> : IDictionary<K, N>
26     {
27         /// <summary>
28         /// The backing dictionary.
29         /// </summary>
30         private readonly IDictionary<K, V> _backing;
31 
32         /// <summary>
33         /// The delegate used to convert values.
34         /// </summary>
35         private readonly Func<V, N> _converter;
36 
37         /// <summary>
38         /// Constructor.
39         /// </summary>
ReadOnlyConvertingDictionary(IDictionary<K, V> backing, Func<V, N> converter)40         internal ReadOnlyConvertingDictionary(IDictionary<K, V> backing, Func<V, N> converter)
41         {
42             ErrorUtilities.VerifyThrowArgumentNull(backing, "backing");
43             ErrorUtilities.VerifyThrowArgumentNull(converter, "converter");
44 
45             _backing = backing;
46             _converter = converter;
47         }
48 
49         #region IDictionary<string,string> Members
50 
51         /// <summary>
52         /// Returns the collection of keys in the dictionary.
53         /// </summary>
54         public ICollection<K> Keys
55         {
56             get { return _backing.Keys; }
57         }
58 
59         /// <summary>
60         /// Returns the collection of values in the dictionary.
61         /// </summary>
62         public ICollection<N> Values
63         {
64             get
65             {
66                 ErrorUtilities.ThrowInternalError("Values is not supported on ReadOnlyConvertingDictionary.");
67 
68                 // Show the compiler that this always throws:
69                 throw new NotImplementedException();
70             }
71         }
72 
73         /// <summary>
74         /// Returns the number of items in the collection.
75         /// </summary>
76         public int Count
77         {
78             get { return _backing.Count; }
79         }
80 
81         /// <summary>
82         /// Returns true if the collection is read-only.
83         /// </summary>
84         public bool IsReadOnly
85         {
86             get { return true; }
87         }
88 
89         /// <summary>
90         /// Accesses the value for the specified key.
91         /// </summary>
92         public N this[K key]
93         {
94             get
95             {
96                 return _converter(_backing[key]);
97             }
98 
99             set
100             {
101                 ErrorUtilities.ThrowInvalidOperation("OM_NotSupportedReadOnlyCollection");
102             }
103         }
104 
105         /// <summary>
106         /// Adds a value to the dictionary.
107         /// </summary>
Add(K key, N value)108         public void Add(K key, N value)
109         {
110             ErrorUtilities.ThrowInvalidOperation("OM_NotSupportedReadOnlyCollection");
111         }
112 
113         /// <summary>
114         /// Returns true if the dictionary contains the specified key.
115         /// </summary>
ContainsKey(K key)116         public bool ContainsKey(K key)
117         {
118             return _backing.ContainsKey(key);
119         }
120 
121         /// <summary>
122         /// Removes the entry for the specified key from the dictionary.
123         /// </summary>
Remove(K key)124         public bool Remove(K key)
125         {
126             ErrorUtilities.ThrowInvalidOperation("OM_NotSupportedReadOnlyCollection");
127             return false;
128         }
129 
130         /// <summary>
131         /// Attempts to find the value for the specified key in the dictionary.
132         /// </summary>
TryGetValue(K key, out N value)133         public bool TryGetValue(K key, out N value)
134         {
135             V originalValue;
136             if (_backing.TryGetValue(key, out originalValue))
137             {
138                 value = _converter(originalValue);
139                 return true;
140             }
141 
142             value = default(N);
143             return false;
144         }
145 
146         #endregion
147 
148         #region ICollection<KeyValuePair<string,string>> Members
149 
150         /// <summary>
151         /// Adds an item to the collection.
152         /// </summary>
Add(KeyValuePair<K, N> item)153         public void Add(KeyValuePair<K, N> item)
154         {
155             ErrorUtilities.ThrowInvalidOperation("OM_NotSupportedReadOnlyCollection");
156         }
157 
158         /// <summary>
159         /// Clears the collection.
160         /// </summary>
Clear()161         public void Clear()
162         {
163             ErrorUtilities.ThrowInvalidOperation("OM_NotSupportedReadOnlyCollection");
164         }
165 
166         /// <summary>
167         /// Returns true ff the collection contains the specified item.
168         /// </summary>
Contains(KeyValuePair<K, N> item)169         public bool Contains(KeyValuePair<K, N> item)
170         {
171             ErrorUtilities.ThrowInvalidOperation("OM_NotSupportedConvertingCollectionValueToBacking");
172             return false;
173         }
174 
175         /// <summary>
176         /// Copies all of the elements of the collection to the specified array.
177         /// </summary>
CopyTo(KeyValuePair<K, N>[] array, int arrayIndex)178         public void CopyTo(KeyValuePair<K, N>[] array, int arrayIndex)
179         {
180             ErrorUtilities.VerifyThrow(array.Length - arrayIndex >= _backing.Count, "Specified array size insufficient to hold the contents of the collection.");
181 
182             foreach (KeyValuePair<K, V> pair in _backing)
183             {
184                 array[arrayIndex++] = KeyValueConverter(pair);
185             }
186         }
187 
188         /// <summary>
189         /// Remove an item from the dictionary.
190         /// </summary>
Remove(KeyValuePair<K, N> item)191         public bool Remove(KeyValuePair<K, N> item)
192         {
193             ErrorUtilities.ThrowInvalidOperation("OM_NotSupportedReadOnlyCollection");
194             return false;
195         }
196 
197         #endregion
198 
199         #region IEnumerable<KeyValuePair<K, N>> Members
200 
201         /// <summary>
202         /// Implementation of generic IEnumerable.GetEnumerator()
203         /// </summary>
GetEnumerator()204         public IEnumerator<KeyValuePair<K, N>> GetEnumerator()
205         {
206             return new ConvertingEnumerable<KeyValuePair<K, V>, KeyValuePair<K, N>>(_backing, KeyValueConverter).GetEnumerator();
207         }
208 
209         #endregion
210 
211         #region IEnumerable Members
212 
213         /// <summary>
214         /// Implementation of IEnumerable.GetEnumerator()
215         /// </summary>
System.Collections.IEnumerable.GetEnumerator()216         System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
217         {
218             return ((IEnumerable<KeyValuePair<K, N>>)this).GetEnumerator();
219         }
220 
221         #endregion
222 
223         /// <summary>
224         /// Delegate used by ConvertingEnumerable
225         /// </summary>
KeyValueConverter(KeyValuePair<K, V> original)226         private KeyValuePair<K, N> KeyValueConverter(KeyValuePair<K, V> original)
227         {
228             return new KeyValuePair<K, N>(original.Key, _converter(original.Value));
229         }
230     }
231 }
232