1 /* ****************************************************************************
3 * Copyright (c) Microsoft Corporation.
5 * This source code is subject to terms and conditions of the Apache License, Version 2.0. A
6 * copy of the license can be found in the License.html file at the root of this distribution. If
7 * you cannot locate the Apache License, Version 2.0, please send an email to
8 * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound
9 * by the terms of the Apache License, Version 2.0.
11 * You must not remove this notice, or any other, from this software.
14 * ***************************************************************************/
17 using Microsoft.Scripting.Ast;
19 using System.Linq.Expressions;
25 using System.Collections.Generic;
26 using System.Collections.ObjectModel;
27 using System.Dynamic.Utils;
29 namespace System.Runtime.CompilerServices {
31 /// The builder for read only collection.
33 /// <typeparam name="T">The type of the collection element.</typeparam>
35 [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")]
36 public sealed class ReadOnlyCollectionBuilder<T> : IList<T>, System.Collections.IList {
37 private const int DefaultCapacity = 4;
44 private Object _syncRoot;
46 static readonly T[] _emptyArray = new T[0];
49 /// Constructs a ReadOnlyCollectionBuilder.
51 public ReadOnlyCollectionBuilder() {
56 /// Constructs a ReadOnlyCollectionBuilder with a given initial capacity.
57 /// The contents are empty but builder will have reserved room for the given
58 /// number of elements before any reallocations are required.
60 public ReadOnlyCollectionBuilder(int capacity) {
61 ContractUtils.Requires(capacity >= 0, "capacity");
62 _items = new T[capacity];
66 /// Constructs a ReadOnlyCollectionBuilder, copying contents of the given collection.
68 /// <param name="collection"></param>
69 public ReadOnlyCollectionBuilder(IEnumerable<T> collection) {
70 ContractUtils.Requires(collection != null, "collection");
72 ICollection<T> c = collection as ICollection<T>;
75 _items = new T[count];
80 _items = new T[DefaultCapacity];
82 using (IEnumerator<T> en = collection.GetEnumerator()) {
83 while (en.MoveNext()) {
91 /// Gets and sets the capacity of this ReadOnlyCollectionBuilder
94 get { return _items.Length; }
96 ContractUtils.Requires(value >= _size, "value");
98 if (value != _items.Length) {
100 T[] newItems = new T[value];
102 Array.Copy(_items, 0, newItems, 0, _size);
106 _items = _emptyArray;
113 /// Returns number of elements in the ReadOnlyCollectionBuilder.
116 get { return _size; }
119 #region IList<T> Members
122 /// Returns the index of the first occurrence of a given value in the builder.
124 /// <param name="item">An item to search for.</param>
125 /// <returns>The index of the first occurrence of an item.</returns>
126 public int IndexOf(T item) {
127 return Array.IndexOf(_items, item, 0, _size);
131 /// Inserts an item to the <see cref="ReadOnlyCollectionBuilder{T}"/> at the specified index.
133 /// <param name="index">The zero-based index at which item should be inserted.</param>
134 /// <param name="item">The object to insert into the <see cref="ReadOnlyCollectionBuilder{T}"/>.</param>
135 public void Insert(int index, T item) {
136 ContractUtils.Requires(index <= _size, "index");
138 if (_size == _items.Length) {
139 EnsureCapacity(_size + 1);
142 Array.Copy(_items, index, _items, index + 1, _size - index);
144 _items[index] = item;
150 /// Removes the <see cref="ReadOnlyCollectionBuilder{T}"/> item at the specified index.
152 /// <param name="index">The zero-based index of the item to remove.</param>
153 public void RemoveAt(int index) {
154 ContractUtils.Requires(index >= 0 && index < _size, "index");
158 Array.Copy(_items, index + 1, _items, index, _size - index);
160 _items[_size] = default(T);
165 /// Gets or sets the element at the specified index.
167 /// <param name="index">The zero-based index of the element to get or set.</param>
168 /// <returns>The element at the specified index.</returns>
169 public T this[int index] {
171 ContractUtils.Requires(index < _size, "index");
172 return _items[index];
175 ContractUtils.Requires(index < _size, "index");
176 _items[index] = value;
183 #region ICollection<T> Members
186 /// Adds an item to the <see cref="ReadOnlyCollectionBuilder{T}"/>.
188 /// <param name="item">The object to add to the <see cref="ReadOnlyCollectionBuilder{T}"/>.</param>
189 public void Add(T item) {
190 if (_size == _items.Length) {
191 EnsureCapacity(_size + 1);
193 _items[_size++] = item;
198 /// Removes all items from the <see cref="ReadOnlyCollectionBuilder{T}"/>.
200 public void Clear() {
202 Array.Clear(_items, 0, _size);
209 /// Determines whether the <see cref="ReadOnlyCollectionBuilder{T}"/> contains a specific value
211 /// <param name="item">the object to locate in the <see cref="ReadOnlyCollectionBuilder{T}"/>.</param>
212 /// <returns>true if item is found in the <see cref="ReadOnlyCollectionBuilder{T}"/>; otherwise, false.</returns>
213 public bool Contains(T item) {
214 if ((Object)item == null) {
215 for (int i = 0; i < _size; i++) {
216 if ((Object)_items[i] == null) {
222 EqualityComparer<T> c = EqualityComparer<T>.Default;
223 for (int i = 0; i < _size; i++) {
224 if (c.Equals(_items[i], item)) {
233 /// Copies the elements of the <see cref="ReadOnlyCollectionBuilder{T}"/> to an <see cref="Array"/>,
234 /// starting at particular <see cref="Array"/> index.
236 /// <param name="array">The one-dimensional <see cref="Array"/> that is the destination of the elements copied from <see cref="ReadOnlyCollectionBuilder{T}"/>.</param>
237 /// <param name="arrayIndex">The zero-based index in array at which copying begins.</param>
238 public void CopyTo(T[] array, int arrayIndex) {
239 Array.Copy(_items, 0, array, arrayIndex, _size);
242 bool ICollection<T>.IsReadOnly {
243 get { return false; }
247 /// Removes the first occurrence of a specific object from the <see cref="ReadOnlyCollectionBuilder{T}"/>.
249 /// <param name="item">The object to remove from the <see cref="ReadOnlyCollectionBuilder{T}"/>.</param>
250 /// <returns>true if item was successfully removed from the <see cref="ReadOnlyCollectionBuilder{T}"/>;
251 /// otherwise, false. This method also returns false if item is not found in the original <see cref="ReadOnlyCollectionBuilder{T}"/>.
253 public bool Remove(T item) {
254 int index = IndexOf(item);
265 #region IEnumerable<T> Members
268 /// Returns an enumerator that iterates through the collection.
270 /// <returns>A <see cref="IEnumerator{T}"/> that can be used to iterate through the collection.</returns>
271 public IEnumerator<T> GetEnumerator() {
272 return new Enumerator(this);
277 #region IEnumerable Members
279 System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() {
280 return GetEnumerator();
285 #region IList Members
287 bool System.Collections.IList.IsReadOnly {
288 get { return false; }
291 int System.Collections.IList.Add(object value) {
292 ValidateNullValue(value, "value");
295 } catch (InvalidCastException) {
296 ThrowInvalidTypeException(value, "value");
301 bool System.Collections.IList.Contains(object value) {
302 if (IsCompatibleObject(value)) {
303 return Contains((T)value);
307 int System.Collections.IList.IndexOf(object value) {
308 if (IsCompatibleObject(value)) {
309 return IndexOf((T)value);
314 void System.Collections.IList.Insert(int index, object value) {
315 ValidateNullValue(value, "value");
317 Insert(index, (T)value);
318 } catch (InvalidCastException) {
319 ThrowInvalidTypeException(value, "value");
323 bool System.Collections.IList.IsFixedSize {
324 get { return false; }
327 void System.Collections.IList.Remove(object value) {
328 if (IsCompatibleObject(value)) {
333 object System.Collections.IList.this[int index] {
338 ValidateNullValue(value, "value");
341 this[index] = (T)value;
342 } catch (InvalidCastException) {
343 ThrowInvalidTypeException(value, "value");
350 #region ICollection Members
352 void System.Collections.ICollection.CopyTo(Array array, int index) {
353 ContractUtils.RequiresNotNull(array, "array");
354 ContractUtils.Requires(array.Rank == 1, "array");
355 Array.Copy(_items, 0, array, index, _size);
358 bool System.Collections.ICollection.IsSynchronized {
359 get { return false; }
362 object System.Collections.ICollection.SyncRoot {
364 if (_syncRoot == null) {
365 System.Threading.Interlocked.CompareExchange<Object>(ref _syncRoot, new Object(), null);
374 /// Reverses the order of the elements in the entire <see cref="ReadOnlyCollectionBuilder{T}"/>.
376 public void Reverse() {
381 /// Reverses the order of the elements in the specified range.
383 /// <param name="index">The zero-based starting index of the range to reverse.</param>
384 /// <param name="count">The number of elements in the range to reverse.</param>
385 public void Reverse(int index, int count) {
386 ContractUtils.Requires(index >= 0, "index");
387 ContractUtils.Requires(count >= 0, "count");
389 Array.Reverse(_items, index, count);
394 /// Copies the elements of the <see cref="ReadOnlyCollectionBuilder{T}"/> to a new array.
396 /// <returns>An array containing copies of the elements of the <see cref="ReadOnlyCollectionBuilder{T}"/>.</returns>
397 public T[] ToArray() {
398 T[] array = new T[_size];
399 Array.Copy(_items, 0, array, 0, _size);
404 /// Creates a <see cref="ReadOnlyCollection{T}"/> containing all of the the elements of the <see cref="ReadOnlyCollectionBuilder{T}"/>,
405 /// avoiding copying the elements to the new array if possible. Resets the <see cref="ReadOnlyCollectionBuilder{T}"/> after the
406 /// <see cref="ReadOnlyCollection{T}"/> has been created.
408 /// <returns>A new instance of <see cref="ReadOnlyCollection{T}"/>.</returns>
409 public ReadOnlyCollection<T> ToReadOnlyCollection() {
410 // Can we use the stored array?
412 if (_size == _items.Length) {
417 _items = _emptyArray;
421 return new TrueReadOnlyCollection<T>(items);
424 private void EnsureCapacity(int min) {
425 if (_items.Length < min) {
426 int newCapacity = DefaultCapacity;
427 if (_items.Length > 0) {
428 newCapacity = _items.Length * 2;
430 if (newCapacity < min) {
433 Capacity = newCapacity;
437 private static bool IsCompatibleObject(object value) {
438 return ((value is T) || (value == null && default(T) == null));
441 private static void ValidateNullValue(object value, string argument) {
442 if (value == null && !(default(T) == null)) {
443 throw new ArgumentException(Strings.InvalidNullValue(typeof(T)), argument);
447 private static void ThrowInvalidTypeException(object value, string argument) {
448 throw new ArgumentException(Strings.InvalidObjectType(value != null ? value.GetType() : (object)"null", typeof(T)), argument);
452 private class Enumerator : IEnumerator<T>, System.Collections.IEnumerator {
453 private readonly ReadOnlyCollectionBuilder<T> _builder;
454 private readonly int _version;
459 internal Enumerator(ReadOnlyCollectionBuilder<T> builder) {
461 _version = builder._version;
463 _current = default(T);
466 #region IEnumerator<T> Members
469 get { return _current; }
474 #region IDisposable Members
476 public void Dispose() {
477 GC.SuppressFinalize(this);
482 #region IEnumerator Members
484 object System.Collections.IEnumerator.Current {
486 if (_index == 0 || _index > _builder._size) {
487 throw Error.EnumerationIsDone();
493 public bool MoveNext() {
494 if (_version == _builder._version) {
495 if (_index < _builder._size) {
496 _current = _builder._items[_index++];
499 _index = _builder._size + 1;
500 _current = default(T);
504 throw Error.CollectionModifiedWhileEnumerating();
510 #region IEnumerator Members
512 void System.Collections.IEnumerator.Reset() {
513 if (_version != _builder._version) {
514 throw Error.CollectionModifiedWhileEnumerating();
517 _current = default(T);