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

Last change on this file since 11266 was 11235, checked in by bastiK, 7 years ago

applied #13948 - Validator seems to hang in last executed test (patch by Gerd Petermann)

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