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

Last change on this file since 6340 was 6316, checked in by Don-vip, 11 years ago

Sonar/FindBugs - Loose coupling

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