using System;
using System.Collections;
using System.Collections.Specialized;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using BLToolkit.Reflection;
using BLToolkit.Mapping;
using BLToolkit.ComponentModel;
namespace BLToolkit.EditableObjects
{
[DebuggerDisplay("Count = {Count}, ItemType = {ItemType}")]
[Serializable]
public class EditableArrayList : ArrayList, IEditable, ISortable, ISupportMapping,
IDisposable, IPrintDebugState, ITypedList, IBindingListView, ICancelAddNew, INotifyCollectionChanged
{
#region Constructors
public EditableArrayList(Type itemType, ArrayList list, bool trackChanges)
{
if (itemType == null) throw new ArgumentNullException("itemType");
if (list == null) throw new ArgumentNullException("list");
_itemType = itemType;
_list = list;
_noTrackingChangesCount++;
AddInternal(_list);
_noTrackingChangesCount--;
if (!trackChanges)
{
SetTrackingChanges(trackChanges);
_minTrackingChangesCount = 1;
}
}
public EditableArrayList(Type itemType)
: this(itemType, new ArrayList(), true)
{
}
public EditableArrayList(Type itemType, bool trackChanges)
: this(itemType, new ArrayList(), trackChanges)
{
}
public EditableArrayList(Type itemType, int capacity)
: this(itemType, new ArrayList(capacity), true)
{
}
public EditableArrayList(Type itemType, int capacity, bool trackChanges)
: this(itemType, new ArrayList(capacity), trackChanges)
{
}
public EditableArrayList(Type itemType, ICollection c)
: this(itemType, new ArrayList(c), true)
{
}
public EditableArrayList(Type itemType, ICollection c, bool trackChanges)
: this(itemType, new ArrayList(c), trackChanges)
{
}
public EditableArrayList(Type itemType, ArrayList list)
: this(itemType, list, true)
{
}
public EditableArrayList(EditableArrayList list)
: this(list.ItemType, new ArrayList(list), true)
{
}
public EditableArrayList(EditableArrayList list, bool trackChanges)
: this(list.ItemType, new ArrayList(list), trackChanges)
{
}
public EditableArrayList(Type itemType, EditableArrayList list)
: this(itemType, new ArrayList(list), true)
{
}
public EditableArrayList(Type itemType, EditableArrayList list, bool trackChanges)
: this(itemType, new ArrayList(list), trackChanges)
{
}
#endregion
#region Public Members
private readonly ArrayList _list;
internal ArrayList List
{
get { return _list; }
}
private readonly Type _itemType;
public Type ItemType
{
get { return _itemType; }
}
private ArrayList _newItems;
public ArrayList NewItems
{
get
{
if (_newItems == null)
_newItems = new ArrayList();
return _newItems;
}
}
private ArrayList _delItems;
public ArrayList DelItems
{
get
{
if (_delItems == null)
_delItems = new ArrayList();
return _delItems;
}
}
public void Sort(params string[] memberNames)
{
Sort(ListSortDirection.Ascending, memberNames);
}
public void Sort(ListSortDirection direction, params string[] memberNames)
{
if (memberNames == null) throw new ArgumentNullException ("memberNames");
if (memberNames.Length == 0) throw new ArgumentOutOfRangeException("memberNames");
Sort(new SortMemberComparer(TypeAccessor.GetAccessor(ItemType), direction, memberNames));
}
public void SortEx(string sortExpression)
{
string[] sorts = sortExpression.Split(',');
for (int i = 0; i < sorts.Length; i++)
sorts[i] = sorts[i].Trim();
string last = sorts[sorts.Length - 1];
bool desc = last.ToLower().EndsWith(" desc");
if (desc)
sorts[sorts.Length - 1] = last.Substring(0, last.Length - " desc".Length);
Sort(desc? ListSortDirection.Descending: ListSortDirection.Ascending, sorts);
}
public void Move(int newIndex, int oldIndex)
{
BindingListImpl.Move(newIndex, oldIndex);
}
public void Move(int newIndex, object item)
{
lock (SyncRoot)
{
int index = IndexOf(item);
if (index >= 0)
Move(newIndex, index);
}
}
#endregion
#region Change Notification
public bool NotifyChanges
{
get { return BindingListImpl.NotifyChanges; }
set { BindingListImpl.NotifyChanges = value; }
}
protected virtual void OnListChanged(ListChangedEventArgs e)
{
if (!_supressEvent && NotifyChanges && ListChanged != null)
ListChanged(this, e);
}
protected void OnListChanged(ListChangedType listChangedType, int index)
{
OnListChanged(new EditableListChangedEventArgs(listChangedType, index));
}
private void OnResetList()
{
OnListChanged(ListChangedType.Reset, -1);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
private void OnAddItem(object newObject, int index)
{
OnListChanged(ListChangedType.ItemAdded, index);
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, newObject, index));
}
#endregion
#region Add/Remove Internal
void AddInternal(object value)
{
if (IsTrackingChanges)
{
if (DelItems.Contains(value))
DelItems.Remove(value);
else
NewItems.Add(value);
}
OnAdd(value);
}
private void RemoveInternal(object value)
{
if (IsTrackingChanges)
{
if (NewItems.Contains(value))
NewItems.Remove(value);
else
DelItems.Add(value);
}
OnRemove(value);
}
private void AddInternal(IEnumerable e)
{
foreach (object o in e)
AddInternal(o);
}
private void RemoveInternal(IEnumerable e)
{
foreach (object o in e)
RemoveInternal(o);
}
protected virtual void OnAdd(object value)
{
}
protected virtual void OnRemove(object value)
{
}
#endregion
#region Track Changes
private int _noTrackingChangesCount;
private readonly int _minTrackingChangesCount = 0;
public bool IsTrackingChanges
{
get { return _noTrackingChangesCount == 0; }
}
protected void SetTrackingChanges(bool trackChanges)
{
if (trackChanges)
{
_noTrackingChangesCount--;
if (_noTrackingChangesCount < _minTrackingChangesCount)
{
_noTrackingChangesCount = _minTrackingChangesCount;
throw new InvalidOperationException("Tracking Changes Counter can not be negative.");
}
}
else
{
_noTrackingChangesCount++;
}
}
#endregion
#region ISupportMapping Members
public virtual void BeginMapping(InitContext initContext)
{
if (initContext.IsDestination)
_noTrackingChangesCount++;
}
public virtual void EndMapping(InitContext initContext)
{
if (initContext.IsDestination)
{
AcceptChanges();
_noTrackingChangesCount--;
}
}
#endregion
#region IEditable Members
public virtual void AcceptChanges()
{
foreach (object o in _list)
if (o is IEditable)
((IEditable)o).AcceptChanges();
_newItems = null;
_delItems = null;
}
public virtual void RejectChanges()
{
_noTrackingChangesCount++;
if (_delItems != null)
foreach (object o in _delItems)
Add(o);
if (_newItems != null)
foreach (object o in _newItems)
Remove(o);
foreach (object o in _list)
if (o is IEditable)
((IEditable)o).RejectChanges();
_noTrackingChangesCount--;
_newItems = null;
_delItems = null;
}
public virtual bool IsDirty
{
get
{
if (_newItems != null && _newItems.Count > 0 ||
_delItems != null && _delItems.Count > 0)
return true;
foreach (object o in _list)
if (o is IEditable)
if (((IEditable)o).IsDirty)
return true;
return false;
}
}
void IPrintDebugState.PrintDebugState(PropertyInfo propertyInfo, ref string str)
{
int original = _list.Count
- (_newItems == null? 0: _newItems.Count)
+ (_delItems == null? 0: _delItems.Count);
str += string.Format("{0,-20} {1} {2,-40} {3,-40} \r\n",
propertyInfo.Name, IsDirty? "*": " ", original, _list.Count);
}
#endregion
#region IList Members
public override bool IsFixedSize
{
get { return BindingListImpl.IsFixedSize; }
}
public override bool IsReadOnly
{
get { return BindingListImpl.IsReadOnly; }
}
public override object this[int index]
{
get { return BindingListImpl[index]; }
set
{
object o = BindingListImpl[index];
if (o != value)
{
RemoveInternal(o);
AddInternal(value);
}
BindingListImpl[index] = value;
}
}
public override int Add(object value)
{
AddInternal(value);
return BindingListImpl.Add(value);
}
public override void Clear()
{
if (_list.Count > 0)
RemoveInternal(_list);
BindingListImpl.Clear();
}
public override bool Contains(object item)
{
return BindingListImpl.Contains(item);
}
public override int IndexOf(object value)
{
return BindingListImpl.IndexOf(value);
}
public override void Insert(int index, object value)
{
AddInternal(value);
BindingListImpl.Insert(index, value);
}
public override void Remove(object value)
{
RemoveInternal(value);
BindingListImpl.Remove(value);
}
public override void RemoveAt(int index)
{
RemoveInternal(BindingListImpl[index]);
BindingListImpl.RemoveAt(index);
}
#endregion
#region ICollection Members
public override int Count
{
get { return BindingListImpl.Count; }
}
public override bool IsSynchronized
{
get { return BindingListImpl.IsSynchronized; }
}
public override object SyncRoot
{
get { return BindingListImpl.SyncRoot; }
}
public override void CopyTo(Array array, int arrayIndex)
{
BindingListImpl.CopyTo(array, arrayIndex);
}
#endregion
#region IEnumerable Members
public override IEnumerator GetEnumerator()
{
return BindingListImpl.GetEnumerator();
}
#endregion
#region Overridden Methods
public int Add(object value, bool trackChanges)
{
if (!trackChanges) _noTrackingChangesCount++;
int idx = Add(value);
if (!trackChanges) _noTrackingChangesCount--;
return idx;
}
public override void AddRange(ICollection c)
{
if (c.Count == 0)
return;
AddInternal(c);
BindingListImpl.AddRange(c);
}
public void AddRange(ICollection c, bool trackChanges)
{
if (c.Count == 0)
return;
if (!trackChanges) _noTrackingChangesCount++;
AddRange(c);
if (!trackChanges) _noTrackingChangesCount--;
}
public override int BinarySearch(int index, int count, object value, IComparer comparer)
{
return _list.BinarySearch(index, count, value, comparer);
}
public override int BinarySearch(object value)
{
return _list.BinarySearch(value);
}
public override int BinarySearch(object value, IComparer comparer)
{
return _list.BinarySearch(value, comparer);
}
public override int Capacity
{
get { return _list.Capacity; }
set { _list.Capacity = value; }
}
public void Clear(bool trackChanges)
{
if (!trackChanges) _noTrackingChangesCount++;
Clear();
if (!trackChanges) _noTrackingChangesCount--;
}
protected EditableArrayList Clone(EditableArrayList el)
{
if (_newItems != null) el._newItems = (ArrayList)_newItems.Clone();
if (_delItems != null) el._delItems = (ArrayList)_delItems.Clone();
el.NotifyChanges = NotifyChanges;
el._noTrackingChangesCount = _noTrackingChangesCount;
return el;
}
public override object Clone()
{
return Clone(new EditableArrayList(ItemType, (ArrayList)_list.Clone()));
}
public override void CopyTo(int index, Array array, int arrayIndex, int count)
{
_list.CopyTo(index, array, arrayIndex, count);
}
public override void CopyTo(Array array)
{
_list.CopyTo(array);
}
public override bool Equals(object obj)
{
return _list.Equals(obj);
}
public override IEnumerator GetEnumerator(int index, int count)
{
return _list.GetEnumerator(index, count);
}
public override int GetHashCode()
{
return _list.GetHashCode();
}
public override ArrayList GetRange(int index, int count)
{
return _list.GetRange(index, count);
}
public override int IndexOf(object value, int startIndex)
{
return _list.IndexOf(value, startIndex);
}
public override int IndexOf(object value, int startIndex, int count)
{
return _list.IndexOf(value, startIndex, count);
}
public void Insert(int index, object value, bool trackChanges)
{
if (!trackChanges) _noTrackingChangesCount++;
Insert(index, value);
if (!trackChanges) _noTrackingChangesCount--;
}
public override void InsertRange(int index, ICollection c)
{
BindingListImpl.InsertRange(index, c);
}
public void InsertRange(int index, ICollection c, bool trackChanges)
{
if (!trackChanges) _noTrackingChangesCount++;
InsertRange(index, c);
if (!trackChanges) _noTrackingChangesCount--;
}
public override int LastIndexOf(object value)
{
return _list.LastIndexOf(value);
}
public override int LastIndexOf(object value, int startIndex)
{
return _list.LastIndexOf(value, startIndex);
}
public override int LastIndexOf(object value, int startIndex, int count)
{
return _list.LastIndexOf(value, startIndex, count);
}
public void Remove(object obj, bool trackChanges)
{
if (!trackChanges) _noTrackingChangesCount++;
Remove(obj);
if (!trackChanges) _noTrackingChangesCount--;
}
public void RemoveAt(int index, bool trackChanges)
{
if (!trackChanges) _noTrackingChangesCount++;
RemoveAt(index);
if (!trackChanges) _noTrackingChangesCount--;
}
public override void RemoveRange(int index, int count)
{
RemoveInternal(GetRange(index, count));
BindingListImpl.RemoveRange(index, count);
}
public void RemoveRange(int index, int count, bool trackChanges)
{
if (!trackChanges) _noTrackingChangesCount++;
RemoveRange(index, count);
if (!trackChanges) _noTrackingChangesCount--;
}
public override void Reverse()
{
BindingListImpl.EndNew();
if (!BindingListImpl.IsSorted)
_list.Reverse();
else
throw new InvalidOperationException("Reverse is not supported for already sorted arrays. Invoke IBindingList.RemoveSort() first or provide reverse sort direction.");
if (_list.Count > 1)
OnResetList();
}
public override void Reverse(int index, int count)
{
BindingListImpl.EndNew();
if (!BindingListImpl.IsSorted)
_list.Reverse(index, count);
else
throw new InvalidOperationException("Range Reverse is not supported for already sorted arrays. Invoke IBindingList.RemoveSort() first.");
if (count > 1)
OnResetList();
}
public override void SetRange(int index, ICollection c)
{
if (c.Count == 0)
return;
AddInternal(c);
BindingListImpl.SetRange(index, c);
}
public override void Sort()
{
BindingListImpl.EndNew();
if (!BindingListImpl.IsSorted)
{
_list.Sort();
if (_list.Count > 1)
OnResetList();
}
else
{
if (BindingListImpl.SortProperty != null)
BindingListImpl.ApplySort(BindingListImpl.SortProperty, BindingListImpl.SortDirection);
else if (BindingListImpl.SortDescriptions != null)
BindingListImpl.ApplySort(BindingListImpl.SortDescriptions);
else
throw new InvalidOperationException("Currently applied sort method is not recognized/supported by EditableArrayList.");
}
}
public override void Sort(int index, int count, IComparer comparer)
{
BindingListImpl.EndNew();
if (!BindingListImpl.IsSorted)
_list.Sort(index, count, comparer);
else
throw new InvalidOperationException("Custom sorting is not supported on already sorted arrays. Invoke IBindingList.RemoveSort first.");
if (count > 1)
OnResetList();
}
public override void Sort(IComparer comparer)
{
BindingListImpl.EndNew();
if (!BindingListImpl.IsSorted)
_list.Sort(comparer);
else
throw new InvalidOperationException("Custom sorting is not supported on already sorted arrays. Invoke IBindingList.RemoveSort first.");
if (_list.Count > 1)
OnResetList();
}
public override object[] ToArray()
{
return _list.ToArray();
}
public override Array ToArray(Type type)
{
return _list.ToArray(type);
}
public override string ToString()
{
return _list.ToString();
}
public override void TrimToSize()
{
_list.TrimToSize();
}
#endregion
#region Static Methods
public static EditableArrayList Adapter(Type itemType, IList list)
{
if (list == null) throw new ArgumentNullException("list");
if (list.IsFixedSize)
return new EditableArrayList(itemType, new ArrayList(list));
return list is ArrayList?
new EditableArrayList(itemType, (ArrayList)list):
new EditableArrayList(itemType, ArrayList.Adapter(list));
}
public static new EditableArrayList Adapter(IList list)
{
return Adapter(TypeHelper.GetListItemType(list), list);
}
#endregion
#region IDisposable
public void Dispose()
{
Clear();
}
#endregion
#region SortMemberComparer
class SortMemberComparer : IComparer
{
readonly ListSortDirection _direction;
readonly string[] _memberNames;
readonly TypeAccessor _typeAccessor;
readonly MemberAccessor[] _members;
readonly MemberAccessor _member;
public SortMemberComparer(TypeAccessor typeAccessor, ListSortDirection direction, string[] memberNames)
{
_typeAccessor = typeAccessor;
_direction = direction;
_memberNames = memberNames;
_members = new MemberAccessor[memberNames.Length];
_member = _members[0] = _typeAccessor[memberNames[0]];
if (_member == null)
throw new InvalidOperationException(
string.Format("Field '{0}.{1}' not found.",
_typeAccessor.OriginalType.Name, _memberNames[0]));
}
public int Compare(object x, object y)
{
object a = _member.GetValue(x);
object b = _member.GetValue(y);
int n = Comparer.Default.Compare(a, b);
if (n == 0) for (int i = 1; n == 0 && i < _members.Length; i++)
{
MemberAccessor member = _members[i];
if (member == null)
{
member = _members[i] = _typeAccessor[_memberNames[i]];
if (member == null)
throw new InvalidOperationException(
string.Format("Field '{0}.{1}' not found.",
_typeAccessor.OriginalType.Name, _memberNames[i]));
}
a = member.GetValue(x);
b = member.GetValue(y);
n = Comparer.Default.Compare(a, b);
}
return _direction == ListSortDirection.Ascending? n: -n;
}
}
#endregion
#region ITypedList Members
[NonSerialized]
private TypedListImpl _typedListImpl;
private TypedListImpl TypedListImpl
{
get
{
if (_typedListImpl == null)
_typedListImpl = new TypedListImpl(_itemType);
return _typedListImpl;
}
}
public PropertyDescriptorCollection GetItemProperties(PropertyDescriptor[] listAccessors)
{
return GetItemProperties(listAccessors, null, null, true);
}
public PropertyDescriptorCollection GetItemProperties(
PropertyDescriptor[] listAccessors,
Type objectViewType,
IsNullHandler isNull,
bool cache)
{
return TypedListImpl.GetItemProperties(listAccessors, objectViewType, isNull, cache);
}
public string GetListName(PropertyDescriptor[] listAccessors)
{
return TypedListImpl.GetListName(listAccessors);
}
#endregion
#region IBindingList Members
sealed class BindingListImplInternal : BindingListImpl
{
private EditableArrayList _owner;
public BindingListImplInternal(IList list, Type itemType, EditableArrayList owner)
: base(list, itemType)
{
_owner = owner;
}
protected override void OnListChanged(EditableListChangedEventArgs e)
{
_owner.OnListChanged(e);
}
protected override void OnCollectionChanged(NotifyCollectionChangedEventArgs ea)
{
_owner.OnCollectionChanged(ea);
}
}
[NonSerialized]
private BindingListImpl _bindingListImpl;
private BindingListImpl BindingListImpl
{
get
{
if (_bindingListImpl == null)
_bindingListImpl = new BindingListImplInternal(_list, _itemType, this);
return _bindingListImpl;
}
}
public void AddIndex(PropertyDescriptor property)
{
BindingListImpl.AddIndex(property);
}
public object AddNew()
{
object newObject = null;
try
{
BeginSuppressEvent();
newObject = BindingListImpl.AddNew();
}
finally
{
EndSuppressEvent();
}
AddInternal(newObject);
int index = IndexOf(newObject);
EndSuppressEvent();
OnAddItem(newObject, index);
return newObject;
}
public bool AllowEdit
{
get { return BindingListImpl.AllowEdit; }
}
public bool AllowNew
{
get { return BindingListImpl.AllowNew; }
}
public bool AllowRemove
{
get { return BindingListImpl.AllowRemove; }
}
public void ApplySort(PropertyDescriptor property, ListSortDirection direction)
{
BindingListImpl.ApplySort(property, direction);
}
public int Find(PropertyDescriptor property, object key)
{
return BindingListImpl.Find(property, key);
}
public bool IsSorted
{
get { return BindingListImpl.IsSorted; }
}
public void RemoveIndex(PropertyDescriptor property)
{
BindingListImpl.RemoveIndex(property);
}
public void RemoveSort()
{
BindingListImpl.RemoveSort();
}
public ListSortDirection SortDirection
{
get { return BindingListImpl.SortDirection; }
}
public PropertyDescriptor SortProperty
{
get { return BindingListImpl.SortProperty; }
}
public bool SupportsChangeNotification
{
get { return BindingListImpl.SupportsChangeNotification; }
}
public event ListChangedEventHandler ListChanged;
private bool _supressEvent = false;
private void BeginSuppressEvent()
{
_supressEvent = true;
}
private void EndSuppressEvent()
{
_supressEvent = false;
}
public bool SupportsSearching
{
get { return BindingListImpl.SupportsSearching; }
}
public bool SupportsSorting
{
get { return BindingListImpl.SupportsSorting; }
}
#endregion
#region Sorting Enhancement
public void CreateSortSubstitution(string originalProperty, string substituteProperty)
{
BindingListImpl.CreateSortSubstitution(originalProperty, substituteProperty);
}
public void RemoveSortSubstitution(string originalProperty)
{
BindingListImpl.RemoveSortSubstitution(originalProperty);
}
#endregion
#region IBindingListView Members
public void ApplySort(ListSortDescriptionCollection sorts)
{
BindingListImpl.ApplySort(sorts);
}
public string Filter
{
get { return BindingListImpl.Filter; }
set { BindingListImpl.Filter = value; }
}
public void RemoveFilter()
{
BindingListImpl.RemoveFilter();
}
public ListSortDescriptionCollection SortDescriptions
{
get { return BindingListImpl.SortDescriptions; }
}
public bool SupportsAdvancedSorting
{
get { return BindingListImpl.SupportsAdvancedSorting; }
}
public bool SupportsFiltering
{
get { return BindingListImpl.SupportsFiltering; }
}
#endregion
#region ICancelAddNew Members
public void CancelNew(int itemIndex)
{
if (itemIndex >= 0 && itemIndex < List.Count)
NewItems.Remove(List[itemIndex]);
BindingListImpl.CancelNew(itemIndex);
}
public void EndNew(int itemIndex)
{
BindingListImpl.EndNew(itemIndex);
}
#endregion
#region INotifyCollectionChanged Members
public event NotifyCollectionChangedEventHandler CollectionChanged;
protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
{
if (!_supressEvent && NotifyChanges && CollectionChanged != null)
CollectionChanged(this, e);
}
#endregion
}
} |