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

Last change on this file since 10332 was 10332, checked in by stoecker, 8 years ago

Do not use deprecated EditLayerChangedListener - patch my Michael Zangl - gsoc-core - fix #12924

  • Property svn:eol-style set to native
File size: 8.2 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.Main;
15import org.openstreetmap.josm.data.osm.DataSet;
16import org.openstreetmap.josm.data.osm.event.DataSetListenerAdapter.Listener;
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 fireConsolidatedEvents(inEDTListeners, consolidatedEvent);
89 }
90 }
91 }
92
93 public enum FireMode {
94 /**
95 * Fire in calling thread immediately.
96 */
97 IMMEDIATELY,
98 /**
99 * Fire in event dispatch thread.
100 */
101 IN_EDT,
102 /**
103 * Fire in event dispatch thread. If more than one event arrived when event queue is checked, merged them to one event
104 */
105 IN_EDT_CONSOLIDATED
106 }
107
108 private static class ListenerInfo {
109 private final DataSetListener listener;
110 private final boolean consolidate;
111
112 ListenerInfo(DataSetListener listener, boolean consolidate) {
113 this.listener = listener;
114 this.consolidate = consolidate;
115 }
116
117 @Override
118 public int hashCode() {
119 return Objects.hash(listener);
120 }
121
122 @Override
123 public boolean equals(Object o) {
124 if (this == o) return true;
125 if (o == null || getClass() != o.getClass()) return false;
126 ListenerInfo that = (ListenerInfo) o;
127 return Objects.equals(listener, that.listener);
128 }
129 }
130
131 /**
132 * Replies the unique instance.
133 * @return the unique instance
134 */
135 public static DatasetEventManager getInstance() {
136 return instance;
137 }
138
139 private final Queue<AbstractDatasetChangedEvent> eventsInEDT = new LinkedBlockingQueue<>();
140 private final CopyOnWriteArrayList<ListenerInfo> inEDTListeners = new CopyOnWriteArrayList<>();
141 private final CopyOnWriteArrayList<ListenerInfo> normalListeners = new CopyOnWriteArrayList<>();
142 private final DataSetListener myListener = new DataSetListenerAdapter(this);
143 private final Runnable edtRunnable = new EdtRunnable();
144
145 /**
146 * Constructs a new {@code DatasetEventManager}.
147 */
148 public DatasetEventManager() {
149 Main.getLayerManager().addActiveLayerChangeListener(this);
150 }
151
152 /**
153 * Register listener, that will receive events from currently active dataset
154 * @param listener the listener to be registered
155 * @param fireMode If {@link FireMode#IN_EDT} or {@link FireMode#IN_EDT_CONSOLIDATED},
156 * listener will be notified in event dispatch thread instead of thread that caused
157 * the dataset change
158 */
159 public void addDatasetListener(DataSetListener listener, FireMode fireMode) {
160 if (fireMode == FireMode.IN_EDT || fireMode == FireMode.IN_EDT_CONSOLIDATED) {
161 inEDTListeners.addIfAbsent(new ListenerInfo(listener, fireMode == FireMode.IN_EDT_CONSOLIDATED));
162 } else {
163 normalListeners.addIfAbsent(new ListenerInfo(listener, false));
164 }
165 }
166
167 public void removeDatasetListener(DataSetListener listener) {
168 ListenerInfo searchListener = new ListenerInfo(listener, false);
169 inEDTListeners.remove(searchListener);
170 normalListeners.remove(searchListener);
171 }
172
173 @Override
174 public void activeOrEditLayerChanged(ActiveLayerChangeEvent e) {
175 DataSet oldData = e.getPreviousEditDataSet();
176 if (oldData != null) {
177 oldData.removeDataSetListener(myListener);
178 }
179
180 DataSet newData = e.getSource().getEditDataSet();
181 if (newData != null) {
182 newData.addDataSetListener(myListener);
183 }
184 processDatasetEvent(new DataChangedEvent(newData));
185 }
186
187 private static void fireEvents(List<ListenerInfo> listeners, AbstractDatasetChangedEvent event) {
188 for (ListenerInfo listener: listeners) {
189 if (!listener.consolidate) {
190 event.fire(listener.listener);
191 }
192 }
193 }
194
195 private static void fireConsolidatedEvents(List<ListenerInfo> listeners, AbstractDatasetChangedEvent event) {
196 for (ListenerInfo listener: listeners) {
197 if (listener.consolidate) {
198 event.fire(listener.listener);
199 }
200 }
201 }
202
203 @Override
204 public void processDatasetEvent(AbstractDatasetChangedEvent event) {
205 fireEvents(normalListeners, event);
206 eventsInEDT.add(event);
207 SwingUtilities.invokeLater(edtRunnable);
208 }
209}
Note: See TracBrowser for help on using the repository browser.