source: josm/trunk/src/org/openstreetmap/josm/data/osm/event/DatasetEventManager.java@ 12636

Last change on this file since 12636 was 12636, checked in by Don-vip, 7 years ago

see #15182 - deprecate Main.getLayerManager(). Replacement: gui.MainApplication.getLayerManager()

  • Property svn:eol-style set to native
File size: 8.5 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.osm.event;
3
4import java.util.ArrayList;
5import java.util.Arrays;
6import java.util.List;
7import java.util.Objects;
8import java.util.Queue;
9import java.util.concurrent.CopyOnWriteArrayList;
10import java.util.concurrent.LinkedBlockingQueue;
11
12import javax.swing.SwingUtilities;
13
14import org.openstreetmap.josm.data.osm.DataSet;
15import org.openstreetmap.josm.data.osm.event.DataSetListenerAdapter.Listener;
16import org.openstreetmap.josm.gui.MainApplication;
17import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeEvent;
18import org.openstreetmap.josm.gui.layer.MainLayerManager.ActiveLayerChangeListener;
19
20/**
21 * This class allows to add DatasetListener to currently active dataset. If active
22 * layer is changed, listeners are automatically registered at new active dataset
23 * (it's no longer necessary to register for layer events and reregister every time
24 * new layer is selected)
25 *
26 * Events in EDT are supported, see {@link #addDatasetListener(DataSetListener, FireMode)}
27 *
28 */
29public class DatasetEventManager implements ActiveLayerChangeListener, Listener {
30
31 private static final DatasetEventManager INSTANCE = new DatasetEventManager();
32
33 private final class EdtRunnable implements Runnable {
34 @Override
35 public void run() {
36 while (!eventsInEDT.isEmpty()) {
37 DataSet dataSet = null;
38 AbstractDatasetChangedEvent consolidatedEvent = null;
39 AbstractDatasetChangedEvent event;
40
41 while ((event = eventsInEDT.poll()) != null) {
42 fireEvents(inEDTListeners, event);
43
44 // DataSet changed - fire consolidated event early
45 if (consolidatedEvent != null && dataSet != event.getDataset()) {
46 fireConsolidatedEvents(inEDTListeners, consolidatedEvent);
47 consolidatedEvent = null;
48 }
49
50 dataSet = event.getDataset();
51
52 // Build consolidated event
53 if (event instanceof DataChangedEvent) {
54 // DataChangeEvent can contains other events, so it gets special handling
55 DataChangedEvent dataEvent = (DataChangedEvent) event;
56 if (dataEvent.getEvents() == null) {
57 consolidatedEvent = dataEvent; // Dataset was completely changed, we can ignore older events
58 } else {
59 if (consolidatedEvent == null) {
60 consolidatedEvent = new DataChangedEvent(dataSet, dataEvent.getEvents());
61 } else if (consolidatedEvent instanceof DataChangedEvent) {
62 List<AbstractDatasetChangedEvent> evts = ((DataChangedEvent) consolidatedEvent).getEvents();
63 if (evts != null) {
64 evts.addAll(dataEvent.getEvents());
65 }
66 } else {
67 AbstractDatasetChangedEvent oldConsolidateEvent = consolidatedEvent;
68 consolidatedEvent = new DataChangedEvent(dataSet, dataEvent.getEvents());
69 ((DataChangedEvent) consolidatedEvent).getEvents().add(oldConsolidateEvent);
70 }
71 }
72 } else {
73 // Normal events
74 if (consolidatedEvent == null) {
75 consolidatedEvent = event;
76 } else if (consolidatedEvent instanceof DataChangedEvent) {
77 List<AbstractDatasetChangedEvent> evs = ((DataChangedEvent) consolidatedEvent).getEvents();
78 if (evs != null) {
79 evs.add(event);
80 }
81 } else {
82 consolidatedEvent = new DataChangedEvent(dataSet, new ArrayList<>(Arrays.asList(consolidatedEvent)));
83 }
84 }
85 }
86
87 // Fire consolidated event
88 if (consolidatedEvent != null) {
89 fireConsolidatedEvents(inEDTListeners, consolidatedEvent);
90 }
91 }
92 }
93 }
94
95 /**
96 * Event firing mode regarding Event Dispatch Thread.
97 */
98 public enum FireMode {
99 /**
100 * Fire in calling thread immediately.
101 */
102 IMMEDIATELY,
103 /**
104 * Fire in event dispatch thread.
105 */
106 IN_EDT,
107 /**
108 * Fire in event dispatch thread. If more than one event arrived when event queue is checked, merged them to one event
109 */
110 IN_EDT_CONSOLIDATED
111 }
112
113 private static class ListenerInfo {
114 private final DataSetListener listener;
115 private final boolean consolidate;
116
117 ListenerInfo(DataSetListener listener, boolean consolidate) {
118 this.listener = listener;
119 this.consolidate = consolidate;
120 }
121
122 @Override
123 public int hashCode() {
124 return Objects.hash(listener);
125 }
126
127 @Override
128 public boolean equals(Object o) {
129 if (this == o) return true;
130 if (o == null || getClass() != o.getClass()) return false;
131 ListenerInfo that = (ListenerInfo) o;
132 return Objects.equals(listener, that.listener);
133 }
134 }
135
136 /**
137 * Replies the unique instance.
138 * @return the unique instance
139 */
140 public static DatasetEventManager getInstance() {
141 return INSTANCE;
142 }
143
144 private final Queue<AbstractDatasetChangedEvent> eventsInEDT = new LinkedBlockingQueue<>();
145 private final CopyOnWriteArrayList<ListenerInfo> inEDTListeners = new CopyOnWriteArrayList<>();
146 private final CopyOnWriteArrayList<ListenerInfo> normalListeners = new CopyOnWriteArrayList<>();
147 private final DataSetListener myListener = new DataSetListenerAdapter(this);
148 private final Runnable edtRunnable = new EdtRunnable();
149
150 /**
151 * Constructs a new {@code DatasetEventManager}.
152 */
153 public DatasetEventManager() {
154 MainApplication.getLayerManager().addActiveLayerChangeListener(this);
155 }
156
157 /**
158 * Register listener, that will receive events from currently active dataset
159 * @param listener the listener to be registered
160 * @param fireMode If {@link FireMode#IN_EDT} or {@link FireMode#IN_EDT_CONSOLIDATED},
161 * listener will be notified in event dispatch thread instead of thread that caused
162 * the dataset change
163 */
164 public void addDatasetListener(DataSetListener listener, FireMode fireMode) {
165 if (fireMode == FireMode.IN_EDT || fireMode == FireMode.IN_EDT_CONSOLIDATED) {
166 inEDTListeners.addIfAbsent(new ListenerInfo(listener, fireMode == FireMode.IN_EDT_CONSOLIDATED));
167 } else {
168 normalListeners.addIfAbsent(new ListenerInfo(listener, false));
169 }
170 }
171
172 /**
173 * Unregister listener.
174 * @param listener listener to remove
175 */
176 public void removeDatasetListener(DataSetListener listener) {
177 ListenerInfo searchListener = new ListenerInfo(listener, false);
178 inEDTListeners.remove(searchListener);
179 normalListeners.remove(searchListener);
180 }
181
182 @Override
183 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
184 DataSet oldData = e.getPreviousEditDataSet();
185 if (oldData != null) {
186 oldData.removeDataSetListener(myListener);
187 }
188
189 DataSet newData = e.getSource().getEditDataSet();
190 if (newData != null) {
191 newData.addDataSetListener(myListener);
192 }
193 processDatasetEvent(new DataChangedEvent(newData));
194 }
195
196 private static void fireEvents(List<ListenerInfo> listeners, AbstractDatasetChangedEvent event) {
197 for (ListenerInfo listener: listeners) {
198 if (!listener.consolidate) {
199 event.fire(listener.listener);
200 }
201 }
202 }
203
204 private static void fireConsolidatedEvents(List<ListenerInfo> listeners, AbstractDatasetChangedEvent event) {
205 for (ListenerInfo listener: listeners) {
206 if (listener.consolidate) {
207 event.fire(listener.listener);
208 }
209 }
210 }
211
212 @Override
213 public void processDatasetEvent(AbstractDatasetChangedEvent event) {
214 fireEvents(normalListeners, event);
215 eventsInEDT.add(event);
216 SwingUtilities.invokeLater(edtRunnable);
217 }
218}
Note: See TracBrowser for help on using the repository browser.