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

Last change on this file since 9371 was 9371, checked in by simon04, 8 years ago

Java 7: use Objects.equals and Objects.hash

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