001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.gui.mappaint;
003
004import java.util.Arrays;
005
006import org.openstreetmap.josm.data.osm.Storage;
007import org.openstreetmap.josm.tools.Pair;
008
009/**
010 * Caches styles for a single primitive.
011 * <p>
012 * This object is immutable.
013 */
014public final class StyleCache {
015
016    // TODO: clean up the intern pool from time to time (after purge or layer removal)
017    private static final Storage<StyleCache> internPool = new Storage<>();
018
019    public static final StyleCache EMPTY_STYLECACHE = (new StyleCache()).intern();
020
021    private static final int PLAIN = 0;
022    private static final int SELECTED = 1;
023
024    @SuppressWarnings("unchecked")
025    private final DividedScale<StyleElementList>[] states = new DividedScale[2];
026
027    private StyleCache(StyleCache sc) {
028        states[0] = sc.states[0];
029        states[1] = sc.states[1];
030    }
031
032    private StyleCache() {
033    }
034
035    /**
036     * Creates a new copy of this style cache with a new entry added.
037     * @param o The style to cache.
038     * @param r The range the style is for.
039     * @param selected The style list we should use (selected/unselected)
040     * @return The new object.
041     */
042    public StyleCache put(StyleElementList o, Range r, boolean selected) {
043        StyleCache s = new StyleCache(this);
044
045        int idx = getIndex(selected);
046        DividedScale<StyleElementList> ds = s.states[idx];
047        if (ds == null) {
048            ds = new DividedScale<>();
049        }
050        s.states[idx] = ds.put(o, r);
051        return s.intern();
052    }
053
054    public Pair<StyleElementList, Range> getWithRange(double scale, boolean selected) {
055        int idx = getIndex(selected);
056        if (states[idx] == null) {
057            return Pair.create(null, Range.ZERO_TO_INFINITY);
058        }
059        return states[idx].getWithRange(scale);
060    }
061
062    private static int getIndex(boolean selected) {
063        return selected ? SELECTED : PLAIN;
064    }
065
066    @Override
067    public int hashCode() {
068        return Arrays.deepHashCode(this.states);
069    }
070
071    @Override
072    public boolean equals(Object obj) {
073        if (obj == null) {
074            return false;
075        }
076        if (getClass() != obj.getClass()) {
077            return false;
078        }
079        final StyleCache other = (StyleCache) obj;
080        return Arrays.deepEquals(this.states, other.states);
081    }
082
083    /**
084     * Like String.intern() (reduce memory consumption).
085     * StyleCache must not be changed after it has been added to the intern pool.
086     * @return style cache
087     */
088    private StyleCache intern() {
089        return internPool.putUnique(this);
090    }
091
092    /**
093     * Get the size of the intern pool. Only for tests!
094     * @return size of the intern pool
095     */
096    public static int getInternPoolSize() {
097        return internPool.size();
098    }
099}