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

Last change on this file since 12718 was 12390, checked in by michael2402, 7 years ago

See #14794: Document data.validation package and subpackages.

  • Property svn:eol-style set to native
File size: 13.9 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.data.validation;
3
4import java.text.MessageFormat;
5import java.util.Arrays;
6import java.util.Collection;
7import java.util.Collections;
8import java.util.List;
9import java.util.Locale;
10import java.util.TreeSet;
11import java.util.function.Supplier;
12
13import org.openstreetmap.josm.command.Command;
14import org.openstreetmap.josm.data.osm.Node;
15import org.openstreetmap.josm.data.osm.OsmPrimitive;
16import org.openstreetmap.josm.data.osm.Relation;
17import org.openstreetmap.josm.data.osm.Way;
18import org.openstreetmap.josm.data.osm.WaySegment;
19import org.openstreetmap.josm.data.validation.util.MultipleNameVisitor;
20import org.openstreetmap.josm.tools.AlphanumComparator;
21import org.openstreetmap.josm.tools.CheckParameterUtil;
22import org.openstreetmap.josm.tools.I18n;
23
24/**
25 * Validation error
26 * @since 3669
27 */
28public class TestError implements Comparable<TestError> {
29 /** is this error on the ignore list */
30 private boolean ignored;
31 /** Severity */
32 private final Severity severity;
33 /** The error message */
34 private final String message;
35 /** Deeper error description */
36 private final String description;
37 private final String descriptionEn;
38 /** The affected primitives */
39 private final Collection<? extends OsmPrimitive> primitives;
40 /** The primitives or way segments to be highlighted */
41 private final Collection<?> highlighted;
42 /** The tester that raised this error */
43 private final Test tester;
44 /** Internal code used by testers to classify errors */
45 private final int code;
46 /** If this error is selected */
47 private boolean selected;
48 /** Supplying a command to fix the error */
49 private final Supplier<Command> fixingCommand;
50
51 /**
52 * A builder for a {@code TestError}.
53 * @since 11129
54 */
55 public static final class Builder {
56 private final Test tester;
57 private final Severity severity;
58 private final int code;
59 private String message;
60 private String description;
61 private String descriptionEn;
62 private Collection<? extends OsmPrimitive> primitives;
63 private Collection<?> highlighted;
64 private Supplier<Command> fixingCommand;
65
66 Builder(Test tester, Severity severity, int code) {
67 this.tester = tester;
68 this.severity = severity;
69 this.code = code;
70 }
71
72 /**
73 * Sets the error message.
74 *
75 * @param message The error message
76 * @return {@code this}
77 */
78 public Builder message(String message) {
79 this.message = message;
80 return this;
81 }
82
83 /**
84 * Sets the error message.
85 *
86 * @param message The the message of this error group
87 * @param description The translated description of this error
88 * @param descriptionEn The English description (for ignoring errors)
89 * @return {@code this}
90 */
91 public Builder messageWithManuallyTranslatedDescription(String message, String description, String descriptionEn) {
92 this.message = message;
93 this.description = description;
94 this.descriptionEn = descriptionEn;
95 return this;
96 }
97
98 /**
99 * Sets the error message.
100 *
101 * @param message The the message of this error group
102 * @param marktrDescription The {@linkplain I18n#marktr prepared for i18n} description of this error
103 * @param args The description arguments to be applied in {@link I18n#tr(String, Object...)}
104 * @return {@code this}
105 */
106 public Builder message(String message, String marktrDescription, Object... args) {
107 this.message = message;
108 this.description = I18n.tr(marktrDescription, args);
109 this.descriptionEn = new MessageFormat(marktrDescription, Locale.ENGLISH).format(args);
110 return this;
111 }
112
113 /**
114 * Sets the primitives affected by this error.
115 *
116 * @param primitives the primitives affected by this error
117 * @return {@code this}
118 */
119 public Builder primitives(OsmPrimitive... primitives) {
120 return primitives(Arrays.asList(primitives));
121 }
122
123 /**
124 * Sets the primitives affected by this error.
125 *
126 * @param primitives the primitives affected by this error
127 * @return {@code this}
128 */
129 public Builder primitives(Collection<? extends OsmPrimitive> primitives) {
130 CheckParameterUtil.ensureThat(this.primitives == null, "primitives already set");
131 CheckParameterUtil.ensureParameterNotNull(primitives, "primitives");
132 this.primitives = primitives;
133 if (this.highlighted == null) {
134 this.highlighted = primitives;
135 }
136 return this;
137 }
138
139 /**
140 * Sets the primitives to highlight when selecting this error.
141 *
142 * @param highlighted the primitives to highlight
143 * @return {@code this}
144 * @see ValidatorVisitor#visit(OsmPrimitive)
145 */
146 public Builder highlight(OsmPrimitive... highlighted) {
147 return highlight(Arrays.asList(highlighted));
148 }
149
150 /**
151 * Sets the primitives to highlight when selecting this error.
152 *
153 * @param highlighted the primitives to highlight
154 * @return {@code this}
155 * @see ValidatorVisitor#visit(OsmPrimitive)
156 */
157 public Builder highlight(Collection<? extends OsmPrimitive> highlighted) {
158 CheckParameterUtil.ensureParameterNotNull(highlighted, "highlighted");
159 this.highlighted = highlighted;
160 return this;
161 }
162
163 /**
164 * Sets the way segments to highlight when selecting this error.
165 *
166 * @param highlighted the way segments to highlight
167 * @return {@code this}
168 * @see ValidatorVisitor#visit(WaySegment)
169 */
170 public Builder highlightWaySegments(Collection<WaySegment> highlighted) {
171 CheckParameterUtil.ensureParameterNotNull(highlighted, "highlighted");
172 this.highlighted = highlighted;
173 return this;
174 }
175
176 /**
177 * Sets the node pairs to highlight when selecting this error.
178 *
179 * @param highlighted the node pairs to highlight
180 * @return {@code this}
181 * @see ValidatorVisitor#visit(List)
182 */
183 public Builder highlightNodePairs(Collection<List<Node>> highlighted) {
184 CheckParameterUtil.ensureParameterNotNull(highlighted, "highlighted");
185 this.highlighted = highlighted;
186 return this;
187 }
188
189 /**
190 * Sets a supplier to obtain a command to fix the error.
191 *
192 * @param fixingCommand the fix supplier
193 * @return {@code this}
194 */
195 public Builder fix(Supplier<Command> fixingCommand) {
196 CheckParameterUtil.ensureThat(this.fixingCommand == null, "fixingCommand already set");
197 this.fixingCommand = fixingCommand;
198 return this;
199 }
200
201 /**
202 * Returns a new test error with the specified values
203 *
204 * @return a new test error with the specified values
205 * @throws IllegalArgumentException when {@link #message} or {@link #primitives} is null/empty.
206 */
207 public TestError build() {
208 CheckParameterUtil.ensureParameterNotNull(message, "message not set");
209 CheckParameterUtil.ensureParameterNotNull(primitives, "primitives not set");
210 CheckParameterUtil.ensureThat(!primitives.isEmpty(), "primitives is empty");
211 if (this.highlighted == null) {
212 this.highlighted = Collections.emptySet();
213 }
214 return new TestError(this);
215 }
216 }
217
218 /**
219 * Starts building a new {@code TestError}
220 * @param tester The tester
221 * @param severity The severity of this error
222 * @param code The test error reference code
223 * @return a new test builder
224 * @since 11129
225 */
226 public static Builder builder(Test tester, Severity severity, int code) {
227 return new Builder(tester, severity, code);
228 }
229
230 TestError(Builder builder) {
231 this.tester = builder.tester;
232 this.severity = builder.severity;
233 this.message = builder.message;
234 this.description = builder.description;
235 this.descriptionEn = builder.descriptionEn;
236 this.primitives = builder.primitives;
237 this.highlighted = builder.highlighted;
238 this.code = builder.code;
239 this.fixingCommand = builder.fixingCommand;
240 }
241
242 /**
243 * Gets the error message
244 * @return the error message
245 */
246 public String getMessage() {
247 return message;
248 }
249
250 /**
251 * Gets the error message
252 * @return the error description
253 */
254 public String getDescription() {
255 return description;
256 }
257
258 /**
259 * Gets the list of primitives affected by this error
260 * @return the list of primitives affected by this error
261 */
262 public Collection<? extends OsmPrimitive> getPrimitives() {
263 return Collections.unmodifiableCollection(primitives);
264 }
265
266 /**
267 * Gets the severity of this error
268 * @return the severity of this error
269 */
270 public Severity getSeverity() {
271 return severity;
272 }
273
274 /**
275 * Returns the ignore state for this error.
276 * @return the ignore state for this error
277 */
278 public String getIgnoreState() {
279 Collection<String> strings = new TreeSet<>();
280 StringBuilder ignorestring = new StringBuilder(getIgnoreSubGroup());
281 for (OsmPrimitive o : primitives) {
282 // ignore data not yet uploaded
283 if (o.isNew())
284 return null;
285 String type = "u";
286 if (o instanceof Way) {
287 type = "w";
288 } else if (o instanceof Relation) {
289 type = "r";
290 } else if (o instanceof Node) {
291 type = "n";
292 }
293 strings.add(type + '_' + o.getId());
294 }
295 for (String o : strings) {
296 ignorestring.append(':').append(o);
297 }
298 return ignorestring.toString();
299 }
300
301 /**
302 * Gets the ignores subgroup that is more specialized than {@link #getIgnoreGroup()}
303 * @return The ignore sub group
304 */
305 public String getIgnoreSubGroup() {
306 String ignorestring = getIgnoreGroup();
307 if (descriptionEn != null) {
308 ignorestring += '_' + descriptionEn;
309 }
310 return ignorestring;
311 }
312
313 /**
314 * Gets the ignore group ID that is used to allow the user to ignore all same errors
315 * @return The group id
316 * @see TestError#getIgnoreSubGroup()
317 */
318 public String getIgnoreGroup() {
319 return Integer.toString(code);
320 }
321
322 /**
323 * Flags this error as ignored
324 * @param state The ignore flag
325 */
326 public void setIgnored(boolean state) {
327 ignored = state;
328 }
329
330 /**
331 * Checks if this error is ignored
332 * @return <code>true</code> if it is ignored
333 */
334 public boolean isIgnored() {
335 return ignored;
336 }
337
338 /**
339 * Gets the tester that raised this error
340 * @return the tester that raised this error
341 */
342 public Test getTester() {
343 return tester;
344 }
345
346 /**
347 * Gets the code
348 * @return the code
349 */
350 public int getCode() {
351 return code;
352 }
353
354 /**
355 * Returns true if the error can be fixed automatically
356 *
357 * @return true if the error can be fixed
358 */
359 public boolean isFixable() {
360 return fixingCommand != null || ((tester != null) && tester.isFixable(this));
361 }
362
363 /**
364 * Fixes the error with the appropriate command
365 *
366 * @return The command to fix the error
367 */
368 public Command getFix() {
369 // obtain fix from the error
370 final Command fix = fixingCommand != null ? fixingCommand.get() : null;
371 if (fix != null) {
372 return fix;
373 }
374
375 // obtain fix from the tester
376 if (tester == null || !tester.isFixable(this) || primitives.isEmpty())
377 return null;
378
379 return tester.fixError(this);
380 }
381
382 /**
383 * Sets the selection flag of this error
384 * @param selected if this error is selected
385 */
386 public void setSelected(boolean selected) {
387 this.selected = selected;
388 }
389
390 /**
391 * Visits all highlighted validation elements
392 * @param v The visitor that should receive a visit-notification on all highlighted elements
393 */
394 @SuppressWarnings("unchecked")
395 public void visitHighlighted(ValidatorVisitor v) {
396 for (Object o : highlighted) {
397 if (o instanceof OsmPrimitive) {
398 v.visit((OsmPrimitive) o);
399 } else if (o instanceof WaySegment) {
400 v.visit((WaySegment) o);
401 } else if (o instanceof List<?>) {
402 v.visit((List<Node>) o);
403 }
404 }
405 }
406
407 /**
408 * Returns the selection flag of this error
409 * @return true if this error is selected
410 * @since 5671
411 */
412 public boolean isSelected() {
413 return selected;
414 }
415
416 /**
417 * Returns The primitives or way segments to be highlighted
418 * @return The primitives or way segments to be highlighted
419 * @since 5671
420 */
421 public Collection<?> getHighlighted() {
422 return Collections.unmodifiableCollection(highlighted);
423 }
424
425 @Override
426 public int compareTo(TestError o) {
427 if (equals(o)) return 0;
428
429 MultipleNameVisitor v1 = new MultipleNameVisitor();
430 MultipleNameVisitor v2 = new MultipleNameVisitor();
431
432 v1.visit(getPrimitives());
433 v2.visit(o.getPrimitives());
434 return AlphanumComparator.getInstance().compare(v1.toString(), v2.toString());
435 }
436
437 @Override
438 public String toString() {
439 return "TestError [tester=" + tester + ", code=" + code + ", message=" + message + ']';
440 }
441}
Note: See TracBrowser for help on using the repository browser.