Files
PowerShell/src/System.Management.Automation/minishell/api/RunspaceConfigurationEntryCollection.cs
T

435 lines
12 KiB
C#

/********************************************************************++
Copyright (c) Microsoft Corporation. All rights reserved.
--********************************************************************/
#pragma warning disable 1634, 1691
#pragma warning disable 56517
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Dbg = System.Management.Automation.Diagnostics;
namespace System.Management.Automation.Runspaces
{
internal delegate void RunspaceConfigurationEntryUpdateEventHandler();
/// <summary>
/// Define class for runspace configuration entry collection.
/// </summary>
/// <!--
/// Runspace configuration entry collection is used for handling following
/// problems for runspace configuration entries.
///
/// 1. synchronization. Since multiple runspaces may be sharing the same
/// runspace configuration, it is essential all the entry collections
/// (cmdlets, providers, assemblies, types, formats) are thread-safe.
/// 2. prepending/appending. Data for types and formats are order
/// sensitive. It is required for supporting prepending/appending to
/// the list and ability to remove the prepended/appended items.
/// 3. update. Update to the data needs to be communicated to other monad
/// components. For example, if cmdlets/providers list is updated, it
/// has to be communicated to engine.
/// -->
#if CORECLR
internal
#else
public
#endif
sealed class RunspaceConfigurationEntryCollection<T> : IEnumerable<T> where T : RunspaceConfigurationEntry
{
/// <summary>
/// Constructor
/// </summary>
public RunspaceConfigurationEntryCollection()
{
}
/// <summary>
/// Constructor
/// </summary>
/// <param name="items"></param>
public RunspaceConfigurationEntryCollection(IEnumerable<T> items)
{
if (items == null)
{
throw PSTraceSource.NewArgumentNullException("item");
}
AddBuiltInItem(items);
}
private Collection<T> _data = new Collection<T>();
private int _builtInEnd = 0; // this is the index of first after item.
private Collection<T> _updateList = new Collection<T>();
internal Collection<T> UpdateList
{
get
{
return _updateList;
}
}
/// <summary>
/// Get items at a index position.
/// </summary>
/// <param name="index"></param>
/// <returns></returns>
public T this[int index]
{
get
{
lock (_syncObject)
{
return _data[index];
}
}
}
/// <summary>
/// Get number of items in the collection.
/// </summary>
public int Count
{
get
{
lock (_syncObject)
{
return _data.Count;
}
}
}
/// <summary>
/// Reset items in the collection
/// </summary>
public void Reset()
{
lock (_syncObject)
{
for (int i = _data.Count - 1; i >= 0; i--)
{
if (!_data[i].BuiltIn)
{
RecordRemove(_data[i]);
_data.RemoveAt(i);
}
}
_builtInEnd = _data.Count;
}
}
/// <summary>
/// Remove one item from the collection
/// </summary>
/// <param name="index"></param>
/// <exception cref="PSArgumentOutOfRangeException">when <paramref name="index"/> is out of range.</exception>
public void RemoveItem(int index)
{
lock (_syncObject)
{
if (index < 0 || index >= _data.Count)
{
throw PSTraceSource.NewArgumentOutOfRangeException("index", index);
}
RecordRemove(_data[index]);
_data.RemoveAt(index);
if (index < _builtInEnd)
_builtInEnd--;
return;
}
}
/// <summary>
/// Remove multiple items in the collection.
/// </summary>
/// <param name="index"></param>
/// <param name="count"></param>
/// <exception cref="PSArgumentOutOfRangeException">when <paramref name="index"/> is out of range.</exception>
public void RemoveItem(int index, int count)
{
lock (_syncObject)
{
if (index < 0 || index + count > _data.Count)
{
throw PSTraceSource.NewArgumentOutOfRangeException("index", index);
}
for (int i = index + count - 1; i >= index; i--)
{
RecordRemove(_data[i]);
_data.RemoveAt(i);
}
int _numBeforeBuiltInEnd = Math.Min(count, _builtInEnd - index);
if (_numBeforeBuiltInEnd > 0)
_builtInEnd -= _numBeforeBuiltInEnd;
return;
}
}
/// <summary>
/// Remove one item from the collection
/// </summary>
/// <param name="item"></param>
internal void Remove(T item)
{
lock (_syncObject)
{
int index = _data.IndexOf(item);
if (index < 0 || index >= _data.Count)
{
Dbg.Assert(false, "Index from which to remove the item is out of range.");
throw PSTraceSource.NewArgumentOutOfRangeException("index", index);
}
RecordRemove(item);
_data.Remove(item);
if (index < _builtInEnd)
_builtInEnd--;
return;
}
}
/// <summary>
/// Prepend an item to the collection.
/// </summary>
/// <param name="item"></param>
public void Prepend(T item)
{
lock (_syncObject)
{
RecordAdd(item);
item._builtIn = false;
_data.Insert(0, item);
_builtInEnd++;
}
}
/// <summary>
/// Prepend items into the collection
/// </summary>
/// <param name="items"></param>
public void Prepend(IEnumerable<T> items)
{
if (items == null)
{
throw new ArgumentNullException("items");
}
lock (_syncObject)
{
int i = 0;
foreach (T t in items)
{
RecordAdd(t);
t._builtIn = false;
_data.Insert(i++, t);
_builtInEnd++;
}
}
}
/// <summary>
/// Append one item to the collection
/// </summary>
/// <param name="item"></param>
public void Append(T item)
{
lock (_syncObject)
{
RecordAdd(item);
item._builtIn = false;
_data.Add(item);
}
}
/// <summary>
/// Append items to the collection.
/// </summary>
/// <param name="items"></param>
public void Append(IEnumerable<T> items)
{
if (items == null)
{
throw new ArgumentNullException("items");
}
lock (_syncObject)
{
foreach (T t in items)
{
RecordAdd(t);
t._builtIn = false;
_data.Add(t);
}
}
}
internal void AddBuiltInItem(T item)
{
lock (_syncObject)
{
item._builtIn = true;
RecordAdd(item);
_data.Insert(_builtInEnd, item);
_builtInEnd++;
}
}
internal void AddBuiltInItem(IEnumerable<T> items)
{
lock (_syncObject)
{
foreach (T t in items)
{
t._builtIn = true;
RecordAdd(t);
_data.Insert(_builtInEnd, t);
_builtInEnd++;
}
}
}
internal void RemovePSSnapIn(string PSSnapinName)
{
lock (_syncObject)
{
for (int i = _data.Count - 1; i >= 0; i--)
{
if (_data[i].PSSnapIn != null)
{
if (_data[i].PSSnapIn.Name.Equals(PSSnapinName, StringComparison.Ordinal))
{
RecordRemove(_data[i]);
_data.RemoveAt(i);
if (i < _builtInEnd)
_builtInEnd--;
}
}
}
}
}
/// <summary>
/// Get enumerator for this collection.
/// </summary>
/// <returns></returns>
/// <!--
/// Enumerator work is not thread safe by default. Any code trying
/// to do enumeration on this collection should lock it first.
///
/// Need to document this.
/// -->
IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
return _data.GetEnumerator();
}
/// <summary>
/// Get enumerator for this collection.
/// </summary>
/// <returns></returns>
/// <!--
/// Enumerator work is not thread safe by default. Any code trying
/// to do enumeration on this collection should lock it first.
///
/// Need to document this.
/// -->
IEnumerator<T> System.Collections.Generic.IEnumerable<T>.GetEnumerator()
{
return _data.GetEnumerator();
}
/// <summary>
/// Update others about the collection change.
/// </summary>
public void Update()
{
Update(false);
}
internal void Update(bool force)
{
lock (_syncObject)
{
if (OnUpdate != null && (force || _updateList.Count > 0))
{
OnUpdate();
// Here we need to clear the Action for each item
// since _updateList is sharing data with _data.
foreach (T t in _updateList)
{
t._action = UpdateAction.None;
}
_updateList.Clear();
}
}
}
private void RecordRemove(T t)
{
// if this item was added recently, simply remove the add action.
if (t.Action == UpdateAction.Add)
{
t._action = UpdateAction.None;
_updateList.Remove(t);
}
else
{
t._action = UpdateAction.Remove;
_updateList.Add(t);
}
}
private void RecordAdd(T t)
{
// if this item was removed recently, simply remove the add action.
if (t.Action == UpdateAction.Remove)
{
t._action = UpdateAction.None;
_updateList.Remove(t);
}
else
{
t._action = UpdateAction.Add;
_updateList.Add(t);
}
}
//object to use for locking
private object _syncObject = new object();
/// <summary>
/// OnUpdate handler should lock the object itself.
/// </summary>
internal event RunspaceConfigurationEntryUpdateEventHandler OnUpdate;
}
}
#pragma warning restore 56517