View Javadoc
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 }