]> git.ktnx.net Git - mobile-ledger.git/blob - app/src/main/java/net/ktnx/mobileledger/utils/ObservableList.java
notify profile list adapter when the currently selected profile changes
[mobile-ledger.git] / app / src / main / java / net / ktnx / mobileledger / utils / ObservableList.java
1 /*
2  * Copyright © 2019 Damyan Ivanov.
3  * This file is part of MoLe.
4  * MoLe is free software: you can distribute it and/or modify it
5  * under the term of the GNU General Public License as published by
6  * the Free Software Foundation, either version 3 of the License, or
7  * (at your opinion), any later version.
8  *
9  * MoLe is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License terms for details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with MoLe. If not, see <https://www.gnu.org/licenses/>.
16  */
17
18 package net.ktnx.mobileledger.utils;
19
20 import android.os.Build;
21 import android.util.Log;
22
23 import org.jetbrains.annotations.NotNull;
24
25 import java.util.Collection;
26 import java.util.Comparator;
27 import java.util.Iterator;
28 import java.util.List;
29 import java.util.ListIterator;
30 import java.util.Observable;
31 import java.util.Spliterator;
32 import java.util.concurrent.locks.ReentrantReadWriteLock;
33 import java.util.function.Consumer;
34 import java.util.function.Predicate;
35 import java.util.function.UnaryOperator;
36 import java.util.stream.Stream;
37
38 import androidx.annotation.NonNull;
39 import androidx.annotation.Nullable;
40 import androidx.annotation.RequiresApi;
41
42 public class ObservableList<T> extends Observable implements List<T> {
43     private List<T> list;
44     private ReentrantReadWriteLock lock = new ReentrantReadWriteLock();
45     public ObservableList(List<T> list) {
46         this.list = list;
47     }
48     private void forceNotify() {
49         setChanged();
50         notifyObservers();
51     }
52     private void forceNotify(int index) {
53         setChanged();
54         notifyObservers(index);
55     }
56     public int size() {
57         try (LockHolder lh = lockForReading()) {
58             return list.size();
59         }
60     }
61     public boolean isEmpty() {
62         try (LockHolder lh = lockForReading()) {
63             return list.isEmpty();
64         }
65     }
66     public boolean contains(@Nullable Object o) {
67         try (LockHolder lh = lockForReading()) {
68             return list.contains(o);
69         }
70     }
71     @NonNull
72     public Iterator<T> iterator() {
73         throw new RuntimeException("Iterators break encapsulation and ignore locking");
74 //        return list.iterator();
75     }
76     public Object[] toArray() {
77         try (LockHolder lh = lockForReading()) {
78             return list.toArray();
79         }
80     }
81     public <T1> T1[] toArray(@Nullable T1[] a) {
82         try (LockHolder lh = lockForReading()) {
83             return list.toArray(a);
84         }
85     }
86     public boolean add(T t) {
87         try (LockHolder lh = lockForWriting()) {
88             boolean result = list.add(t);
89             lh.downgrade();
90             if (result) forceNotify();
91             return result;
92         }
93     }
94     public boolean remove(@Nullable Object o) {
95         try (LockHolder lh = lockForWriting()) {
96             boolean result = list.remove(o);
97             lh.downgrade();
98             if (result) forceNotify();
99             return result;
100         }
101     }
102     public T removeQuietly(int index) {
103         return list.remove(index);
104     }
105     public boolean containsAll(@NonNull Collection<?> c) {
106         try (LockHolder lh = lockForReading()) {
107             return list.containsAll(c);
108         }
109     }
110     public boolean addAll(@NonNull Collection<? extends T> c) {
111         try (LockHolder lh = lockForWriting()) {
112             boolean result = list.addAll(c);
113             lh.downgrade();
114             if (result) forceNotify();
115             return result;
116         }
117     }
118     public boolean addAll(int index, @NonNull Collection<? extends T> c) {
119         try (LockHolder lh = lockForWriting()) {
120             boolean result = list.addAll(index, c);
121             lh.downgrade();
122             if (result) forceNotify();
123             return result;
124         }
125     }
126     public boolean addAllQuietly(int index, Collection<? extends T> c) {
127         return list.addAll(index, c);
128     }
129     public boolean removeAll(@NonNull Collection<?> c) {
130         try (LockHolder lh = lockForWriting()) {
131             boolean result = list.removeAll(c);
132             lh.downgrade();
133             if (result) forceNotify();
134             return result;
135         }
136     }
137     public boolean retainAll(@NonNull Collection<?> c) {
138         try (LockHolder lh = lockForWriting()) {
139             boolean result = list.retainAll(c);
140             lh.downgrade();
141             if (result) forceNotify();
142             return result;
143         }
144     }
145     @RequiresApi(api = Build.VERSION_CODES.N)
146     public void replaceAll(@NonNull UnaryOperator<T> operator) {
147         try (LockHolder lh = lockForWriting()) {
148             list.replaceAll(operator);
149             lh.downgrade();
150             forceNotify();
151         }
152     }
153     @RequiresApi(api = Build.VERSION_CODES.N)
154     public void sort(@Nullable Comparator<? super T> c) {
155         try (LockHolder lh = lockForWriting()) {
156             lock.writeLock().lock();
157             list.sort(c);
158             lh.downgrade();
159             forceNotify();
160         }
161     }
162     public void clear() {
163         try (LockHolder lh = lockForWriting()) {
164             boolean wasEmpty = list.isEmpty();
165             list.clear();
166             lh.downgrade();
167             if (!wasEmpty) forceNotify();
168         }
169     }
170     public T get(int index) {
171         try (LockHolder lh = lockForReading()) {
172             return list.get(index);
173         }
174     }
175     public T set(int index, T element) {
176         try (LockHolder lh = lockForWriting()) {
177             T result = list.set(index, element);
178             lh.downgrade();
179             forceNotify();
180             return result;
181         }
182     }
183     public void add(int index, T element) {
184         try (LockHolder lh = lockForWriting()) {
185             list.add(index, element);
186             lh.downgrade();
187             forceNotify();
188         }
189     }
190     public T remove(int index) {
191         try (LockHolder lh = lockForWriting()) {
192             T result = list.remove(index);
193             lh.downgrade();
194             forceNotify();
195             return result;
196         }
197     }
198     public int indexOf(@Nullable Object o) {
199         try (LockHolder lh = lockForReading()) {
200             return list.indexOf(o);
201         }
202     }
203     public int lastIndexOf(@Nullable Object o) {
204         try (LockHolder lh = lockForReading()) {
205             return list.lastIndexOf(o);
206         }
207     }
208     @NotNull
209     public ListIterator<T> listIterator() {
210         if (!lock.isWriteLockedByCurrentThread()) throw new RuntimeException(
211                 "Iterators break encapsulation and ignore locking. Write-lock first");
212         return list.listIterator();
213     }
214     @NotNull
215     public ListIterator<T> listIterator(int index) {
216         if (!lock.isWriteLockedByCurrentThread()) throw new RuntimeException(
217                 "Iterators break encapsulation and ignore locking. Write-lock first");
218         return list.listIterator(index);
219     }
220     @NotNull
221     public List<T> subList(int fromIndex, int toIndex) {
222         try (LockHolder lh = lockForReading()) {
223             return list.subList(fromIndex, toIndex);
224         }
225     }
226     @NotNull
227     @RequiresApi(api = Build.VERSION_CODES.N)
228     public Spliterator<T> spliterator() {
229         if (!lock.isWriteLockedByCurrentThread()) throw new RuntimeException(
230                 "Iterators break encapsulation and ignore locking. Write-lock first");
231         return list.spliterator();
232     }
233     @RequiresApi(api = Build.VERSION_CODES.N)
234     public boolean removeIf(Predicate<? super T> filter) {
235         try (LockHolder lh = lockForWriting()) {
236             boolean result = list.removeIf(filter);
237             lh.downgrade();
238             if (result) forceNotify();
239             return result;
240         }
241     }
242     @RequiresApi(api = Build.VERSION_CODES.N)
243     public Stream<T> stream() {
244         if (!lock.isWriteLockedByCurrentThread()) throw new RuntimeException(
245                 "Iterators break encapsulation and ignore locking. Write-lock first");
246         return list.stream();
247     }
248     @RequiresApi(api = Build.VERSION_CODES.N)
249     public Stream<T> parallelStream() {
250         if (!lock.isWriteLockedByCurrentThread()) throw new RuntimeException(
251                 "Iterators break encapsulation and ignore locking. Write-lock first");
252         return list.parallelStream();
253     }
254     @RequiresApi(api = Build.VERSION_CODES.N)
255     public void forEach(Consumer<? super T> action) {
256         try (LockHolder lh = lockForReading()) {
257             list.forEach(action);
258         }
259     }
260     public List<T> getList() {
261         if (!lock.isWriteLockedByCurrentThread()) throw new RuntimeException(
262                 "Direct list access breaks encapsulation and ignore locking. Write-lock first");
263         return list;
264     }
265     public void setList(List<T> aList) {
266         try (LockHolder lh = lockForWriting()) {
267             list = aList;
268             lh.downgrade();
269             forceNotify();
270         }
271     }
272     public void triggerItemChangedNotification(T item) {
273         try (LockHolder lh = lockForReading()) {
274             int index = list.indexOf(item);
275             if (index == -1) {
276                 Log.d("ObList", "??? not sending notifications for item not found in the list");
277                 return;
278             }
279             Log.d("ObList", "Notifying item change observers");
280             triggerItemChangedNotification(index);
281         }
282     }
283     public void triggerItemChangedNotification(int index) {
284         forceNotify(index);
285     }
286     public LockHolder lockForWriting() {
287         ReentrantReadWriteLock.WriteLock wLock = lock.writeLock();
288         wLock.lock();
289
290         ReentrantReadWriteLock.ReadLock rLock = lock.readLock();
291         rLock.lock();
292
293         return new LockHolder(rLock, wLock);
294     }
295     public LockHolder lockForReading() {
296         ReentrantReadWriteLock.ReadLock rLock = lock.readLock();
297         rLock.lock();
298         return new LockHolder(rLock);
299     }
300 }