source: josm/trunk/src/org/openstreetmap/josm/data/validation/TestError.java@ 4048

Last change on this file since 4048 was 3674, checked in by bastiK, 13 years ago

Replace Bag by MultiMap.
Both collections are quite similar, but there is a technical difference: Bag is backed by an ArrayList and MultiMap by a LinkedHashSet. So with a Bag you could map a key multiple times to a certain value. I haven't found any usage where this behaviour is required or intended.
Also there could be a certain performance drawback with LinkedHashSet compared to ArrayList. However in the typical validator use case the value collection represents duplicate or crossing ways, so the number of elements is usually 1 or 2 and mostly smaller than the initial capacity (LHS=16, AL=10). (But I could have overlooked some use cases.)

  • Property svn:eol-style set to native
File size: 12.8 KB
Line 
1// License: GPL. See LICENSE file for details.
2package org.openstreetmap.josm.data.validation;
3
4import java.awt.Color;
5import java.awt.Graphics;
6import java.awt.Point;
7import java.util.Collection;
8import java.util.Collections;
9import java.util.List;
10import java.util.TreeSet;
11
12import org.openstreetmap.josm.command.Command;
13import org.openstreetmap.josm.data.osm.Node;
14import org.openstreetmap.josm.data.osm.OsmPrimitive;
15import org.openstreetmap.josm.data.osm.Relation;
16import org.openstreetmap.josm.data.osm.Way;
17import org.openstreetmap.josm.data.osm.WaySegment;
18import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
19import org.openstreetmap.josm.gui.MapView;
20
21/**
22 * Validation error
23 * @author frsantos
24 */
25public class TestError {
26 /** is this error on the ignore list */
27 private Boolean ignored = false;
28 /** Severity */
29 private Severity severity;
30 /** The error message */
31 private String message;
32 /** Deeper error description */
33 private String description;
34 private String description_en;
35 /** The affected primitives */
36 private Collection<? extends OsmPrimitive> primitives;
37 /** The primitives to be highlighted */
38 private Collection<?> highlighted;
39 /** The tester that raised this error */
40 private Test tester;
41 /** Internal code used by testers to classify errors */
42 private int code;
43 /** If this error is selected */
44 private boolean selected;
45
46 /**
47 * Constructors
48 * @param tester The tester
49 * @param severity The severity of this error
50 * @param message The error message
51 * @param primitive The affected primitive
52 * @param primitives The affected primitives
53 * @param code The test error reference code
54 */
55 public TestError(Test tester, Severity severity, String message, String description, String description_en,
56 int code, Collection<? extends OsmPrimitive> primitives, Collection<?> highlighted) {
57 this.tester = tester;
58 this.severity = severity;
59 this.message = message;
60 this.description = description;
61 this.description_en = description_en;
62 this.primitives = primitives;
63 this.highlighted = highlighted;
64 this.code = code;
65 }
66
67 public TestError(Test tester, Severity severity, String message, int code, Collection<? extends OsmPrimitive> primitives,
68 Collection<?> highlighted) {
69 this(tester, severity, message, null, null, code, primitives, highlighted);
70 }
71
72 public TestError(Test tester, Severity severity, String message, String description, String description_en,
73 int code, Collection<? extends OsmPrimitive> primitives) {
74 this(tester, severity, message, description, description_en, code, primitives, primitives);
75 }
76
77 public TestError(Test tester, Severity severity, String message, int code, Collection<? extends OsmPrimitive> primitives) {
78 this(tester, severity, message, null, null, code, primitives, primitives);
79 }
80
81 public TestError(Test tester, Severity severity, String message, int code, OsmPrimitive primitive) {
82 this(tester, severity, message, null, null, code, Collections.singletonList(primitive), Collections
83 .singletonList(primitive));
84 }
85
86 public TestError(Test tester, Severity severity, String message, String description, String description_en,
87 int code, OsmPrimitive primitive) {
88 this(tester, severity, message, description, description_en, code, Collections.singletonList(primitive));
89 }
90
91 /**
92 * Gets the error message
93 * @return the error message
94 */
95 public String getMessage() {
96 return message;
97 }
98
99 /**
100 * Gets the error message
101 * @return the error description
102 */
103 public String getDescription() {
104 return description;
105 }
106
107 /**
108 * Sets the error message
109 * @param message The error message
110 */
111 public void setMessage(String message) {
112 this.message = message;
113 }
114
115 /**
116 * Gets the list of primitives affected by this error
117 * @return the list of primitives affected by this error
118 */
119 public Collection<? extends OsmPrimitive> getPrimitives() {
120 return primitives;
121 }
122
123 /**
124 * Sets the list of primitives affected by this error
125 * @param primitives the list of primitives affected by this error
126 */
127
128 public void setPrimitives(List<OsmPrimitive> primitives) {
129 this.primitives = primitives;
130 }
131
132 /**
133 * Gets the severity of this error
134 * @return the severity of this error
135 */
136 public Severity getSeverity() {
137 return severity;
138 }
139
140 /**
141 * Sets the severity of this error
142 * @param severity the severity of this error
143 */
144 public void setSeverity(Severity severity) {
145 this.severity = severity;
146 }
147
148 /**
149 * Sets the ignore state for this error
150 */
151 public String getIgnoreState() {
152 Collection<String> strings = new TreeSet<String>();
153 String ignorestring = getIgnoreSubGroup();
154 for (OsmPrimitive o : primitives) {
155 // ignore data not yet uploaded
156 if (o.isNew())
157 return null;
158 String type = "u";
159 if (o instanceof Way) {
160 type = "w";
161 } else if (o instanceof Relation) {
162 type = "r";
163 } else if (o instanceof Node) {
164 type = "n";
165 }
166 strings.add(type + "_" + o.getId());
167 }
168 for (String o : strings) {
169 ignorestring += ":" + o;
170 }
171 return ignorestring;
172 }
173
174 public String getIgnoreSubGroup() {
175 String ignorestring = getIgnoreGroup();
176 if (description_en != null) {
177 ignorestring += "_" + description_en;
178 }
179 return ignorestring;
180 }
181
182 public String getIgnoreGroup() {
183 return Integer.toString(code);
184 }
185
186 public void setIgnored(boolean state) {
187 ignored = state;
188 }
189
190 public Boolean getIgnored() {
191 return ignored;
192 }
193
194 /**
195 * Gets the tester that raised this error
196 * @return the tester that raised this error
197 */
198 public Test getTester() {
199 return tester;
200 }
201
202 /**
203 * Gets the code
204 * @return the code
205 */
206 public int getCode() {
207 return code;
208 }
209
210 /**
211 * Returns true if the error can be fixed automatically
212 *
213 * @return true if the error can be fixed
214 */
215 public boolean isFixable() {
216 return tester != null && tester.isFixable(this);
217 }
218
219 /**
220 * Fixes the error with the appropriate command
221 *
222 * @return The command to fix the error
223 */
224 public Command getFix() {
225 if (tester == null)
226 return null;
227
228 return tester.fixError(this);
229 }
230
231 /**
232 * Paints the error on affected primitives
233 *
234 * @param g The graphics
235 * @param mv The MapView
236 */
237 public void paint(Graphics g, MapView mv) {
238 if (!ignored) {
239 PaintVisitor v = new PaintVisitor(g, mv);
240 visitHighlighted(v);
241 }
242 }
243
244 @SuppressWarnings("unchecked")
245 public void visitHighlighted(ValidatorVisitor v) {
246 for (Object o : highlighted) {
247 if (o instanceof OsmPrimitive) {
248 v.visit((OsmPrimitive) o);
249 } else if (o instanceof WaySegment) {
250 v.visit((WaySegment) o);
251 } else if (o instanceof List<?>) {
252 v.visit((List<Node>)o);
253 }
254 }
255 }
256
257 /**
258 * Visitor that highlights the primitives affected by this error
259 * @author frsantos
260 */
261 class PaintVisitor extends AbstractVisitor implements ValidatorVisitor {
262 /** The graphics */
263 private final Graphics g;
264 /** The MapView */
265 private final MapView mv;
266
267 /**
268 * Constructor
269 * @param g The graphics
270 * @param mv The Mapview
271 */
272 public PaintVisitor(Graphics g, MapView mv) {
273 this.g = g;
274 this.mv = mv;
275 }
276
277 @Override
278 public void visit(OsmPrimitive p) {
279 if (p.isUsable()) {
280 p.visit(this);
281 }
282 }
283
284 /**
285 * Draws a circle around the node
286 * @param n The node
287 * @param color The circle color
288 */
289 public void drawNode(Node n, Color color) {
290 Point p = mv.getPoint(n);
291 g.setColor(color);
292 if (selected) {
293 g.fillOval(p.x - 5, p.y - 5, 10, 10);
294 } else {
295 g.drawOval(p.x - 5, p.y - 5, 10, 10);
296 }
297 }
298
299 public void drawSegment(Point p1, Point p2, Color color) {
300 g.setColor(color);
301
302 double t = Math.atan2(p2.x - p1.x, p2.y - p1.y);
303 double cosT = Math.cos(t);
304 double sinT = Math.sin(t);
305 int deg = (int) Math.toDegrees(t);
306 if (selected) {
307 int[] x = new int[] { (int) (p1.x + 5 * cosT), (int) (p2.x + 5 * cosT),
308 (int) (p2.x - 5 * cosT), (int) (p1.x - 5 * cosT) };
309 int[] y = new int[] { (int) (p1.y - 5 * sinT), (int) (p2.y - 5 * sinT),
310 (int) (p2.y + 5 * sinT), (int) (p1.y + 5 * sinT) };
311 g.fillPolygon(x, y, 4);
312 g.fillArc(p1.x - 5, p1.y - 5, 10, 10, deg, 180);
313 g.fillArc(p2.x - 5, p2.y - 5, 10, 10, deg, -180);
314 } else {
315 g.drawLine((int) (p1.x + 5 * cosT), (int) (p1.y - 5 * sinT),
316 (int) (p2.x + 5 * cosT), (int) (p2.y - 5 * sinT));
317 g.drawLine((int) (p1.x - 5 * cosT), (int) (p1.y + 5 * sinT),
318 (int) (p2.x - 5 * cosT), (int) (p2.y + 5 * sinT));
319 g.drawArc(p1.x - 5, p1.y - 5, 10, 10, deg, 180);
320 g.drawArc(p2.x - 5, p2.y - 5, 10, 10, deg, -180);
321 }
322 }
323
324 /**
325 * Draws a line around the segment
326 *
327 * @param s The segment
328 * @param color The color
329 */
330 public void drawSegment(Node n1, Node n2, Color color) {
331 drawSegment(mv.getPoint(n1), mv.getPoint(n2), color);
332 }
333
334 /**
335 * Draw a small rectangle.
336 * White if selected (as always) or red otherwise.
337 *
338 * @param n The node to draw.
339 */
340 @Override
341 public void visit(Node n) {
342 if (isNodeVisible(n)) {
343 drawNode(n, severity.getColor());
344 }
345 }
346
347 @Override
348 public void visit(Way w) {
349 visit(w.getNodes());
350 }
351
352 @Override
353 public void visit(WaySegment ws) {
354 if (ws.lowerIndex < 0 || ws.lowerIndex + 1 >= ws.way.getNodesCount())
355 return;
356 Node a = ws.way.getNodes().get(ws.lowerIndex), b = ws.way.getNodes().get(ws.lowerIndex + 1);
357 if (isSegmentVisible(a, b)) {
358 drawSegment(a, b, severity.getColor());
359 }
360 }
361
362 @Override
363 public void visit(Relation r) {
364 /* No idea how to draw a relation. */
365 }
366
367 /**
368 * Checks if the given node is in the visible area.
369 * @param n The node to check for visibility
370 * @return true if the node is visible
371 */
372 protected boolean isNodeVisible(Node n) {
373 Point p = mv.getPoint(n);
374 return !((p.x < 0) || (p.y < 0) || (p.x > mv.getWidth()) || (p.y > mv.getHeight()));
375 }
376
377 /**
378 * Checks if the given segment is in the visible area.
379 * NOTE: This will return true for a small number of non-visible
380 * segments.
381 * @param ls The segment to check
382 * @return true if the segment is visible
383 */
384 protected boolean isSegmentVisible(Node n1, Node n2) {
385 Point p1 = mv.getPoint(n1);
386 Point p2 = mv.getPoint(n2);
387 if ((p1.x < 0) && (p2.x < 0))
388 return false;
389 if ((p1.y < 0) && (p2.y < 0))
390 return false;
391 if ((p1.x > mv.getWidth()) && (p2.x > mv.getWidth()))
392 return false;
393 if ((p1.y > mv.getHeight()) && (p2.y > mv.getHeight()))
394 return false;
395 return true;
396 }
397
398 @Override
399 public void visit(List<Node> nodes) {
400 Node lastN = null;
401 for (Node n : nodes) {
402 if (lastN == null) {
403 lastN = n;
404 continue;
405 }
406 if (n.isDrawable() && isSegmentVisible(lastN, n)) {
407 drawSegment(lastN, n, severity.getColor());
408 }
409 lastN = n;
410 }
411 }
412 }
413
414 /**
415 * Sets the selection flag of this error
416 * @param selected if this error is selected
417 */
418 public void setSelected(boolean selected) {
419 this.selected = selected;
420 }
421}
Note: See TracBrowser for help on using the repository browser.