source: josm/trunk/src/org/openstreetmap/josm/gui/mappaint/DividedScale.java@ 13917

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

sonar - squid:S1194 - "java.lang.Error" should not be extended

  • Property svn:eol-style set to native
File size: 6.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.List;
6import java.util.Objects;
7
8import org.openstreetmap.josm.gui.mappaint.styleelement.StyleElement;
9import org.openstreetmap.josm.tools.Pair;
10
11/**
12 * Splits the range of possible scale values (0 < scale < +Infinity) into
13 * multiple subranges, for each scale range it keeps a data object of a certain
14 * type T (can be null).
15 *
16 * Used for caching style information for different zoom levels.
17 *
18 * Immutable class, equals & hashCode is required (the same for
19 * {@link StyleElementList}, {@link StyleElement} and its subclasses).
20 *
21 * @param <T> the type of the data objects
22 */
23public class DividedScale<T> {
24
25 /**
26 * This exception type is for debugging #8997 and can later be replaced by AssertionError
27 */
28 public static class RangeViolatedError extends RuntimeException {
29 /**
30 * Constructs a new {@code RangeViolatedError}
31 * @param message error message
32 */
33 public RangeViolatedError(String message) {
34 super(message);
35 }
36 }
37
38 /* list of boundaries for the scale ranges */
39 private final List<Double> bd;
40 /* data objects for each scale range */
41 private final List<T> data;
42
43 protected DividedScale() {
44 bd = new ArrayList<>();
45 bd.add(0.0);
46 bd.add(Double.POSITIVE_INFINITY);
47 data = new ArrayList<>();
48 data.add(null);
49 }
50
51 protected DividedScale(DividedScale<T> s) {
52 bd = new ArrayList<>(s.bd);
53 data = new ArrayList<>(s.data);
54 }
55
56 /**
57 * Looks up the data object for a certain scale value.
58 *
59 * @param scale scale
60 * @return the data object at the given scale, can be null
61 */
62 public T get(double scale) {
63 if (scale <= 0)
64 throw new IllegalArgumentException("scale must be <= 0 but is "+scale);
65 for (int i = 0; i < data.size(); ++i) {
66 if (bd.get(i) < scale && scale <= bd.get(i+1)) {
67 return data.get(i);
68 }
69 }
70 throw new AssertionError();
71 }
72
73 /**
74 * Looks up the data object for a certain scale value and additionally returns
75 * the scale range where the object is valid.
76 *
77 * @param scale scale
78 * @return pair containing data object and range
79 */
80 public Pair<T, Range> getWithRange(double scale) {
81 if (scale <= 0)
82 throw new IllegalArgumentException("scale must be <= 0 but is "+scale);
83 for (int i = 0; i < data.size(); ++i) {
84 if (bd.get(i) < scale && scale <= bd.get(i+1)) {
85 return new Pair<>(data.get(i), new Range(bd.get(i), bd.get(i+1)));
86 }
87 }
88 throw new AssertionError();
89 }
90
91 /**
92 * Add data object which is valid for the given range.
93 *
94 * This is only possible, if there is no data for the given range yet.
95 *
96 * @param o data object
97 * @param r the valid range
98 * @return a new, updated, <code>DividedScale</code> object
99 */
100 public DividedScale<T> put(T o, Range r) {
101 DividedScale<T> s = new DividedScale<>(this);
102 s.putImpl(o, r.getLower(), r.getUpper());
103 s.consistencyTest();
104 return s;
105 }
106
107 /**
108 * Implementation of the <code>put</code> operation.
109 *
110 * ASCII-art explanation:
111 *
112 * data[i]
113 * --|-------|---------|--
114 * bd[i-1] bd[i] bd[i+1]
115 *
116 * (--------]
117 * lower upper
118 * @param o data object
119 * @param lower lower bound
120 * @param upper upper bound
121 */
122 private void putImpl(T o, double lower, double upper) {
123 int i = 0;
124 while (bd.get(i) < lower) {
125 ++i;
126 }
127 if (bd.get(i) == lower) {
128 if (upper > bd.get(i+1))
129 throw new RangeViolatedError("the new range must be within a single subrange (1)");
130 if (data.get(i) != null)
131 throw new RangeViolatedError("the new range must be within a subrange that has no data");
132
133 if (bd.get(i+1) == upper) {
134 // --|-------|--------|--
135 // i-1 i i+1
136 // (--------]
137 data.set(i, o);
138 } else {
139 // --|-------|--------|--
140 // i-1 i i+1
141 // (-----]
142 bd.add(i+1, upper);
143 data.add(i, o);
144 }
145 } else {
146 if (bd.get(i) < upper)
147 throw new RangeViolatedError("the new range must be within a single subrange (2)");
148 if (data.get(i-1) != null)
149 throw new AssertionError();
150
151 // --|-------|--------|--
152 // i-1 i i+1
153 // (--] or
154 // (----]
155 bd.add(i, lower);
156 data.add(i, o);
157
158 // --|--|----|--------|--
159 // i-1 i i+1 i+2
160 // (--]
161 if (bd.get(i+1) > upper) {
162 bd.add(i+1, upper);
163 data.add(i+1, null);
164 }
165 }
166 }
167
168 /**
169 * Runs a consistency test.
170 * @throws AssertionError When an invariant is broken.
171 */
172 public void consistencyTest() {
173 if (bd.size() < 2) throw new AssertionError(bd);
174 if (data.isEmpty()) throw new AssertionError(data);
175 if (bd.size() != data.size() + 1) throw new AssertionError();
176 if (bd.get(0) != 0) throw new AssertionError();
177 if (bd.get(bd.size() - 1) != Double.POSITIVE_INFINITY) throw new AssertionError();
178 for (int i = 0; i < data.size() - 1; ++i) {
179 if (bd.get(i) >= bd.get(i + 1)) throw new AssertionError();
180 }
181 }
182
183 @Override
184 public boolean equals(Object obj) {
185 if (this == obj) return true;
186 if (obj == null || getClass() != obj.getClass()) return false;
187 DividedScale<?> that = (DividedScale<?>) obj;
188 return Objects.equals(bd, that.bd) &&
189 Objects.equals(data, that.data);
190 }
191
192 @Override
193 public int hashCode() {
194 return Objects.hash(bd, data);
195 }
196
197 @Override
198 public String toString() {
199 return "DS{" + bd + ' ' + data + '}';
200 }
201}
Note: See TracBrowser for help on using the repository browser.