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 */
012public final class StyleCache {
013
014    // TODO: clean up the intern pool from time to time (after purge or layer removal)
015    private static final Storage<StyleCache> internPool = new Storage<>();
016
017    public static final StyleCache EMPTY_STYLECACHE = (new StyleCache()).intern();
018
019    private static final int PLAIN = 0;
020    private static final int SELECTED = 1;
021
022    @SuppressWarnings("unchecked")
023    private final DividedScale<StyleElementList>[] states = new DividedScale[2];
024
025    private StyleCache(StyleCache sc) {
026        states[0] = sc.states[0];
027        states[1] = sc.states[1];
028    }
029
030    private StyleCache() {
031    }
032
033    public StyleCache put(StyleElementList o, Range r, boolean selected) {
034        StyleCache s = new StyleCache(this);
035
036        int idx = getIndex(selected);
037        DividedScale<StyleElementList> ds = s.states[idx];
038        if (ds == null) {
039            ds = s.states[idx] = new DividedScale<>();
040        }
041        ds.putImpl(o, r.getLower(), r.getUpper());
042        ds.consistencyTest();
043        s.intern();
044        return s;
045    }
046
047    public Pair<StyleElementList, Range> getWithRange(double scale, boolean selected) {
048        int idx = getIndex(selected);
049        if (states[idx] == null) {
050            return Pair.create(null, Range.ZERO_TO_INFINITY);
051        }
052        return states[idx].getWithRange(scale);
053    }
054
055    private int getIndex(boolean selected) {
056        return selected ? SELECTED : PLAIN;
057    }
058
059    @Override
060    public int hashCode() {
061        return Arrays.deepHashCode(this.states);
062    }
063
064    @Override
065    public boolean equals(Object obj) {
066        if (obj == null) {
067            return false;
068        }
069        if (getClass() != obj.getClass()) {
070            return false;
071        }
072        final StyleCache other = (StyleCache) obj;
073        return Arrays.deepEquals(this.states, other.states);
074    }
075
076    /**
077     * Like String.intern() (reduce memory consumption).
078     * StyleCache must not be changed after it has been added to the intern pool.
079     * @return style cache
080     */
081    private StyleCache intern() {
082        return internPool.putUnique(this);
083    }
084
085    /**
086     * Get the size of the intern pool. Only for tests!
087     * @return size of the intern pool
088     */
089    public static int getInternPoolSize() {
090        return internPool.size();
091    }
092}