Mercurial > pub > bltoolkit
diff Source/EditableObjects/EditableArrayList.cs @ 0:f990fcb411a9
Копия текущей версии из github
author | cin |
---|---|
date | Thu, 27 Mar 2014 21:46:09 +0400 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Source/EditableObjects/EditableArrayList.cs Thu Mar 27 21:46:09 2014 +0400 @@ -0,0 +1,1100 @@ +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 + } +}