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

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

add missing svn:eol-style=native (see #12410)

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