001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.layer; 003 004import java.util.ArrayList; 005import java.util.Collections; 006import java.util.List; 007import java.util.concurrent.CopyOnWriteArrayList; 008 009import org.openstreetmap.josm.gui.util.GuiHelper; 010import org.openstreetmap.josm.tools.Utils; 011 012/** 013 * This class handles the layer management. 014 * <p> 015 * This manager handles a list of layers with the first layer being the front layer. 016 * <h1>Threading</h1> 017 * Methods of this manager may be called from any thread in any order. 018 * Listeners are called while this layer manager is locked, so they should not block. 019 * 020 * @author Michael Zangl 021 * @since 10273 022 */ 023public class LayerManager { 024 /** 025 * Interface to notify listeners of a layer change. 026 */ 027 public interface LayerChangeListener { 028 /** 029 * Notifies this listener that a layer has been added. 030 * <p> 031 * Listeners are called in the EDT thread and you can manipulate the layer manager in the current thread. 032 * @param e The new added layer event 033 */ 034 void layerAdded(LayerAddEvent e); 035 036 /** 037 * Notifies this listener that a layer is about to be removed. 038 * <p> 039 * Listeners are called in the EDT thread and you can manipulate the layer manager in the current thread. 040 * @param e The layer to be removed (as event) 041 */ 042 void layerRemoving(LayerRemoveEvent e); 043 044 /** 045 * Notifies this listener that the order of layers was changed. 046 * <p> 047 * Listeners are called in the EDT thread and you can manipulate the layer manager in the current thread. 048 * @param e The order change event. 049 */ 050 void layerOrderChanged(LayerOrderChangeEvent e); 051 } 052 053 protected static class LayerManagerEvent { 054 private final LayerManager source; 055 056 LayerManagerEvent(LayerManager source) { 057 this.source = source; 058 } 059 060 public LayerManager getSource() { 061 return source; 062 } 063 } 064 065 /** 066 * The event that is fired whenever a layer was added. 067 * @author Michael Zangl 068 */ 069 public static class LayerAddEvent extends LayerManagerEvent { 070 private final Layer addedLayer; 071 072 LayerAddEvent(LayerManager source, Layer addedLayer) { 073 super(source); 074 this.addedLayer = addedLayer; 075 } 076 077 /** 078 * Gets the layer that was added. 079 * @return The added layer. 080 */ 081 public Layer getAddedLayer() { 082 return addedLayer; 083 } 084 } 085 086 /** 087 * The event that is fired before removing a layer. 088 * @author Michael Zangl 089 */ 090 public static class LayerRemoveEvent extends LayerManagerEvent { 091 private final Layer removedLayer; 092 093 LayerRemoveEvent(LayerManager source, Layer removedLayer) { 094 super(source); 095 this.removedLayer = removedLayer; 096 } 097 098 /** 099 * Gets the layer that is about to be removed. 100 * @return The layer. 101 */ 102 public Layer getRemovedLayer() { 103 return removedLayer; 104 } 105 } 106 107 /** 108 * An event that is fired whenever the order of layers changed. 109 * <p> 110 * We currently do not report the exact changes. 111 * @author Michael Zangl 112 */ 113 public static class LayerOrderChangeEvent extends LayerManagerEvent { 114 LayerOrderChangeEvent(LayerManager source) { 115 super(source); 116 } 117 118 } 119 120 /** 121 * This is the list of layers we manage. 122 */ 123 private final List<Layer> layers = new ArrayList<>(); 124 125 private final List<LayerChangeListener> layerChangeListeners = new CopyOnWriteArrayList<>(); 126 127 /** 128 * Add a layer. The layer will be added at a given psoition. 129 * @param layer The layer to add 130 */ 131 public void addLayer(final Layer layer) { 132 // we force this on to the EDT Thread to make events fire from there. 133 // The synchronization lock needs to be held by the EDT. 134 GuiHelper.runInEDTAndWaitWithException(new Runnable() { 135 @Override 136 public void run() { 137 realAddLayer(layer); 138 } 139 }); 140 } 141 142 protected synchronized void realAddLayer(Layer layer) { 143 if (containsLayer(layer)) { 144 throw new IllegalArgumentException("Cannot add a layer twice."); 145 } 146 LayerPositionStrategy positionStrategy = layer.getDefaultLayerPosition(); 147 int position = positionStrategy.getPosition(this); 148 checkPosition(position); 149 insertLayerAt(layer, position); 150 fireLayerAdded(layer); 151 } 152 153 /** 154 * Remove the layer from the mapview. If the layer was in the list before, 155 * an LayerChange event is fired. 156 * @param layer The layer to remove 157 */ 158 public void removeLayer(final Layer layer) { 159 // we force this on to the EDT Thread to make events fire from there. 160 // The synchronization lock needs to be held by the EDT. 161 GuiHelper.runInEDTAndWaitWithException(new Runnable() { 162 @Override 163 public void run() { 164 realRemoveLayer(layer); 165 } 166 }); 167 } 168 169 protected synchronized void realRemoveLayer(Layer layer) { 170 checkContainsLayer(layer); 171 172 fireLayerRemoving(layer); 173 layers.remove(layer); 174 } 175 176 /** 177 * Move a layer to a new position. 178 * @param layer The layer to move. 179 * @param position The position. 180 * @throws IndexOutOfBoundsException if the position is out of bounds. 181 */ 182 public void moveLayer(final Layer layer, final int position) { 183 // we force this on to the EDT Thread to make events fire from there. 184 // The synchronization lock needs to be held by the EDT. 185 GuiHelper.runInEDTAndWaitWithException(new Runnable() { 186 @Override 187 public void run() { 188 realMoveLayer(layer, position); 189 } 190 }); 191 } 192 193 protected synchronized void realMoveLayer(Layer layer, int position) { 194 checkContainsLayer(layer); 195 checkPosition(position); 196 197 int curLayerPos = layers.indexOf(layer); 198 if (position == curLayerPos) 199 return; // already in place. 200 layers.remove(curLayerPos); 201 insertLayerAt(layer, position); 202 fireLayerOrderChanged(); 203 } 204 205 /** 206 * Insert a layer at a given position. 207 * @param layer The layer to add. 208 * @param position The position on which we should add it. 209 */ 210 private void insertLayerAt(Layer layer, int position) { 211 if (position == layers.size()) { 212 layers.add(layer); 213 } else { 214 layers.add(position, layer); 215 } 216 } 217 218 /** 219 * Check if the (new) position is valid 220 * @param position The position index 221 * @throws IndexOutOfBoundsException if it is not. 222 */ 223 private void checkPosition(int position) { 224 if (position < 0 || position > layers.size()) { 225 throw new IndexOutOfBoundsException("Position " + position + " out of range."); 226 } 227 } 228 229 /** 230 * Gets an unmodifiable list of all layers that are currently in this manager. This list won't update once layers are added or removed. 231 * @return The list of layers. 232 */ 233 public List<Layer> getLayers() { 234 return Collections.unmodifiableList(new ArrayList<>(layers)); 235 } 236 237 /** 238 * Replies an unmodifiable list of layers of a certain type. 239 * 240 * Example: 241 * <pre> 242 * List<WMSLayer> wmsLayers = getLayersOfType(WMSLayer.class); 243 * </pre> 244 * @param <T> The layer type 245 * @param ofType The layer type. 246 * @return an unmodifiable list of layers of a certain type. 247 */ 248 public <T extends Layer> List<T> getLayersOfType(Class<T> ofType) { 249 return new ArrayList<>(Utils.filteredCollection(getLayers(), ofType)); 250 } 251 252 /** 253 * replies true if the list of layers managed by this map view contain layer 254 * 255 * @param layer the layer 256 * @return true if the list of layers managed by this map view contain layer 257 */ 258 public synchronized boolean containsLayer(Layer layer) { 259 return layers.contains(layer); 260 } 261 262 protected void checkContainsLayer(Layer layer) { 263 if (!containsLayer(layer)) { 264 throw new IllegalArgumentException(layer + " is not managed by us."); 265 } 266 } 267 268 /** 269 * Adds a layer change listener 270 * 271 * @param listener the listener. 272 * @throws IllegalArgumentException If the listener was added twice. 273 */ 274 public synchronized void addLayerChangeListener(LayerChangeListener listener) { 275 addLayerChangeListener(listener, false); 276 } 277 278 /** 279 * Adds a layer change listener 280 * 281 * @param listener the listener. 282 * @param fireAdd if we should fire an add event for every layer in this manager. 283 * @throws IllegalArgumentException If the listener was added twice. 284 */ 285 public synchronized void addLayerChangeListener(LayerChangeListener listener, boolean fireAdd) { 286 if (layerChangeListeners.contains(listener)) { 287 throw new IllegalArgumentException("Listener already registered."); 288 } 289 layerChangeListeners.add(listener); 290 if (fireAdd) { 291 for (Layer l : getLayers()) { 292 listener.layerAdded(new LayerAddEvent(this, l)); 293 } 294 } 295 } 296 297 /** 298 * Removes a layer change listener 299 * 300 * @param listener the listener. Ignored if null or already registered. 301 */ 302 public synchronized void removeLayerChangeListener(LayerChangeListener listener) { 303 removeLayerChangeListener(listener, false); 304 } 305 306 307 /** 308 * Removes a layer change listener 309 * 310 * @param listener the listener. 311 * @param fireRemove if we should fire a remove event for every layer in this manager. 312 */ 313 public synchronized void removeLayerChangeListener(LayerChangeListener listener, boolean fireRemove) { 314 if (!layerChangeListeners.remove(listener)) { 315 throw new IllegalArgumentException("Listener was not registered before: " + listener); 316 } else { 317 if (fireRemove) { 318 for (Layer l : getLayers()) { 319 listener.layerRemoving(new LayerRemoveEvent(this, l)); 320 } 321 } 322 } 323 } 324 325 private void fireLayerAdded(Layer layer) { 326 GuiHelper.assertCallFromEdt(); 327 LayerAddEvent e = new LayerAddEvent(this, layer); 328 for (LayerChangeListener l : layerChangeListeners) { 329 l.layerAdded(e); 330 } 331 } 332 333 private void fireLayerRemoving(Layer layer) { 334 GuiHelper.assertCallFromEdt(); 335 LayerRemoveEvent e = new LayerRemoveEvent(this, layer); 336 for (LayerChangeListener l : layerChangeListeners) { 337 l.layerRemoving(e); 338 } 339 } 340 341 private void fireLayerOrderChanged() { 342 GuiHelper.assertCallFromEdt(); 343 LayerOrderChangeEvent e = new LayerOrderChangeEvent(this); 344 for (LayerChangeListener l : layerChangeListeners) { 345 l.layerOrderChanged(e); 346 } 347 } 348}