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

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

fix sonar squid:S2039 - Member variable visibility should be specified

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