// License: GPL. For details, see LICENSE file.
package org.openstreetmap.josm.gui.mappaint;
import java.util.Arrays;
import java.util.Optional;
import org.openstreetmap.josm.data.osm.Storage;
import org.openstreetmap.josm.tools.Pair;
/**
* Caches styles for a single primitive.
* <p>
* This object is immutable.
*/
public final class StyleCache {
// TODO: clean up the intern pool from time to time (after purge or layer removal)
private static final Storage<StyleCache> internPool = new Storage<>();
public static final StyleCache EMPTY_STYLECACHE = (new StyleCache()).intern();
private static final int PLAIN = 0;
private static final int SELECTED = 1;
@SuppressWarnings("unchecked")
private final DividedScale<StyleElementList>[] states = new DividedScale[2];
private StyleCache(StyleCache sc) {
states[0] = sc.states[0];
states[1] = sc.states[1];
}
private StyleCache() {
}
/**
* Creates a new copy of this style cache with a new entry added.
* @param o The style to cache.
* @param r The range the style is for.
* @param selected The style list we should use (selected/unselected)
* @return The new object.
*/
public StyleCache put(StyleElementList o, Range r, boolean selected) {
StyleCache s = new StyleCache(this);
int idx = getIndex(selected);
s.states[idx] = Optional.ofNullable(s.states[idx]).orElseGet(DividedScale::new).put(o, r);
return s.intern();
}
/**
* Get the style for a specific style. Returns the range as well.
* @param scale The current scale
* @param selected true to get the state for a selected element,
* @return The style and the range it is valid for.
*/
public Pair<StyleElementList, Range> getWithRange(double scale, boolean selected) {
int idx = getIndex(selected);
if (states[idx] == null) {
return Pair.create(null, Range.ZERO_TO_INFINITY);
}
return states[idx].getWithRange(scale);
}
private static int getIndex(boolean selected) {
return selected ? SELECTED : PLAIN;
}
@Override
public int hashCode() {
return Arrays.deepHashCode(this.states);
}
@Override
public boolean equals(Object obj) {
if (obj == null) {
return false;
}
if (getClass() != obj.getClass()) {
return false;
}
final StyleCache other = (StyleCache) obj;
return Arrays.deepEquals(this.states, other.states);
}
/**
* Like String.intern() (reduce memory consumption).
* StyleCache must not be changed after it has been added to the intern pool.
* @return style cache
*/
private StyleCache intern() {
return internPool.putUnique(this);
}
/**
* Clears the style cache. This should only be used for testing.
* It may be removed some day and replaced by a WeakReference implementation that automatically forgets old entries.
*/
static void clearStyleCachePool() {
internPool.clear();
}
/**
* Get the size of the intern pool. Only for tests!
* @return size of the intern pool
*/
public static int getInternPoolSize() {
return internPool.size();
}
}