2a220ea7f5f88214eb2b280ac82b6c4387e6872c
[platform/core/csapi/tizenfx.git] / src / Tizen.NUI / src / internal / XamlBinding / ObservableWrapper.cs
1 using System;
2 using System.Collections;
3 using System.Collections.Generic;
4 using System.Collections.ObjectModel;
5 using System.Collections.Specialized;
6 using System.Linq;
7
8 namespace Tizen.NUI.Binding
9 {
10     internal class ObservableWrapper<TTrack, TRestrict> : IList<TRestrict>, INotifyCollectionChanged where TTrack : Element where TRestrict : TTrack
11     {
12         readonly ObservableCollection<TTrack> _list;
13
14         public ObservableWrapper(ObservableCollection<TTrack> list)
15         {
16             if (list == null)
17                 throw new ArgumentNullException("list");
18
19             _list = list;
20
21             list.CollectionChanged += ListOnCollectionChanged;
22         }
23
24         public void Add(TRestrict item)
25         {
26             if (item == null)
27                 throw new ArgumentNullException("item");
28             if (IsReadOnly)
29                 throw new NotSupportedException("The collection is read-only.");
30
31             if (_list.Contains(item))
32                 return;
33
34             item.Owned = true;
35             _list.Add(item);
36         }
37
38         public void Clear()
39         {
40             if (IsReadOnly)
41                 throw new NotSupportedException("The collection is read-only.");
42
43             foreach (TRestrict item in _list.OfType<TRestrict>().ToArray())
44             {
45                 _list.Remove(item);
46                 item.Owned = false;
47             }
48         }
49
50         public bool Contains(TRestrict item)
51         {
52             return item.Owned && _list.Contains(item);
53         }
54
55         public void CopyTo(TRestrict[] array, int destIndex)
56         {
57             if (array.Length - destIndex < Count)
58                 throw new ArgumentException("Destination array was not long enough. Check destIndex and length, and the array's lower bounds.");
59             foreach (TRestrict item in this)
60             {
61                 array[destIndex] = item;
62                 destIndex++;
63             }
64         }
65
66         public int Count
67         {
68             get { return _list.Where(i => i.Owned).OfType<TRestrict>().Count(); }
69         }
70
71         public bool IsReadOnly { get; internal set; }
72
73         public bool Remove(TRestrict item)
74         {
75             if (item == null)
76                 throw new ArgumentNullException("item");
77             if (IsReadOnly)
78                 throw new NotSupportedException("The collection is read-only.");
79
80             if (!item.Owned)
81                 return false;
82
83             if (_list.Remove(item))
84             {
85                 item.Owned = false;
86                 return true;
87             }
88             return false;
89         }
90
91         IEnumerator IEnumerable.GetEnumerator()
92         {
93             return GetEnumerator();
94         }
95
96         public IEnumerator<TRestrict> GetEnumerator()
97         {
98             return _list.Where(i => i.Owned).OfType<TRestrict>().GetEnumerator();
99         }
100
101         public int IndexOf(TRestrict value)
102         {
103             int innerIndex = _list.IndexOf(value);
104             if (innerIndex == -1)
105                 return -1;
106             return ToOuterIndex(innerIndex);
107         }
108
109         public void Insert(int index, TRestrict item)
110         {
111             if (item == null)
112                 throw new ArgumentNullException("item");
113             if (IsReadOnly)
114                 throw new NotSupportedException("The collection is read-only.");
115
116             item.Owned = true;
117             _list.Insert(ToInnerIndex(index), item);
118         }
119
120         public TRestrict this[int index]
121         {
122             get { return (TRestrict)_list[ToInnerIndex(index)]; }
123             set
124             {
125                 int innerIndex = ToInnerIndex(index);
126                 if (value != null)
127                     value.Owned = true;
128                 TTrack old = _list[innerIndex];
129                 _list[innerIndex] = value;
130
131                 if (old != null)
132                     old.Owned = false;
133             }
134         }
135
136         public void RemoveAt(int index)
137         {
138             if (IsReadOnly)
139                 throw new NotSupportedException("The collection is read-only");
140             int innerIndex = ToInnerIndex(index);
141             TTrack item = _list[innerIndex];
142             if (item != null && item.Owned)
143             {
144                 _list.RemoveAt(innerIndex);
145                 item.Owned = false;
146             }
147         }
148
149         public event NotifyCollectionChangedEventHandler CollectionChanged;
150
151         void ListOnCollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
152         {
153             NotifyCollectionChangedEventHandler handler = CollectionChanged;
154             if (handler == null)
155                 return;
156
157             switch (e.Action)
158             {
159                 case NotifyCollectionChangedAction.Add:
160                     if (e.NewStartingIndex == -1 || e.NewItems?.Count > 1)
161                         goto case NotifyCollectionChangedAction.Reset;
162
163                     var newItem = e.NewItems?[0] as TRestrict;
164                     if (newItem == null || !newItem.Owned)
165                         break;
166
167                     int outerIndex = ToOuterIndex(e.NewStartingIndex);
168                     handler(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, e.NewItems, outerIndex));
169                     break;
170                 case NotifyCollectionChangedAction.Move:
171                     if (e.NewStartingIndex == -1 || e.OldStartingIndex == -1 || e.NewItems?.Count > 1)
172                         goto case NotifyCollectionChangedAction.Reset;
173
174                     var movedItem = e.NewItems?[0] as TRestrict;
175                     if (movedItem == null || !movedItem.Owned)
176                         break;
177
178                     int outerOldIndex = ToOuterIndex(e.OldStartingIndex);
179                     int outerNewIndex = ToOuterIndex(e.NewStartingIndex);
180                     handler(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Move, e.NewItems, outerNewIndex, outerOldIndex));
181                     break;
182                 case NotifyCollectionChangedAction.Remove:
183                     if (e.OldStartingIndex == -1 || e.OldItems?.Count > 1)
184                         goto case NotifyCollectionChangedAction.Reset;
185
186                     var removedItem = e.OldItems?[0] as TRestrict;
187                     if (removedItem == null || !removedItem.Owned)
188                         break;
189
190                     int outerRemovedIndex = ToOuterIndex(e.OldStartingIndex);
191                     var args = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, removedItem, outerRemovedIndex);
192                     handler(this, args);
193                     break;
194                 case NotifyCollectionChangedAction.Replace:
195                     if (e.NewStartingIndex == -1 || e.OldStartingIndex == -1 || e.NewItems?.Count > 1)
196                         goto case NotifyCollectionChangedAction.Reset;
197
198                     var newReplaceItem = e.NewItems?[0] as TRestrict;
199                     var oldReplaceItem = e.OldItems?[0] as TRestrict;
200
201                     if ((newReplaceItem == null || !newReplaceItem.Owned) && (oldReplaceItem == null || !oldReplaceItem.Owned))
202                     {
203                         break;
204                     }
205                     if (newReplaceItem == null || !newReplaceItem.Owned || oldReplaceItem == null || !oldReplaceItem.Owned)
206                     {
207                         goto case NotifyCollectionChangedAction.Reset;
208                     }
209
210                     int index = ToOuterIndex(e.NewStartingIndex);
211
212                     var replaceArgs = new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newReplaceItem, oldReplaceItem, index);
213                     handler(this, replaceArgs);
214                     break;
215                 case NotifyCollectionChangedAction.Reset:
216                     handler(this, new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
217                     break;
218                 default:
219                     throw new ArgumentOutOfRangeException();
220             }
221         }
222
223         int ToInnerIndex(int outterIndex)
224         {
225             var outerIndex = 0;
226             int innerIndex;
227             for (innerIndex = 0; innerIndex < _list.Count; innerIndex++)
228             {
229                 TTrack item = _list[innerIndex];
230                 if (item is TRestrict && item.Owned)
231                 {
232                     if (outerIndex == outterIndex)
233                         return innerIndex;
234                     outerIndex++;
235                 }
236             }
237
238             return innerIndex;
239         }
240
241         int ToOuterIndex(int innerIndex)
242         {
243             var outerIndex = 0;
244             for (var index = 0; index < innerIndex; index++)
245             {
246                 TTrack item = _list[index];
247                 if (item is TRestrict && item.Owned)
248                 {
249                     outerIndex++;
250                 }
251             }
252
253             return outerIndex;
254         }
255     }
256 }