Mercurial > pub > bltoolkit
view Source/EditableObjects/EditableArrayList.cs @ 9:1e85f66cf767 default tip
update bltoolkit
author | nickolay |
---|---|
date | Thu, 05 Apr 2018 20:53:26 +0300 |
parents | f990fcb411a9 |
children |
line wrap: on
line source
using System; using System.Collections; using System.Collections.Specialized; using System.ComponentModel; using System.Diagnostics; using System.Reflection; namespace BLToolkit.EditableObjects { using Reflection; using Mapping; using ComponentModel; [DebuggerDisplay("Count = {Count}, ItemType = {ItemType}")] [Serializable] public class EditableArrayList : ArrayList, IEditable, ISortable, ISupportMapping, IDisposable, IPrintDebugState, ITypedList, IBindingListView, ICancelAddNew, INotifyCollectionChanged { #region Constructors public EditableArrayList() : this(typeof(object), new ArrayList(), true) { } public EditableArrayList([JetBrains.Annotations.NotNull] Type itemType, [JetBrains.Annotations.NotNull] 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(false); _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 internal ArrayList List { get; private set; } public Type ItemType { get; private set; } private ArrayList _newItems; public ArrayList NewItems { get { return _newItems ?? (_newItems = new ArrayList()); } } private ArrayList _delItems; public ArrayList DelItems { get { return _delItems ?? (_delItems = new ArrayList()); } } 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) { var sorts = sortExpression.Split(','); for (var i = 0; i < sorts.Length; i++) sorts[i] = sorts[i].Trim(); var last = sorts[sorts.Length - 1]; var 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) { var 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 (var o in e) AddInternal(o); } private void RemoveInternal(IEnumerable e) { foreach (var 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; 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 (var o in List) { if (o is EditableObject) ((EditableObject)o).AcceptChanges(); else if (o is IEditable) ((IEditable)o).AcceptChanges(); } _newItems = null; _delItems = null; } public virtual void RejectChanges() { _noTrackingChangesCount++; if (_delItems != null) foreach (var o in _delItems) Add(o); if (_newItems != null) foreach (var o in _newItems) Remove(o); foreach (var o in List) { if (o is EditableObject) ((EditableObject)o).RejectChanges(); else 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 (var o in List) { if (o is EditableObject) if (((EditableObject)o).IsDirty) return true; else if (o is IEditable) if (((IEditable)o).IsDirty) return true; } return false; } } void IPrintDebugState.PrintDebugState(PropertyInfo propertyInfo, ref string str) { var 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 { var 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++; var 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) { var a = _member.GetValue(x); var b = _member.GetValue(y); var n = Comparer.Default.Compare(a, b); if (n == 0) for (var i = 1; n == 0 && i < _members.Length; i++) { var 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 { return _typedListImpl ?? (_typedListImpl = new TypedListImpl(ItemType)); } } 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 { readonly 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 { return _bindingListImpl ?? (_bindingListImpl = new BindingListImplInternal(List, ItemType, this)); } } public void AddIndex(PropertyDescriptor property) { BindingListImpl.AddIndex(property); } public object AddNew() { object newObject; try { BeginSuppressEvent(); newObject = BindingListImpl.AddNew(); } finally { EndSuppressEvent(); } AddInternal(newObject); var 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; 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 } }