source: josm/trunk/src/org/openstreetmap/josm/gui/mappaint/StyleCache.java@ 7909

Last change on this file since 7909 was 7864, checked in by Don-vip, 9 years ago

global cleanup of IllegalArgumentExceptions thrown by JOSM

  • Property svn:eol-style set to native
File size: 7.3 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.mappaint;
3
4import java.util.ArrayList;
5import java.util.Arrays;
6import java.util.Collection;
7import java.util.Iterator;
8import java.util.List;
9import java.util.Objects;
10
11import org.openstreetmap.josm.data.osm.Storage;
12import org.openstreetmap.josm.tools.Pair;
13
14/**
15 * Caches styles for a single primitive.
16 * Splits the range of possible scale values (0 < scale < +Infinity) into multiple
17 * subranges, for each scale range it keeps a list of styles.
18 * Immutable class, equals & hashCode is required (the same for StyleList, ElemStyle
19 * and its subclasses).
20 */
21public final class StyleCache {
22 /* list of boundaries for the scale ranges */
23 private final List<Double> bd;
24 /* styles for each scale range */
25 private final List<StyleList> data;
26
27 private static final Storage<StyleCache> internPool = new Storage<>(); // TODO: clean up the intern pool from time to time (after purge or layer removal)
28
29 public static final StyleCache EMPTY_STYLECACHE = (new StyleCache()).intern();
30
31 private StyleCache() {
32 bd = new ArrayList<>();
33 bd.add(0.0);
34 bd.add(Double.POSITIVE_INFINITY);
35 data = new ArrayList<>();
36 data.add(null);
37 }
38
39 private StyleCache(StyleCache s) {
40 bd = new ArrayList<>(s.bd);
41 data = new ArrayList<>(s.data);
42 }
43
44 /**
45 * List of Styles, immutable
46 */
47 public static class StyleList implements Iterable<ElemStyle> {
48 private List<ElemStyle> lst;
49
50 /**
51 * Constructs a new {@code StyleList}.
52 */
53 public StyleList() {
54 lst = new ArrayList<>();
55 }
56
57 public StyleList(ElemStyle... init) {
58 lst = new ArrayList<>(Arrays.asList(init));
59 }
60
61 public StyleList(Collection<ElemStyle> sl) {
62 lst = new ArrayList<>(sl);
63 }
64
65 public StyleList(StyleList sl, ElemStyle s) {
66 lst = new ArrayList<>(sl.lst);
67 lst.add(s);
68 }
69
70 @Override
71 public Iterator<ElemStyle> iterator() {
72 return lst.iterator();
73 }
74
75 public boolean isEmpty() {
76 return lst.isEmpty();
77 }
78
79 public int size() {
80 return lst.size();
81 }
82
83 @Override
84 public String toString() {
85 return lst.toString();
86 }
87
88 @Override
89 public boolean equals(Object obj) {
90 if (obj == null || getClass() != obj.getClass())
91 return false;
92 final StyleList other = (StyleList) obj;
93 return Objects.equals(lst, other.lst);
94 }
95
96 @Override
97 public int hashCode() {
98 return lst.hashCode();
99 }
100 }
101
102 /**
103 * looks up styles for a certain scale value
104 */
105 public StyleList get(double scale) {
106 if (scale <= 0)
107 throw new IllegalArgumentException("scale must be <= 0 but is "+scale);
108 for (int i=0; i<data.size(); ++i) {
109 if (bd.get(i) < scale && scale <= bd.get(i+1)) {
110 return data.get(i);
111 }
112 }
113 throw new AssertionError();
114 }
115
116 /**
117 * looks up styles for a certain scale value and additionally returns
118 * the scale range for the returned styles
119 */
120 public Pair<StyleList, Range> getWithRange(double scale) {
121 if (scale <= 0)
122 throw new IllegalArgumentException("scale must be <= 0 but is "+scale);
123 for (int i=0; i<data.size(); ++i) {
124 if (bd.get(i) < scale && scale <= bd.get(i+1)) {
125 return new Pair<>(data.get(i), new Range(bd.get(i), bd.get(i+1)));
126 }
127 }
128 throw new AssertionError();
129 }
130
131 public StyleCache put(StyleList sl, Range r) {
132 return put(sl, r.getLower(), r.getUpper());
133 }
134
135 /**
136 * add a new styles to the cache. this is only possible, if
137 * for this scale range, there is nothing in the cache yet.
138 */
139 public StyleCache put(StyleList sl, double lower, double upper) {
140 StyleCache s = new StyleCache(this);
141 s.putImpl(sl, lower, upper);
142 s.consistencyTest();
143 return s.intern();
144 }
145
146 // this exception type is for debugging #8997 and can later be replaced
147 // by AssertionError
148 public static class RangeViolatedError extends Error {
149 }
150
151 /**
152 * ASCII-art explanation:
153 *
154 * data[i]
155 * --|-------|---------|--
156 * bd[i-1] bd[i] bd[i+1]
157 *
158 * (--------]
159 * lower upper
160 */
161 private void putImpl(StyleList sl, double lower, double upper) {
162 int i=0;
163 while (bd.get(i) < lower) {
164 ++i;
165 }
166 if (bd.get(i) == lower) {
167 if (upper > bd.get(i+1))
168 throw new RangeViolatedError();
169 if (data.get(i) != null)
170 throw new AssertionError("the new range must be within a subrange that has no data");
171
172 // --|-------|--------|--
173 // i-1 i i+1
174 // (--------]
175 if (bd.get(i+1) == upper) {
176 data.set(i, sl);
177 }
178 // --|-------|--------|--
179 // i-1 i i+1
180 // (-----]
181 else {
182 bd.add(i+1, upper);
183 data.add(i, sl);
184 }
185 return;
186 } else {
187 if (bd.get(i) < upper)
188 throw new AssertionError("the new range must be within a single subrange");
189 if (data.get(i-1) != null)
190 throw new AssertionError();
191
192 // --|-------|--------|--
193 // i-1 i i+1
194 // (--] or
195 // (----]
196 bd.add(i, lower);
197 data.add(i, sl);
198
199 // --|--|----|--------|--
200 // i-1 i i+1 i+2
201 // (--]
202 if (bd.get(i+1) > upper) {
203 bd.add(i+1, upper);
204 data.add(i+1, null);
205 }
206 return;
207 }
208 }
209
210 public void consistencyTest() {
211 if (bd.size() < 2) throw new AssertionError();
212 if (data.size() < 1) throw new AssertionError();
213 if (bd.size() != data.size() + 1) throw new AssertionError();
214 if (bd.get(0) != 0) throw new AssertionError();
215 if (bd.get(bd.size() - 1) != Double.POSITIVE_INFINITY) throw new AssertionError();
216 for (int i=0; i<data.size() - 1; ++i) {
217 if (bd.get(i) >= bd.get(i + 1)) throw new AssertionError();
218 }
219 }
220
221 /**
222 * Like String.intern() (reduce memory consumption).
223 * StyleCache must not be changed after it has
224 * been added to the intern pool.
225 */
226 public StyleCache intern() {
227 return internPool.putUnique(this);
228 }
229
230 @Override
231 public boolean equals(Object obj) {
232 if (obj == null || getClass() != obj.getClass())
233 return false;
234 final StyleCache other = (StyleCache) obj;
235 return bd.equals(other.bd) && data.equals(other.data);
236 }
237
238 @Override
239 public int hashCode() {
240 int hash = 7;
241 hash = 23 * hash + bd.hashCode();
242 hash = 23 * hash + data.hashCode();
243 return hash;
244 }
245
246 @Override
247 public String toString() {
248 return "SC{" + bd + ' ' + data + '}';
249 }
250}
Note: See TracBrowser for help on using the repository browser.