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

Last change on this file since 9341 was 9341, checked in by bastiK, 8 years ago

mapcss: basic support for :selected pseudoclass (see #9891)

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