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}