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 wrapper around an ICollection&lt;K&gt;</summary>
6 //-----------------------------------------------------------------------
7 
8 using System;
9 using System.Collections.Generic;
10 using System.Linq;
11 using System.Text;
12 using System.Collections;
13 using Microsoft.Build.Shared;
14 
15 namespace Microsoft.Build.Collections
16 {
17     /// <summary>
18     /// A read-only live wrapper over a collection.
19     /// It does not prevent modification of the values themselves.
20     /// </summary>
21     /// <remarks>
22     /// There is a type with the same name in the BCL, but it is actually a ReadOnlyList and does not accept an ICollection&gt;T&lt;.
23     /// Thus this is an omission from the BCL.
24     /// </remarks>
25     /// <typeparam name="T">Type of element in the collection</typeparam>
26     internal class ReadOnlyCollection<T> : ICollection<T>, ICollection
27     {
28         /// <summary>
29         /// Backing live enumerable.
30         /// May be a collection.
31         /// </summary>
32         private IEnumerable<T> _backing;
33 
34         /// <summary>
35         /// Construct a read only wrapper around the current contents
36         /// of the IEnumerable, or around the backing collection if the
37         /// IEnumerable is in fact a collection.
38         /// </summary>
ReadOnlyCollection(IEnumerable<T> backing)39         internal ReadOnlyCollection(IEnumerable<T> backing)
40         {
41             ErrorUtilities.VerifyThrow(backing != null, "Need backing collection");
42 
43             _backing = backing;
44         }
45 
46         /// <summary>
47         /// Return the number of items in the backing collection
48         /// </summary>
49         public int Count
50         {
51             get
52             {
53                 return BackingCollection.Count;
54             }
55         }
56 
57         /// <summary>
58         /// Returns true.
59         /// </summary>
60         public bool IsReadOnly
61         {
62             get { return true; }
63         }
64 
65         /// <summary>
66         /// Whether collection is synchronized
67         /// </summary>
68         bool ICollection.IsSynchronized
69         {
70             get { return false; }
71         }
72 
73         /// <summary>
74         /// Sync root
75         /// </summary>
76         object ICollection.SyncRoot
77         {
78             get { return this; }
79         }
80 
81         /// <summary>
82         /// Get a backing ICollection.
83         /// </summary>
84         private ICollection<T> BackingCollection
85         {
86             get
87             {
88                 ICollection<T> backingCollection = _backing as ICollection<T>;
89                 if (backingCollection == null)
90                 {
91                     backingCollection = new List<T>(_backing);
92                     _backing = backingCollection;
93                 }
94 
95                 return backingCollection;
96             }
97         }
98 
99         /// <summary>
100         /// Prohibited on read only collection: throws
101         /// </summary>
Add(T item)102         public void Add(T item)
103         {
104             ErrorUtilities.ThrowInvalidOperation("OM_NotSupportedReadOnlyCollection");
105         }
106 
107         /// <summary>
108         /// Prohibited on read only collection: throws
109         /// </summary>
Clear()110         public void Clear()
111         {
112             ErrorUtilities.ThrowInvalidOperation("OM_NotSupportedReadOnlyCollection");
113         }
114 
115         /// <summary>
116         /// Pass through for underlying collection
117         /// </summary>
Contains(T item)118         public bool Contains(T item)
119         {
120             // UNDONE: IEnumerable.Contains<T>() does the ICollection check,
121             // so we could just use IEnumerable.Contains<T>() here.
122             if (!(_backing is ICollection<T>))
123             {
124                 return _backing.Contains<T>(item);
125             }
126 
127             return BackingCollection.Contains(item);
128         }
129 
130         /// <summary>
131         /// Pass through for underlying collection
132         /// </summary>
CopyTo(T[] array, int arrayIndex)133         public void CopyTo(T[] array, int arrayIndex)
134         {
135             ErrorUtilities.VerifyThrowArgumentNull(array, "array");
136 
137             ICollection<T> backingCollection = _backing as ICollection<T>;
138             if (backingCollection != null)
139             {
140                 backingCollection.CopyTo(array, arrayIndex);
141             }
142             else
143             {
144                 int i = arrayIndex;
145                 foreach (T entry in _backing)
146                 {
147                     array[i] = entry;
148                     i++;
149                 }
150             }
151         }
152 
153         /// <summary>
154         /// Prohibited on read only collection: throws
155         /// </summary>
Remove(T item)156         public bool Remove(T item)
157         {
158             ErrorUtilities.ThrowInvalidOperation("OM_NotSupportedReadOnlyCollection");
159             return false;
160         }
161 
162         /// <summary>
163         /// Pass through for underlying collection
164         /// </summary>
165         /// <comment>
166         /// NOTE: This does NOT cause a copy into a List, since the
167         /// backing enumerable suffices.
168         /// </comment>
GetEnumerator()169         public IEnumerator<T> GetEnumerator()
170         {
171             return _backing.GetEnumerator();
172         }
173 
174         /// <summary>
175         /// Pass through for underlying collection
176         /// </summary>
177         /// <comment>
178         /// NOTE: This does NOT cause a copy into a List, since the
179         /// backing enumerable suffices.
180         /// </comment>
IEnumerable.GetEnumerator()181         IEnumerator IEnumerable.GetEnumerator()
182         {
183             return ((IEnumerable)_backing).GetEnumerator();
184         }
185 
186         /// <summary>
187         /// ICollection version of CopyTo
188         /// </summary>
ICollection.CopyTo(Array array, int index)189         void ICollection.CopyTo(Array array, int index)
190         {
191             ErrorUtilities.VerifyThrowArgumentNull(array, "array");
192 
193             int i = index;
194             foreach (T entry in _backing)
195             {
196                 array.SetValue(entry, i);
197                 i++;
198             }
199         }
200     }
201 }
202