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

Last change on this file since 11134 was 11134, checked in by simon04, 9 years ago

fix #13807 see #13799 - Regression: zoom to problem doesn't work anymore

  • Property svn:eol-style set to native
File size: 20.8 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.validation;
3
4import java.text.MessageFormat;
5import java.util.ArrayList;
6import java.util.Arrays;
7import java.util.Collection;
8import java.util.Collections;
9import java.util.List;
10import java.util.Locale;
11import java.util.TreeSet;
12import java.util.function.Supplier;
13
14import org.openstreetmap.josm.Main;
15import org.openstreetmap.josm.command.Command;
16import org.openstreetmap.josm.data.osm.Node;
17import org.openstreetmap.josm.data.osm.OsmPrimitive;
18import org.openstreetmap.josm.data.osm.Relation;
19import org.openstreetmap.josm.data.osm.Way;
20import org.openstreetmap.josm.data.osm.WaySegment;
21import org.openstreetmap.josm.data.osm.event.AbstractDatasetChangedEvent;
22import org.openstreetmap.josm.data.osm.event.DataChangedEvent;
23import org.openstreetmap.josm.data.osm.event.DataSetListener;
24import org.openstreetmap.josm.data.osm.event.NodeMovedEvent;
25import org.openstreetmap.josm.data.osm.event.PrimitivesAddedEvent;
26import org.openstreetmap.josm.data.osm.event.PrimitivesRemovedEvent;
27import org.openstreetmap.josm.data.osm.event.RelationMembersChangedEvent;
28import org.openstreetmap.josm.data.osm.event.TagsChangedEvent;
29import org.openstreetmap.josm.data.osm.event.WayNodesChangedEvent;
30import org.openstreetmap.josm.data.validation.util.MultipleNameVisitor;
31import org.openstreetmap.josm.tools.AlphanumComparator;
32import org.openstreetmap.josm.tools.CheckParameterUtil;
33import org.openstreetmap.josm.tools.I18n;
34
35/**
36 * Validation error
37 * @since 3669
38 */
39public class TestError implements Comparable<TestError>, DataSetListener {
40 /** is this error on the ignore list */
41 private boolean ignored;
42 /** Severity */
43 private Severity severity;
44 /** The error message */
45 private String message;
46 /** Deeper error description */
47 private final String description;
48 private final String descriptionEn;
49 /** The affected primitives */
50 private Collection<? extends OsmPrimitive> primitives;
51 /** The primitives or way segments to be highlighted */
52 private final Collection<?> highlighted;
53 /** The tester that raised this error */
54 private Test tester;
55 /** Internal code used by testers to classify errors */
56 private final int code;
57 /** If this error is selected */
58 private boolean selected;
59 /** Supplying a command to fix the error */
60 private final Supplier<Command> fixingCommand;
61
62 /**
63 * A builder for a {@code TestError}.
64 * @since 11129
65 */
66 public static final class Builder {
67 private final Test tester;
68 private final Severity severity;
69 private final int code;
70 private String message;
71 private String description;
72 private String descriptionEn;
73 private Collection<? extends OsmPrimitive> primitives;
74 private Collection<?> highlighted;
75 private Supplier<Command> fixingCommand;
76
77 private Builder(Test tester, Severity severity, int code) {
78 this.tester = tester;
79 this.severity = severity;
80 this.code = code;
81 }
82
83 /**
84 * Sets the error message.
85 *
86 * @param message The error message
87 * @return {@code this}
88 */
89 public Builder message(String message) {
90 this.message = message;
91 return this;
92 }
93
94 /**
95 * Sets the error message.
96 *
97 * @param message The the message of this error group
98 * @param description The translated description of this error
99 * @param descriptionEn The English description (for ignoring errors)
100 * @return {@code this}
101 */
102 public Builder messageWithManuallyTranslatedDescription(String message, String description, String descriptionEn) {
103 this.message = message;
104 this.description = description;
105 this.descriptionEn = descriptionEn;
106 return this;
107 }
108
109 /**
110 * Sets the error message.
111 *
112 * @param message The the message of this error group
113 * @param marktrDescription The {@linkplain I18n#marktr prepared for i18n} description of this error
114 * @param args The description arguments to be applied in {@link I18n#tr(String, Object...)}
115 * @return {@code this}
116 */
117 public Builder message(String message, String marktrDescription, Object... args) {
118 this.message = message;
119 this.description = I18n.tr(marktrDescription, args);
120 this.descriptionEn = new MessageFormat(marktrDescription, Locale.ENGLISH).format(args);
121 return this;
122 }
123
124 /**
125 * Sets the primitives affected by this error.
126 *
127 * @param primitives the primitives affected by this error
128 * @return {@code this}
129 */
130 public Builder primitives(OsmPrimitive... primitives) {
131 return primitives(Arrays.asList(primitives));
132 }
133
134 /**
135 * Sets the primitives affected by this error.
136 *
137 * @param primitives the primitives affected by this error
138 * @return {@code this}
139 */
140 public Builder primitives(Collection<? extends OsmPrimitive> primitives) {
141 CheckParameterUtil.ensureThat(this.primitives == null, "primitives already set");
142 this.primitives = primitives;
143 if (this.highlighted == null) {
144 this.highlighted = primitives;
145 }
146 return this;
147 }
148
149 /**
150 * Sets the primitives to highlight when selecting this error.
151 *
152 * @param highlighted the primitives to highlight
153 * @return {@code this}
154 * @see ValidatorVisitor#visit(OsmPrimitive)
155 */
156 public Builder highlight(OsmPrimitive... highlighted) {
157 return highlight(Arrays.asList(highlighted));
158 }
159
160 /**
161 * Sets the primitives to highlight when selecting this error.
162 *
163 * @param highlighted the primitives to highlight
164 * @return {@code this}
165 * @see ValidatorVisitor#visit(OsmPrimitive)
166 */
167 public Builder highlight(Collection<? extends OsmPrimitive> highlighted) {
168 this.highlighted = highlighted;
169 return this;
170 }
171
172 /**
173 * Sets the way segments to highlight when selecting this error.
174 *
175 * @param highlighted the way segments to highlight
176 * @return {@code this}
177 * @see ValidatorVisitor#visit(WaySegment)
178 */
179 public Builder highlightWaySegments(Collection<WaySegment> highlighted) {
180 this.highlighted = highlighted;
181 return this;
182 }
183
184 /**
185 * Sets the node pairs to highlight when selecting this error.
186 *
187 * @param highlighted the node pairs to highlight
188 * @return {@code this}
189 * @see ValidatorVisitor#visit(List)
190 */
191 public Builder highlightNodePairs(Collection<List<Node>> highlighted) {
192 this.highlighted = highlighted;
193 return this;
194 }
195
196 /**
197 * Sets a supplier to obtain a command to fix the error.
198 *
199 * @param fixingCommand the fix supplier
200 * @return {@code this}
201 */
202 public Builder fix(Supplier<Command> fixingCommand) {
203 CheckParameterUtil.ensureThat(this.fixingCommand == null, "fixingCommand already set");
204 this.fixingCommand = fixingCommand;
205 return this;
206 }
207
208 /**
209 * Returns a new test error with the specified values
210 *
211 * @return a new test error with the specified values
212 * @throws IllegalArgumentException when {@link #message} or {@link #primitives} is null/empty.
213 */
214 public TestError build() {
215 CheckParameterUtil.ensureParameterNotNull(message, "message not set");
216 CheckParameterUtil.ensureParameterNotNull(primitives, "primitives not set");
217 CheckParameterUtil.ensureThat(!primitives.isEmpty(), "primitives is empty");
218 if (this.highlighted == null) {
219 this.highlighted = Collections.emptySet();
220 }
221 return new TestError(this);
222 }
223 }
224
225 /**
226 * Starts building a new {@code TestError}
227 * @param tester The tester
228 * @param severity The severity of this error
229 * @param code The test error reference code
230 * @return a new test builder
231 * @since 11129
232 */
233 public static Builder builder(Test tester, Severity severity, int code) {
234 return new Builder(tester, severity, code);
235 }
236
237 private TestError(Builder builder) {
238 this.tester = builder.tester;
239 this.severity = builder.severity;
240 this.message = builder.message;
241 this.description = builder.description;
242 this.descriptionEn = builder.descriptionEn;
243 this.primitives = builder.primitives;
244 this.highlighted = builder.highlighted;
245 this.code = builder.code;
246 this.fixingCommand = builder.fixingCommand;
247 }
248
249 /**
250 * Constructs a new {@code TestError}.
251 * @param tester The tester
252 * @param severity The severity of this error
253 * @param message The error message
254 * @param description The translated description
255 * @param descriptionEn The English description
256 * @param code The test error reference code
257 * @param primitives The affected primitives
258 * @param highlighted OSM primitives to highlight
259 * @deprecated Use {@link #builder} instead. Will be removed in 2016-12.
260 */
261 @Deprecated
262 public TestError(Test tester, Severity severity, String message, String description, String descriptionEn,
263 int code, Collection<? extends OsmPrimitive> primitives, Collection<?> highlighted) {
264 this.tester = tester;
265 this.severity = severity;
266 this.message = message;
267 this.description = description;
268 this.descriptionEn = descriptionEn;
269 this.primitives = primitives;
270 this.highlighted = highlighted;
271 this.code = code;
272 this.fixingCommand = null;
273 }
274
275 /**
276 * Constructs a new {@code TestError} without description.
277 * @param tester The tester
278 * @param severity The severity of this error
279 * @param message The error message
280 * @param code The test error reference code
281 * @param primitives The affected primitives
282 * @param highlighted OSM primitives to highlight
283 * @deprecated Use {@link #builder} instead. Will be removed in 2016-12.
284 */
285 @Deprecated
286 public TestError(Test tester, Severity severity, String message, int code, Collection<? extends OsmPrimitive> primitives,
287 Collection<?> highlighted) {
288 this(tester, severity, message, null, null, code, primitives, highlighted);
289 }
290
291 /**
292 * Constructs a new {@code TestError}.
293 * @param tester The tester
294 * @param severity The severity of this error
295 * @param message The error message
296 * @param description The translated description
297 * @param descriptionEn The English description
298 * @param code The test error reference code
299 * @param primitives The affected primitives
300 * @deprecated Use {@link #builder} instead. Will be removed in 2016-12.
301 */
302 @Deprecated
303 public TestError(Test tester, Severity severity, String message, String description, String descriptionEn,
304 int code, Collection<? extends OsmPrimitive> primitives) {
305 this(tester, severity, message, description, descriptionEn, code, primitives, primitives);
306 }
307
308 /**
309 * Constructs a new {@code TestError} without description.
310 * @param tester The tester
311 * @param severity The severity of this error
312 * @param message The error message
313 * @param code The test error reference code
314 * @param primitives The affected primitives
315 * @deprecated Use {@link #builder} instead. Will be removed in 2016-12.
316 */
317 @Deprecated
318 public TestError(Test tester, Severity severity, String message, int code, Collection<? extends OsmPrimitive> primitives) {
319 this(tester, severity, message, null, null, code, primitives, primitives);
320 }
321
322 /**
323 * Constructs a new {@code TestError} without description, for a single primitive.
324 * @param tester The tester
325 * @param severity The severity of this error
326 * @param message The error message
327 * @param code The test error reference code
328 * @param primitive The affected primitive
329 * @deprecated Use {@link #builder} instead. Will be removed in 2016-12.
330 */
331 @Deprecated
332 public TestError(Test tester, Severity severity, String message, int code, OsmPrimitive primitive) {
333 this(tester, severity, message, null, null, code, Collections.singletonList(primitive), Collections
334 .singletonList(primitive));
335 }
336
337 /**
338 * Constructs a new {@code TestError} for a single primitive.
339 * @param tester The tester
340 * @param severity The severity of this error
341 * @param message The error message
342 * @param description The translated description
343 * @param descriptionEn The English description
344 * @param code The test error reference code
345 * @param primitive The affected primitive
346 * @deprecated Use {@link #builder} instead. Will be removed in 2016-12.
347 */
348 @Deprecated
349 public TestError(Test tester, Severity severity, String message, String description, String descriptionEn,
350 int code, OsmPrimitive primitive) {
351 this(tester, severity, message, description, descriptionEn, code, Collections.singletonList(primitive));
352 }
353
354 /**
355 * Gets the error message
356 * @return the error message
357 */
358 public String getMessage() {
359 return message;
360 }
361
362 /**
363 * Gets the error message
364 * @return the error description
365 */
366 public String getDescription() {
367 return description;
368 }
369
370 /**
371 * Sets the error message
372 * @param message The error message
373 * @deprecated Use {@link #builder} instead. Will be removed in 2016-12.
374 */
375 @Deprecated
376 public void setMessage(String message) {
377 this.message = message;
378 }
379
380 /**
381 * Gets the list of primitives affected by this error
382 * @return the list of primitives affected by this error
383 */
384 public Collection<? extends OsmPrimitive> getPrimitives() {
385 return primitives;
386 }
387
388 /**
389 * Gets the list of primitives affected by this error and are selectable
390 * @return the list of selectable primitives affected by this error
391 */
392 public Collection<? extends OsmPrimitive> getSelectablePrimitives() {
393 List<OsmPrimitive> selectablePrimitives = new ArrayList<>(primitives.size());
394 for (OsmPrimitive o : primitives) {
395 if (o.isSelectable()) {
396 selectablePrimitives.add(o);
397 }
398 }
399 return selectablePrimitives;
400 }
401
402 /**
403 * Sets the list of primitives affected by this error
404 * @param primitives the list of primitives affected by this error*
405 * @deprecated Use {@link #builder} instead. Will be removed in 2016-12.
406 */
407 @Deprecated
408 public void setPrimitives(List<? extends OsmPrimitive> primitives) {
409 this.primitives = primitives;
410 }
411
412 /**
413 * Gets the severity of this error
414 * @return the severity of this error
415 */
416 public Severity getSeverity() {
417 return severity;
418 }
419
420 /**
421 * Sets the severity of this error
422 * @param severity the severity of this error
423 * @deprecated Use {@link #builder} instead. Will be removed in 2016-12.
424 */
425 @Deprecated
426 public void setSeverity(Severity severity) {
427 this.severity = severity;
428 }
429
430 /**
431 * Returns the ignore state for this error.
432 * @return the ignore state for this error
433 */
434 public String getIgnoreState() {
435 Collection<String> strings = new TreeSet<>();
436 StringBuilder ignorestring = new StringBuilder(getIgnoreSubGroup());
437 for (OsmPrimitive o : primitives) {
438 // ignore data not yet uploaded
439 if (o.isNew())
440 return null;
441 String type = "u";
442 if (o instanceof Way) {
443 type = "w";
444 } else if (o instanceof Relation) {
445 type = "r";
446 } else if (o instanceof Node) {
447 type = "n";
448 }
449 strings.add(type + '_' + o.getId());
450 }
451 for (String o : strings) {
452 ignorestring.append(':').append(o);
453 }
454 return ignorestring.toString();
455 }
456
457 public String getIgnoreSubGroup() {
458 String ignorestring = getIgnoreGroup();
459 if (descriptionEn != null) {
460 ignorestring += '_' + descriptionEn;
461 }
462 return ignorestring;
463 }
464
465 public String getIgnoreGroup() {
466 return Integer.toString(code);
467 }
468
469 public void setIgnored(boolean state) {
470 ignored = state;
471 }
472
473 public boolean isIgnored() {
474 return ignored;
475 }
476
477 /**
478 * Gets the tester that raised this error
479 * @return the tester that raised this error
480 */
481 public Test getTester() {
482 return tester;
483 }
484
485 /**
486 * Set the tester that raised the error.
487 * @param tester te tester
488 * @deprecated Use {@link #builder} instead. Will be removed in 2016-12.
489 */
490 @Deprecated
491 public void setTester(Test tester) {
492 this.tester = tester;
493 }
494
495 /**
496 * Gets the code
497 * @return the code
498 */
499 public int getCode() {
500 return code;
501 }
502
503 /**
504 * Returns true if the error can be fixed automatically
505 *
506 * @return true if the error can be fixed
507 */
508 public boolean isFixable() {
509 return fixingCommand != null || ((tester != null) && tester.isFixable(this));
510 }
511
512 /**
513 * Fixes the error with the appropriate command
514 *
515 * @return The command to fix the error
516 */
517 public Command getFix() {
518 // obtain fix from the error
519 final Command fix = fixingCommand != null ? fixingCommand.get() : null;
520 if (fix != null) {
521 return fix;
522 }
523
524 // obtain fix from the tester
525 if (tester == null || !tester.isFixable(this) || primitives.isEmpty())
526 return null;
527
528 return tester.fixError(this);
529 }
530
531 /**
532 * Sets the selection flag of this error
533 * @param selected if this error is selected
534 */
535 public void setSelected(boolean selected) {
536 this.selected = selected;
537 }
538
539 @SuppressWarnings("unchecked")
540 public void visitHighlighted(ValidatorVisitor v) {
541 for (Object o : highlighted) {
542 if (o instanceof OsmPrimitive) {
543 v.visit((OsmPrimitive) o);
544 } else if (o instanceof WaySegment) {
545 v.visit((WaySegment) o);
546 } else if (o instanceof List<?>) {
547 v.visit((List<Node>) o);
548 }
549 }
550 }
551
552 /**
553 * Returns the selection flag of this error
554 * @return true if this error is selected
555 * @since 5671
556 */
557 public boolean isSelected() {
558 return selected;
559 }
560
561 /**
562 * Returns The primitives or way segments to be highlighted
563 * @return The primitives or way segments to be highlighted
564 * @since 5671
565 */
566 public Collection<?> getHighlighted() {
567 return highlighted;
568 }
569
570 @Override
571 public int compareTo(TestError o) {
572 if (equals(o)) return 0;
573
574 MultipleNameVisitor v1 = new MultipleNameVisitor();
575 MultipleNameVisitor v2 = new MultipleNameVisitor();
576
577 v1.visit(getPrimitives());
578 v2.visit(o.getPrimitives());
579 return AlphanumComparator.getInstance().compare(v1.toString(), v2.toString());
580 }
581
582 @Override public void primitivesRemoved(PrimitivesRemovedEvent event) {
583 // Remove purged primitives (fix #8639)
584 try {
585 primitives.removeAll(event.getPrimitives());
586 } catch (UnsupportedOperationException e) {
587 if (event.getPrimitives().containsAll(primitives)) {
588 primitives = Collections.emptyList();
589 } else {
590 Main.warn(e, "Unable to remove primitives from "+this+'.');
591 }
592 }
593 }
594
595 @Override public void primitivesAdded(PrimitivesAddedEvent event) {
596 // Do nothing
597 }
598
599 @Override public void tagsChanged(TagsChangedEvent event) {
600 // Do nothing
601 }
602
603 @Override public void nodeMoved(NodeMovedEvent event) {
604 // Do nothing
605 }
606
607 @Override public void wayNodesChanged(WayNodesChangedEvent event) {
608 // Do nothing
609 }
610
611 @Override public void relationMembersChanged(RelationMembersChangedEvent event) {
612 // Do nothing
613 }
614
615 @Override public void otherDatasetChange(AbstractDatasetChangedEvent event) {
616 // Do nothing
617 }
618
619 @Override public void dataChanged(DataChangedEvent event) {
620 // Do nothing
621 }
622
623 @Override
624 public String toString() {
625 return "TestError [tester=" + tester + ", code=" + code + ", message=" + message + ']';
626 }
627}
Note: See TracBrowser for help on using the repository browser.