|
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
using System;
using System.Collections;
using System.Collections.Generic;
using System.Diagnostics;
namespace System.DirectoryServices.AccountManagement
{
public class PrincipalValueCollection<T> : IList<T>, IList
// T must be a ValueType or immutable ref type (such as string)
{
//
// IList
//
bool IList.IsFixedSize
{
get
{
return IsFixedSize;
}
}
bool IList.IsReadOnly
{
get
{
return IsReadOnly;
}
}
int IList.Add(object value)
{
if (value == null)
throw new ArgumentNullException(nameof(value));
_inner.Add((T)value);
return Count;
}
void IList.Clear()
{
Clear();
}
bool IList.Contains(object value)
{
if (value == null)
throw new ArgumentNullException(nameof(value));
return _inner.Contains((T)value);
}
int IList.IndexOf(object value)
{
if (value == null)
throw new ArgumentNullException(nameof(value));
return IndexOf((T)value);
}
void IList.Insert(int index, object value)
{
if (value == null)
throw new ArgumentNullException(nameof(value));
Insert(index, (T)value);
}
void IList.Remove(object value)
{
if (value == null)
throw new ArgumentNullException(nameof(value));
_inner.Remove((T)value);
}
void IList.RemoveAt(int index)
{
RemoveAt(index);
}
object IList.this[int index]
{
get
{
return this[index];
}
set
{
if (value == null)
throw new ArgumentNullException(nameof(value));
this[index] = (T)value;
}
}
//
// ICollection
//
void ICollection.CopyTo(Array array, int index)
{
((ICollection)_inner).CopyTo(array, index);
}
int ICollection.Count
{
get
{
return _inner.Count;
}
}
bool ICollection.IsSynchronized
{
get
{
return IsSynchronized;
}
}
object ICollection.SyncRoot
{
get
{
return SyncRoot;
}
}
public bool IsSynchronized
{
get
{
return ((ICollection)_inner).IsSynchronized;
}
}
public object SyncRoot
{
get
{
return this;
}
}
//
// IEnumerable
//
IEnumerator IEnumerable.GetEnumerator()
{
return (IEnumerator)GetEnumerator();
}
//
// IList<T>
//
public bool IsFixedSize
{
get
{
return false;
}
}
public bool IsReadOnly
{
get
{
return false;
}
}
// Adds obj to the end of the list by inserting it into insertedValues.
public void Add(T value)
{
if (value == null)
throw new ArgumentNullException(nameof(value));
_inner.Add(value);
}
public void Clear()
{
_inner.Clear();
}
public bool Contains(T value)
{
if (value == null)
throw new ArgumentNullException(nameof(value));
return _inner.Contains(value);
}
public int IndexOf(T value)
{
if (value == null)
throw new ArgumentNullException(nameof(value));
int index = 0;
foreach (TrackedCollection<T>.ValueEl el in _inner.combinedValues)
{
if (el.isInserted && el.insertedValue.Equals(value))
{
GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalValueCollection", "IndexOf: found {0} on inserted at {1}", value, index);
return index;
}
if (!el.isInserted && el.originalValue.Right.Equals(value))
{
GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalValueCollection", "IndexOf: found {0} on original at {1}", value, index);
return index;
}
index++;
}
return -1;
}
public void Insert(int index, T value)
{
_inner.MarkChange();
if (value == null)
throw new ArgumentNullException(nameof(value));
if ((index < 0) || (index > _inner.combinedValues.Count))
{
GlobalDebug.WriteLineIf(GlobalDebug.Warn, "PrincipalValueCollection", "Insert({0}): out of range (count={1})", index, _inner.combinedValues.Count);
throw new ArgumentOutOfRangeException(nameof(index));
}
TrackedCollection<T>.ValueEl el = new TrackedCollection<T>.ValueEl();
el.isInserted = true;
el.insertedValue = value;
_inner.combinedValues.Insert(index, el);
}
public bool Remove(T value)
{
if (value == null)
throw new ArgumentNullException(nameof(value));
return _inner.Remove(value);
}
public void RemoveAt(int index)
{
_inner.MarkChange();
if ((index < 0) || (index >= _inner.combinedValues.Count))
{
GlobalDebug.WriteLineIf(GlobalDebug.Warn, "PrincipalValueCollection", "RemoveAt({0}): out of range (count={1})", index, _inner.combinedValues.Count);
throw new ArgumentOutOfRangeException(nameof(index));
}
TrackedCollection<T>.ValueEl el = _inner.combinedValues[index];
if (el.isInserted)
{
// We're removing an inserted value.
GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalValueCollection", "RemoveAt({0}): removing inserted", index);
_inner.combinedValues.RemoveAt(index);
}
else
{
// We're removing an original value.
GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalValueCollection", "RemoveAt({0}): removing original", index);
Pair<T, T> pair = _inner.combinedValues[index].originalValue;
_inner.combinedValues.RemoveAt(index);
_inner.removedValues.Add(pair.Left);
}
}
public T this[int index]
{
get
{
if ((index < 0) || (index >= _inner.combinedValues.Count))
{
GlobalDebug.WriteLineIf(GlobalDebug.Warn, "PrincipalValueCollection", "this[{0}].get: out of range (count={1})", index, _inner.combinedValues.Count);
throw new ArgumentOutOfRangeException(nameof(index));
}
TrackedCollection<T>.ValueEl el = _inner.combinedValues[index];
if (el.isInserted)
{
GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalValueCollection", "this[{0}].get: is inserted {1}", index, el.insertedValue);
return el.insertedValue;
}
else
{
GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalValueCollection", "this[{0}].get: is original {1}", index, el.originalValue.Right);
return el.originalValue.Right; // Right == current value
}
}
set
{
_inner.MarkChange();
if ((index < 0) || (index >= _inner.combinedValues.Count))
{
GlobalDebug.WriteLineIf(GlobalDebug.Warn, "PrincipalValueCollection", "this[{0}].set: out of range (count={1})", index, _inner.combinedValues.Count);
throw new ArgumentOutOfRangeException(nameof(index));
}
if (value == null)
throw new ArgumentNullException(nameof(value));
TrackedCollection<T>.ValueEl el = _inner.combinedValues[index];
if (el.isInserted)
{
GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalValueCollection", "this[{0}].set: is inserted {1}", index, value);
el.insertedValue = value;
}
else
{
GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalValueCollection", "this[{0}].set: is original {1}", index, value);
el.originalValue.Right = value;
}
}
}
//
// ICollection<T>
//
public void CopyTo(T[] array, int index)
{
((ICollection)this).CopyTo((Array)array, index);
}
public int Count
{
get
{
return _inner.Count;
}
}
//
// IEnumerable<T>
//
public IEnumerator<T> GetEnumerator()
{
return new ValueCollectionEnumerator<T>(_inner, _inner.combinedValues);
}
//
// Private implementation
//
private readonly TrackedCollection<T> _inner = new TrackedCollection<T>();
//
// Internal constructor
//
internal PrincipalValueCollection()
{
GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalValueCollection", "Ctor");
// Nothing to do here
}
//
// Load/Store implementation
//
internal void Load(List<T> values)
{
GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalValueCollection", "Load, count={0}", values.Count);
// To support reload
_inner.combinedValues.Clear();
_inner.removedValues.Clear();
foreach (T value in values)
{
// If T was a mutable reference type, would need to make a copy of value
// for the left-side of the Pair, so that changes to the value in the
// right-side wouldn't also change the left-side value (due to aliasing).
// However, we constrain T to be either a value type or a immutable ref type (e.g., string)
// to avoid this problem.
TrackedCollection<T>.ValueEl el = new TrackedCollection<T>.ValueEl();
el.isInserted = false;
el.originalValue = new Pair<T, T>(value, value);
_inner.combinedValues.Add(el);
}
}
internal List<T> Inserted
{
get
{
return _inner.Inserted;
}
}
internal List<T> Removed
{
get
{
return _inner.Removed;
}
}
internal List<Pair<T, T>> ChangedValues
{
get
{
return _inner.ChangedValues;
}
}
internal bool Changed
{
get
{
return _inner.Changed;
}
}
// Resets the change-tracking of the collection to 'unchanged', by clearing out the removedValues,
// changing inserted values into original values, and for all Pairs<T,T> in originalValues for which
// the left-side does not equal the right-side, copying the right-side over the left-side
internal void ResetTracking()
{
GlobalDebug.WriteLineIf(GlobalDebug.Info, "PrincipalValueCollection", "Entering ResetTracking");
_inner.removedValues.Clear();
foreach (TrackedCollection<T>.ValueEl el in _inner.combinedValues)
{
if (el.isInserted)
{
GlobalDebug.WriteLineIf(
GlobalDebug.Info,
"PrincipalValueCollection",
"ResetTracking: moving {0} (type {1}) from inserted to original",
el.insertedValue,
el.insertedValue.GetType());
el.isInserted = false;
el.originalValue = new Pair<T, T>(el.insertedValue, el.insertedValue);
//el.insertedValue = T.default;
}
else
{
Pair<T, T> pair = el.originalValue;
if (!pair.Left.Equals(pair.Right))
{
GlobalDebug.WriteLineIf(
GlobalDebug.Info,
"PrincipalValueCollection",
"ResetTracking: found changed original, left={0}, right={1}, type={2}",
pair.Left,
pair.Right,
pair.Left.GetType());
pair.Left = pair.Right;
}
}
}
}
}
}
|