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

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

see #20129 - Fix typos and misspellings in the code (patch by gaben)

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