001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.layer;
003
004import java.util.List;
005import java.util.Objects;
006import java.util.function.Predicate;
007
008/**
009 * This class defines a position to insert a given layer in the list of layers.
010 * @author Michael Zangl
011 * @since 10008
012 * @since 10592 functional interface
013 */
014@FunctionalInterface
015public interface LayerPositionStrategy {
016
017    /**
018     * always inserts at the front of the stack.
019     */
020    LayerPositionStrategy IN_FRONT = manager -> 0;
021
022    /**
023     * A GPX layer is added below the lowest data layer.
024     */
025    LayerPositionStrategy AFTER_LAST_DATA_LAYER = afterLast(
026            layer -> layer instanceof OsmDataLayer || layer instanceof ValidatorLayer);
027
028    /**
029     * A normal layer is added after all validation layers.
030     */
031    LayerPositionStrategy AFTER_LAST_VALIDATION_LAYER = afterLast(
032            layer -> layer instanceof ValidatorLayer);
033
034    /**
035     * The default for background layers: They are added before the first background layer in the list.
036     * If there is none, they are added at the end of the list.
037     */
038    LayerPositionStrategy BEFORE_FIRST_BACKGROUND_LAYER = inFrontOfFirst(
039            Layer::isBackgroundLayer);
040
041    /**
042     * Gets a {@link LayerPositionStrategy} that inserts this layer in front of a given layer
043     * @param other The layer before which to insert this layer
044     * @return The strategy
045     */
046    static LayerPositionStrategy inFrontOf(Layer other) {
047        return inFrontOfFirst(obj -> Objects.equals(obj, other));
048    }
049
050    /**
051     * Gets a {@link LayerPositionStrategy} that inserts the layer in front of the first layer that matches a condition.
052     * @param what The condition to match.
053     * @return The strategy.
054     */
055    static LayerPositionStrategy inFrontOfFirst(final Predicate<Layer> what) {
056        return manager -> {
057            if (manager != null) {
058                List<Layer> layers = manager.getLayers();
059                for (int i = 0; i < layers.size(); i++) {
060                    if (what.test(layers.get(i))) {
061                        return i;
062                    }
063                }
064                return layers.size();
065            }
066            return 0;
067        };
068    }
069
070    /**
071     * Creates a strategy that places the layer after the last layer of a given kind or at the beginning of the list if no such layer exists.
072     * @param what what to search for
073     * @return The strategy.
074     */
075    static LayerPositionStrategy afterLast(final Predicate<Layer> what) {
076        return manager -> {
077            if (manager != null) {
078                List<Layer> layers = manager.getLayers();
079                for (int i = layers.size() - 1; i >= 0; i--) {
080                    if (what.test(layers.get(i))) {
081                        return i + 1;
082                    }
083                }
084            }
085            return 0;
086        };
087    }
088
089    /**
090     * Gets the position where the layer should be inserted
091     * @param manager The layer manager to insert the layer in.
092     * @return The position in the range 0...layers.size
093     */
094    int getPosition(LayerManager manager);
095}