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        s.intern();
052        return s;
053    }
054
055    public Pair<StyleElementList, Range> getWithRange(double scale, boolean selected) {
056        int idx = getIndex(selected);
057        if (states[idx] == null) {
058            return Pair.create(null, Range.ZERO_TO_INFINITY);
059        }
060        return states[idx].getWithRange(scale);
061    }
062
063    private static int getIndex(boolean selected) {
064        return selected ? SELECTED : PLAIN;
065    }
066
067    @Override
068    public int hashCode() {
069        return Arrays.deepHashCode(this.states);
070    }
071
072    @Override
073    public boolean equals(Object obj) {
074        if (obj == null) {
075            return false;
076        }
077        if (getClass() != obj.getClass()) {
078            return false;
079        }
080        final StyleCache other = (StyleCache) obj;
081        return Arrays.deepEquals(this.states, other.states);
082    }
083
084    /**
085     * Like String.intern() (reduce memory consumption).
086     * StyleCache must not be changed after it has been added to the intern pool.
087     * @return style cache
088     */
089    private StyleCache intern() {
090        return internPool.putUnique(this);
091    }
092
093    /**
094     * Get the size of the intern pool. Only for tests!
095     * @return size of the intern pool
096     */
097    public static int getInternPoolSize() {
098        return internPool.size();
099    }
100}