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}