1 /*
2 * Copyright 2013-2023 Medical Information Systems Research Group (https://medical.zcu.cz),
3 * Department of Computer Science and Engineering, University of West Bohemia.
4 * Address: Univerzitni 8, 306 14 Plzen, Czech Republic.
5 *
6 * This file is part of Sparkle project.
7 *
8 * Sparkle is free software: you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation, either version 3 of the License.
11 *
12 * Sparkle is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with Sparkle. If not, see <http://www.gnu.org/licenses/>.
19 */
20 package cz.zcu.mre.sparkle.tools;
21
22 import javafx.beans.InvalidationListener;
23 import javafx.beans.Observable;
24 import javafx.collections.ListChangeListener;
25 import javafx.collections.ObservableList;
26 import java.util.Collection;
27 import java.util.Set;
28
29 /**
30 * <p>
31 * Komplexní, snadno použitelné rozhraní určené ke sledování změn v různých
32 * částech aplikace. Objekt třídy, která toto rozhraní implementuje, si musí
33 * držet seznam svých posluchačů a o svých změnách dávat vědět voláním
34 * {@link Changeable#notifyChanged()}. Zároveň se sám stává posluchačem
35 * {@link InvalidationListener}. O změnách komponent, které objekt sleduje, se
36 * automaticky dozvídají také jeho vlastní posluchači. Informace o změně tak
37 * šíří lavinově.
38 * <p>
39 * Chce-li objekt sledovat změny {@link Observable} komponenty, zavolá metodu
40 * {@link Changeable#watch(Observable)} a tuto komponentu jí předá jako
41 * parametr.
42 * <p>
43 * Chce-li objekt sledovat změny více komponent na seznamu včetně změn samotného
44 * seznamu a komponent přidaných na seznam později, může k tomu využít metody
45 * {@link Changeable#autoWatch(ObservableList, Class)}, resp.
46 * {@link Changeable#autoWatch(ObservableList, Collection)}.
47 *
48 * @author Jan Smucr
49 * @author Klara Hlavacova
50 * @author Petr Vcelak (vcelak@kiv.zcu.cz)
51 */
52 @SuppressWarnings("UnusedReturnValue")
53 public interface Changeable extends Observable, InvalidationListener {
54
55 Set<InvalidationListener> getObservers();
56
57 default @Override
58 void addListener(final InvalidationListener listener) {
59 getObservers().add(listener);
60 }
61
62 default @Override
63 void removeListener(final InvalidationListener listener) {
64 getObservers().remove(listener);
65 }
66
67 default void notifyChanged() {
68 getObservers().forEach((o) -> o.invalidated(this));
69 }
70
71 default @Override
72 void invalidated(final Observable observable) {
73 getObservers().forEach((o) -> o.invalidated(observable));
74 }
75
76 default void watch(final Observable observable) {
77 observable.addListener(this);
78 }
79
80 default void unwatch(final Observable observable) {
81 observable.removeListener(this);
82 }
83
84 default <E> ListChangeListener<?> autoWatch(final ObservableList<E> items,
85 final Collection<Class<? extends Observable>> watchedObjectsClasses) {
86 final ListChangeListener<E> result = c
87 -> {
88 while (c.next()) {
89 for (final Object n : c.getAddedSubList()) {
90 for (final Class<? extends Observable> clazz : watchedObjectsClasses) {
91 if (clazz.isAssignableFrom(n.getClass())) {
92 watch((Observable) n);
93 break;
94 }
95 }
96 }
97 for (final Object n : c.getRemoved()) {
98 for (final Class<? extends Observable> clazz : watchedObjectsClasses) {
99 if (clazz.isAssignableFrom(n.getClass())) {
100 unwatch((Observable) n);
101 break;
102 }
103 }
104 }
105 }
106 notifyChanged();
107 };
108
109 items.addListener(result);
110 return result;
111 }
112
113 default <E> ListChangeListener<?> autoWatch(final ObservableList<E> items,
114 final Class<? extends Observable> watchedObjectsClass) {
115 final ListChangeListener<E> result = c
116 -> {
117 while (c.next()) {
118 for (final Object n : c.getAddedSubList()) {
119 if (watchedObjectsClass.isAssignableFrom(n.getClass())) {
120 watch((Observable) n);
121 break;
122 }
123 }
124 for (final Object n : c.getRemoved()) {
125 if (watchedObjectsClass.isAssignableFrom(n.getClass())) {
126 unwatch((Observable) n);
127 break;
128 }
129 }
130 }
131 notifyChanged();
132 };
133
134 items.addListener(result);
135 return result;
136 }
137 }