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

Last change on this file since 9239 was 9239, checked in by Don-vip, 8 years ago

javadoc update

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