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

Last change on this file since 2920 was 2920, checked in by jttt, 17 years ago

Fixed NPE from log in #4409

  • Property svn:mime-type set to text/plain
File size: 7.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.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 IMMEDIATELY,
33 IN_EDT,
34 /**
35 * Fire in event dispatch thread. If more than one event arrived when event queue is checked, merged them to
36 * one event
37 */
38 IN_EDT_CONSOLIDATED}
39
40 private static class ListenerInfo {
41 final DataSetListener listener;
42 final boolean consolidate;
43
44 public ListenerInfo(DataSetListener listener, boolean consolidate) {
45 this.listener = listener;
46 this.consolidate = consolidate;
47 }
48
49 @Override
50 public int hashCode() {
51 return listener.hashCode();
52 }
53
54 @Override
55 public boolean equals(Object o) {
56 return o instanceof ListenerInfo && ((ListenerInfo)o).listener == listener;
57 }
58 }
59
60 public static DatasetEventManager getInstance() {
61 return instance;
62 }
63
64 private final Queue<AbstractDatasetChangedEvent> eventsInEDT = new LinkedBlockingQueue<AbstractDatasetChangedEvent>();
65 private final CopyOnWriteArrayList<ListenerInfo> inEDTListeners = new CopyOnWriteArrayList<ListenerInfo>();
66 private final CopyOnWriteArrayList<ListenerInfo> normalListeners = new CopyOnWriteArrayList<ListenerInfo>();
67 private final DataSetListener myListener = new DataSetListenerAdapter(this);
68
69 public DatasetEventManager() {
70 MapView.addEditLayerChangeListener(this);
71 }
72
73 /**
74 * Register listener, that will receive events from currently active dataset
75 * @param listener
76 * @param fireInEDT If true, listener will be notified in event dispatch thread
77 * instead of thread that caused the dataset change
78 */
79 public void addDatasetListener(DataSetListener listener, FireMode fireMode) {
80 if (fireMode == FireMode.IN_EDT || fireMode == FireMode.IN_EDT_CONSOLIDATED) {
81 inEDTListeners.addIfAbsent(new ListenerInfo(listener, fireMode == FireMode.IN_EDT_CONSOLIDATED));
82 } else {
83 normalListeners.addIfAbsent(new ListenerInfo(listener, false));
84 }
85 }
86
87 public void removeDatasetListener(DataSetListener listener) {
88 ListenerInfo searchListener = new ListenerInfo(listener, false);
89 inEDTListeners.remove(searchListener);
90 normalListeners.remove(searchListener);
91 }
92
93 public void editLayerChanged(OsmDataLayer oldLayer, OsmDataLayer newLayer) {
94 if (oldLayer != null) {
95 oldLayer.data.removeDataSetListener(myListener);
96 }
97
98 if (newLayer != null) {
99 newLayer.data.addDataSetListener(myListener);
100 processDatasetEvent(new DataChangedEvent(newLayer.data));
101 } else {
102 processDatasetEvent(new DataChangedEvent(null));
103 }
104 }
105
106 private void fireEvents(List<ListenerInfo> listeners, AbstractDatasetChangedEvent event) {
107 for (ListenerInfo listener: listeners) {
108 if (!listener.consolidate) {
109 event.fire(listener.listener);
110 }
111 }
112 }
113
114 private void fireConsolidatedEvents(List<ListenerInfo> listeners, AbstractDatasetChangedEvent event) {
115 for (ListenerInfo listener: listeners) {
116 if (listener.consolidate) {
117 event.fire(listener.listener);
118 }
119 }
120 }
121
122 public void processDatasetEvent(AbstractDatasetChangedEvent event) {
123 fireEvents(normalListeners, event);
124 eventsInEDT.add(event);
125 SwingUtilities.invokeLater(edtRunnable);
126 }
127
128 private final Runnable edtRunnable = new Runnable() {
129 public void run() {
130 while (!eventsInEDT.isEmpty()) {
131 List<AbstractDatasetChangedEvent> events = new ArrayList<AbstractDatasetChangedEvent>();
132 events.addAll(eventsInEDT);
133
134 DataSet dataSet = null;
135 AbstractDatasetChangedEvent consolidatedEvent = null;
136 AbstractDatasetChangedEvent event = null;
137
138 while ((event = eventsInEDT.poll()) != null) {
139 fireEvents(inEDTListeners, event);
140
141 // DataSet changed - fire consolidated event early
142 if (consolidatedEvent != null && dataSet != event.getDataset()) {
143 fireConsolidatedEvents(inEDTListeners, consolidatedEvent);
144 consolidatedEvent = null;
145 }
146
147 dataSet = event.getDataset();
148
149 // Build consolidated event
150 if (event instanceof DataChangedEvent) {
151 // DataChangeEvent can contains other events, so it gets special handling
152 DataChangedEvent dataEvent = (DataChangedEvent) event;
153 if (dataEvent.getEvents() == null) {
154 consolidatedEvent = dataEvent; // Dataset was completely changed, we can ignore older events
155 } else {
156 if (consolidatedEvent == null) {
157 consolidatedEvent = new DataChangedEvent(dataSet, dataEvent.getEvents());
158 } else if (consolidatedEvent instanceof DataChangedEvent) {
159 List<AbstractDatasetChangedEvent> evts = ((DataChangedEvent) consolidatedEvent).getEvents();
160 if (evts != null) {
161 evts.addAll(dataEvent.getEvents());
162 }
163 } else {
164 AbstractDatasetChangedEvent oldConsolidateEvent = consolidatedEvent;
165 consolidatedEvent = new DataChangedEvent(dataSet, dataEvent.getEvents());
166 ((DataChangedEvent) consolidatedEvent).getEvents().add(oldConsolidateEvent);
167 }
168 }
169 } else {
170 // Normal events
171 if (consolidatedEvent == null) {
172 consolidatedEvent = event;
173 } else if (consolidatedEvent instanceof DataChangedEvent) {
174 List<AbstractDatasetChangedEvent> evs = ((DataChangedEvent) consolidatedEvent).getEvents();
175 if (evs != null) {
176 evs.add(event);
177 }
178 } else {
179 consolidatedEvent = new DataChangedEvent(dataSet,
180 new ArrayList<AbstractDatasetChangedEvent>(Arrays.asList(consolidatedEvent)));
181 }
182
183 }
184 }
185
186 // Fire consolidated event
187 fireConsolidatedEvents(inEDTListeners, consolidatedEvent);
188 }
189 }
190 };
191}
Note: See TracBrowser for help on using the repository browser.