Changeset 11129 in josm for trunk/src/org/openstreetmap


Ignore:
Timestamp:
2016-10-15T00:17:47+02:00 (8 years ago)
Author:
simon04
Message:

fix #13799 - Use builder pattern for TestError

Location:
trunk/src/org/openstreetmap/josm/data/validation
Files:
1 deleted
33 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/validation/TestError.java

    r10880 r11129  
    22package org.openstreetmap.josm.data.validation;
    33
     4import java.text.MessageFormat;
    45import java.util.ArrayList;
     6import java.util.Arrays;
    57import java.util.Collection;
    68import java.util.Collections;
    79import java.util.List;
     10import java.util.Locale;
    811import java.util.TreeSet;
     12import java.util.function.Supplier;
    913
    1014import org.openstreetmap.josm.Main;
     
    2630import org.openstreetmap.josm.data.validation.util.MultipleNameVisitor;
    2731import org.openstreetmap.josm.tools.AlphanumComparator;
     32import org.openstreetmap.josm.tools.CheckParameterUtil;
     33import org.openstreetmap.josm.tools.I18n;
    2834
    2935/**
     
    3945    private String message;
    4046    /** Deeper error description */
    41     private String description;
    42     private String descriptionEn;
     47    private final String description;
     48    private final String descriptionEn;
    4349    /** The affected primitives */
    4450    private Collection<? extends OsmPrimitive> primitives;
    4551    /** The primitives or way segments to be highlighted */
    46     private Collection<?> highlighted;
     52    private final Collection<?> highlighted;
    4753    /** The tester that raised this error */
    4854    private Test tester;
    4955    /** Internal code used by testers to classify errors */
    50     private int code;
     56    private final int code;
    5157    /** If this error is selected */
    5258    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            return this;
     144        }
     145
     146        /**
     147         * Sets the primitives to highlight when selecting this error.
     148         *
     149         * @param highlighted the primitives to highlight
     150         * @return {@code this}
     151         * @see ValidatorVisitor#visit(OsmPrimitive)
     152         */
     153        public Builder highlight(OsmPrimitive... highlighted) {
     154            return highlight(Arrays.asList(highlighted));
     155        }
     156
     157        /**
     158         * Sets the primitives to highlight when selecting this error.
     159         *
     160         * @param highlighted the primitives to highlight
     161         * @return {@code this}
     162         * @see ValidatorVisitor#visit(OsmPrimitive)
     163         */
     164        public Builder highlight(Collection<? extends OsmPrimitive> highlighted) {
     165            CheckParameterUtil.ensureThat(this.highlighted != null, "highlighted already set");
     166            this.highlighted = highlighted;
     167            return this;
     168        }
     169
     170        /**
     171         * Sets the way segments to highlight when selecting this error.
     172         *
     173         * @param highlighted the way segments to highlight
     174         * @return {@code this}
     175         * @see ValidatorVisitor#visit(WaySegment)
     176         */
     177        public Builder highlightWaySegments(Collection<WaySegment> highlighted) {
     178            CheckParameterUtil.ensureThat(this.highlighted != null, "highlighted already set");
     179            this.highlighted = highlighted;
     180            return this;
     181        }
     182
     183        /**
     184         * Sets the node pairs to highlight when selecting this error.
     185         *
     186         * @param highlighted the node pairs to highlight
     187         * @return {@code this}
     188         * @see ValidatorVisitor#visit(List)
     189         */
     190        public Builder highlightNodePairs(Collection<List<Node>> highlighted) {
     191            CheckParameterUtil.ensureThat(this.highlighted != null, "highlighted already set");
     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    }
    53248
    54249    /**
     
    62257     * @param primitives The affected primitives
    63258     * @param highlighted OSM primitives to highlight
    64      */
     259     * @deprecated Use {@link #builder} instead. Will be removed in 2016-12.
     260     */
     261    @Deprecated
    65262    public TestError(Test tester, Severity severity, String message, String description, String descriptionEn,
    66263            int code, Collection<? extends OsmPrimitive> primitives, Collection<?> highlighted) {
     
    73270        this.highlighted = highlighted;
    74271        this.code = code;
     272        this.fixingCommand = null;
    75273    }
    76274
     
    83281     * @param primitives The affected primitives
    84282     * @param highlighted OSM primitives to highlight
    85      */
     283     * @deprecated Use {@link #builder} instead. Will be removed in 2016-12.
     284     */
     285    @Deprecated
    86286    public TestError(Test tester, Severity severity, String message, int code, Collection<? extends OsmPrimitive> primitives,
    87287            Collection<?> highlighted) {
     
    98298     * @param code The test error reference code
    99299     * @param primitives The affected primitives
    100      */
     300     * @deprecated Use {@link #builder} instead. Will be removed in 2016-12.
     301     */
     302    @Deprecated
    101303    public TestError(Test tester, Severity severity, String message, String description, String descriptionEn,
    102304            int code, Collection<? extends OsmPrimitive> primitives) {
     
    111313     * @param code The test error reference code
    112314     * @param primitives The affected primitives
    113      */
     315     * @deprecated Use {@link #builder} instead. Will be removed in 2016-12.
     316     */
     317    @Deprecated
    114318    public TestError(Test tester, Severity severity, String message, int code, Collection<? extends OsmPrimitive> primitives) {
    115319        this(tester, severity, message, null, null, code, primitives, primitives);
     
    123327     * @param code The test error reference code
    124328     * @param primitive The affected primitive
    125      */
     329     * @deprecated Use {@link #builder} instead. Will be removed in 2016-12.
     330     */
     331    @Deprecated
    126332    public TestError(Test tester, Severity severity, String message, int code, OsmPrimitive primitive) {
    127333        this(tester, severity, message, null, null, code, Collections.singletonList(primitive), Collections
     
    138344     * @param code The test error reference code
    139345     * @param primitive The affected primitive
    140      */
     346     * @deprecated Use {@link #builder} instead. Will be removed in 2016-12.
     347     */
     348    @Deprecated
    141349    public TestError(Test tester, Severity severity, String message, String description, String descriptionEn,
    142350            int code, OsmPrimitive primitive) {
     
    163371     * Sets the error message
    164372     * @param message The error message
    165      */
     373     * @deprecated Use {@link #builder} instead. Will be removed in 2016-12.
     374     */
     375    @Deprecated
    166376    public void setMessage(String message) {
    167377        this.message = message;
     
    192402    /**
    193403     * Sets the list of primitives affected by this error
    194      * @param primitives the list of primitives affected by this error
    195      */
     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
    196408    public void setPrimitives(List<? extends OsmPrimitive> primitives) {
    197409        this.primitives = primitives;
     
    209421     * Sets the severity of this error
    210422     * @param severity the severity of this error
    211      */
     423     * @deprecated Use {@link #builder} instead. Will be removed in 2016-12.
     424     */
     425    @Deprecated
    212426    public void setSeverity(Severity severity) {
    213427        this.severity = severity;
     
    272486     * Set the tester that raised the error.
    273487     * @param tester te tester
    274      */
     488     * @deprecated Use {@link #builder} instead. Will be removed in 2016-12.
     489     */
     490    @Deprecated
    275491    public void setTester(Test tester) {
    276492        this.tester = tester;
     
    291507     */
    292508    public boolean isFixable() {
    293         return tester != null && tester.isFixable(this);
     509        return fixingCommand != null || ((tester != null) && tester.isFixable(this));
    294510    }
    295511
     
    300516     */
    301517    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
    302525        if (tester == null || !tester.isFixable(this) || primitives.isEmpty())
    303526            return null;
  • trunk/src/org/openstreetmap/josm/data/validation/tests/Addresses.java

    r10657 r11129  
    77import java.util.ArrayList;
    88import java.util.Collection;
    9 import java.util.Collections;
    109import java.util.HashMap;
    1110import java.util.HashSet;
     
    5150    // CHECKSTYLE.ON: SingleSpaceSeparator
    5251
    53     protected static class AddressError extends TestError {
    54 
    55         public AddressError(Addresses tester, int code, OsmPrimitive p, String message) {
    56             this(tester, code, Collections.singleton(p), message);
    57         }
    58 
    59         public AddressError(Addresses tester, int code, Collection<OsmPrimitive> collection, String message) {
    60             this(tester, code, collection, message, null, null);
    61         }
    62 
    63         public AddressError(Addresses tester, int code, Collection<OsmPrimitive> collection, String message,
    64                 String description, String englishDescription) {
    65             this(tester, code, Severity.WARNING, collection, message, description, englishDescription);
    66         }
    67 
    68         public AddressError(Addresses tester, int code, Severity severity, Collection<OsmPrimitive> collection, String message,
    69                 String description, String englishDescription) {
    70             super(tester, severity, message, description, englishDescription, code, collection);
    71         }
    72     }
    73 
    7452    /**
    7553     * Constructor
     
    9876            List<OsmPrimitive> errorList = new ArrayList<>(list);
    9977            errorList.add(0, p);
    100             errors.add(new AddressError(this, MULTIPLE_STREET_RELATIONS, level, errorList,
    101                     tr("Multiple associatedStreet relations"), null, null));
     78            errors.add(TestError.builder(this, level, MULTIPLE_STREET_RELATIONS)
     79                    .message(tr("Multiple associatedStreet relations"))
     80                    .primitives(errorList)
     81                    .build());
    10282        }
    10383        return list;
     
    11999            }
    120100            // No street found
    121             errors.add(new AddressError(this, HOUSE_NUMBER_WITHOUT_STREET, p, tr("House number without street")));
     101            errors.add(TestError.builder(this, Severity.WARNING, HOUSE_NUMBER_WITHOUT_STREET)
     102                    .message(tr("House number without street"))
     103                    .primitives(p)
     104                    .build());
    122105        }
    123106    }
     
    179162            }
    180163            // Report duplicate house numbers
    181             String englishDescription = marktr("House number ''{0}'' duplicated");
    182164            for (Entry<String, List<OsmPrimitive>> entry : map.entrySet()) {
    183165                List<OsmPrimitive> list = entry.getValue();
    184166                if (list.size() > 1) {
    185                     errors.add(new AddressError(this, DUPLICATE_HOUSE_NUMBER, list,
    186                             tr("Duplicate house numbers"), tr(englishDescription, entry.getKey()), englishDescription));
     167                    errors.add(TestError.builder(this, Severity.WARNING, DUPLICATE_HOUSE_NUMBER)
     168                            .message(tr("Duplicate house numbers"), marktr("House number ''{0}'' duplicated"), entry.getKey())
     169                            .primitives(list)
     170                            .build());
    187171                }
    188172            }
    189173            // Report wrong street names
    190174            if (!wrongStreetNames.isEmpty()) {
    191                 errors.add(new AddressError(this, MULTIPLE_STREET_NAMES, wrongStreetNames,
    192                         tr("Multiple street names in relation")));
     175                errors.add(TestError.builder(this, Severity.WARNING, MULTIPLE_STREET_NAMES)
     176                        .message(tr("Multiple street names in relation"))
     177                        .primitives(wrongStreetNames)
     178                        .build());
    193179            }
    194180            // Report addresses too far away
     
    245231        List<OsmPrimitive> errorList = new ArrayList<>(street);
    246232        errorList.add(0, house);
    247         errors.add(new AddressError(this, HOUSE_NUMBER_TOO_FAR, errorList,
    248                 tr("House number too far from street")));
     233        errors.add(TestError.builder(this, Severity.WARNING, HOUSE_NUMBER_TOO_FAR)
     234                .message(tr("House number too far from street"))
     235                .primitives(errorList)
     236                .build());
    249237    }
    250238}
  • trunk/src/org/openstreetmap/josm/data/validation/tests/ApiCapabilitiesTest.java

    r7937 r11129  
    5050                message = tr("Way contains more than {0} nodes. It should be split or simplified", maxNodes);
    5151            }
    52             errors.add(new TestError(this, Severity.ERROR, message, MAX_WAY_NODES_ERROR, w));
     52            errors.add(TestError.builder(this, Severity.ERROR, MAX_WAY_NODES_ERROR)
     53                    .message(message)
     54                    .primitives(w)
     55                    .build());
    5356        }
    5457    }
  • trunk/src/org/openstreetmap/josm/data/validation/tests/BarriersEntrances.java

    r8378 r11129  
    3333                }
    3434            }
    35             errors.add(new TestError(this, Severity.WARNING, tr("Barrier entrance not set on a barrier"), BARRIER_ENTRANCE_WITHOUT_BARRIER, n));
     35            errors.add(TestError
     36                    .builder(this, Severity.WARNING, BARRIER_ENTRANCE_WITHOUT_BARRIER)
     37                    .message(tr("Barrier entrance not set on a barrier"))
     38                    .primitives(n)
     39                    .build());
    3640        }
    3741    }
  • trunk/src/org/openstreetmap/josm/data/validation/tests/Coastlines.java

    r10991 r11129  
    147147            // simple closed ways are reported by WronglyOrderedWays
    148148            if (visited.size() > 1 && nodes.get(0) == nodes.get(nodes.size()-1) && Geometry.isClockwise(nodes)) {
    149                 errors.add(new TestError(this, Severity.WARNING, tr("Reversed coastline: land not on left side"),
    150                         WRONG_ORDER_COASTLINE, visited));
     149                errors.add(TestError.builder(this, Severity.WARNING, WRONG_ORDER_COASTLINE)
     150                        .message(tr("Reversed coastline: land not on left side"))
     151                        .primitives(visited)
     152                        .build());
    151153            }
    152154        }
     
    226228        }
    227229        if (errCode != REVERSED_COASTLINE)
    228             errors.add(new TestError(this, Severity.ERROR, msg, errCode, primitives, Collections.singletonList(n)));
     230            errors.add(TestError.builder(this, Severity.ERROR, errCode)
     231                    .message(msg)
     232                    .primitives(primitives)
     233                    .highlight(n)
     234                    .build());
    229235        else
    230             errors.add(new TestError(this, Severity.ERROR, msg, errCode, primitives));
     236            errors.add(TestError.builder(this, Severity.ERROR, errCode)
     237                    .message(msg)
     238                    .primitives(primitives)
     239                    .build());
    231240    }
    232241
  • trunk/src/org/openstreetmap/josm/data/validation/tests/ConditionalKeys.java

    r10715 r11129  
    167167                Pattern.compile(":conditional(:.*)?$").asPredicate())) {
    168168            if (!isKeyValid(key)) {
    169                 errors.add(new TestError(this, Severity.WARNING, tr("Wrong syntax in {0} key", key), 3201, p));
     169                errors.add(TestError.builder(this, Severity.WARNING, 3201)
     170                        .message(tr("Wrong syntax in {0} key", key))
     171                        .primitives(p)
     172                        .build());
    170173                continue;
    171174            }
     
    173176            final String error = validateValue(key, value);
    174177            if (error != null) {
    175                 errors.add(new TestError(this, Severity.WARNING, tr("Error in {0} value: {1}", key, error), 3202, p));
     178                errors.add(TestError.builder(this, Severity.WARNING, 3202)
     179                        .message(tr("Error in {0} value: {1}", key, error))
     180                        .primitives(p)
     181                        .build());
    176182            }
    177183        }
  • trunk/src/org/openstreetmap/josm/data/validation/tests/CrossingWays.java

    r10803 r11129  
    247247
    248248                        final String message = createMessage(es1.way, es2.way);
    249                         errors.add(new TestError(this, Severity.WARNING,
    250                                 message,
    251                                 CROSSING_WAYS,
    252                                 prims,
    253                                 highlight));
     249                        errors.add(TestError.builder(this, Severity.WARNING, CROSSING_WAYS)
     250                                .message(message)
     251                                .primitives(prims)
     252                                .highlightWaySegments(highlight)
     253                                .build());
    254254                        seenWays.put(prims, highlight);
    255255                    } else {
  • trunk/src/org/openstreetmap/josm/data/validation/tests/DuplicateNode.java

    r11062 r11129  
    3232import org.openstreetmap.josm.data.validation.TestError;
    3333import org.openstreetmap.josm.gui.progress.ProgressMonitor;
    34 import org.openstreetmap.josm.tools.CheckParameterUtil;
    3534import org.openstreetmap.josm.tools.MultiMap;
    3635
     
    8483            LatLon coorK = getLatLon(k);
    8584            return coorK == null ? 0 : coorK.hashCode();
    86         }
    87     }
    88 
    89     private static class DuplicateNodeTestError extends TestError {
    90         DuplicateNodeTestError(Test parentTest, Severity severity, String msg, int code, Set<OsmPrimitive> primitives) {
    91             super(parentTest, severity, tr("Duplicated nodes"), tr(msg), msg, code, primitives);
    92             CheckParameterUtil.ensureThat(!primitives.isEmpty(), "Empty primitives: " + msg);
    9385        }
    9486    }
     
    214206
    215207                if (nbType > 1) {
    216                     errors.add(new DuplicateNodeTestError(
    217                             parentTest,
    218                             Severity.WARNING,
    219                             marktr("Mixed type duplicated nodes"),
    220                             DUPLICATE_NODE_MIXED,
    221                             primitives
    222                             ));
     208                    errors.add(TestError.builder(parentTest, Severity.WARNING, DUPLICATE_NODE_MIXED)
     209                            .message(marktr("Mixed type duplicated nodes"))
     210                            .primitives(primitives)
     211                            .build());
    223212                } else if (typeMap.get("highway")) {
    224                     errors.add(new DuplicateNodeTestError(
    225                             parentTest,
    226                             Severity.ERROR,
    227                             marktr("Highway duplicated nodes"),
    228                             DUPLICATE_NODE_HIGHWAY,
    229                             primitives
    230                             ));
     213                    errors.add(TestError.builder(parentTest, Severity.ERROR, DUPLICATE_NODE_HIGHWAY)
     214                            .message(marktr("Highway duplicated nodes"))
     215                            .primitives(primitives)
     216                            .build());
    231217                } else if (typeMap.get("railway")) {
    232                     errors.add(new DuplicateNodeTestError(
    233                             parentTest,
    234                             Severity.ERROR,
    235                             marktr("Railway duplicated nodes"),
    236                             DUPLICATE_NODE_RAILWAY,
    237                             primitives
    238                             ));
     218                    errors.add(TestError.builder(parentTest, Severity.ERROR, DUPLICATE_NODE_RAILWAY)
     219                            .message(marktr("Railway duplicated nodes"))
     220                            .primitives(primitives)
     221                            .build());
    239222                } else if (typeMap.get("waterway")) {
    240                     errors.add(new DuplicateNodeTestError(
    241                             parentTest,
    242                             Severity.ERROR,
    243                             marktr("Waterway duplicated nodes"),
    244                             DUPLICATE_NODE_WATERWAY,
    245                             primitives
    246                             ));
     223                    errors.add(TestError.builder(parentTest, Severity.ERROR, DUPLICATE_NODE_WATERWAY)
     224                            .message(marktr("Waterway duplicated nodes"))
     225                            .primitives(primitives)
     226                            .build());
    247227                } else if (typeMap.get("boundary")) {
    248                     errors.add(new DuplicateNodeTestError(
    249                             parentTest,
    250                             Severity.ERROR,
    251                             marktr("Boundary duplicated nodes"),
    252                             DUPLICATE_NODE_BOUNDARY,
    253                             primitives
    254                             ));
     228                    errors.add(TestError.builder(parentTest, Severity.ERROR, DUPLICATE_NODE_BOUNDARY)
     229                            .message(marktr("Boundary duplicated nodes"))
     230                            .primitives(primitives)
     231                            .build());
    255232                } else if (typeMap.get("power")) {
    256                     errors.add(new DuplicateNodeTestError(
    257                             parentTest,
    258                             Severity.ERROR,
    259                             marktr("Power duplicated nodes"),
    260                             DUPLICATE_NODE_POWER,
    261                             primitives
    262                             ));
     233                    errors.add(TestError.builder(parentTest, Severity.ERROR, DUPLICATE_NODE_POWER)
     234                            .message(marktr("Power duplicated nodes"))
     235                            .primitives(primitives)
     236                            .build());
    263237                } else if (typeMap.get("natural")) {
    264                     errors.add(new DuplicateNodeTestError(
    265                             parentTest,
    266                             Severity.ERROR,
    267                             marktr("Natural duplicated nodes"),
    268                             DUPLICATE_NODE_NATURAL,
    269                             primitives
    270                             ));
     238                    errors.add(TestError.builder(parentTest, Severity.ERROR, DUPLICATE_NODE_NATURAL)
     239                            .message(marktr("Natural duplicated nodes"))
     240                            .primitives(primitives)
     241                            .build());
    271242                } else if (typeMap.get("building")) {
    272                     errors.add(new DuplicateNodeTestError(
    273                             parentTest,
    274                             Severity.ERROR,
    275                             marktr("Building duplicated nodes"),
    276                             DUPLICATE_NODE_BUILDING,
    277                             primitives
    278                             ));
     243                    errors.add(TestError.builder(parentTest, Severity.ERROR, DUPLICATE_NODE_BUILDING)
     244                            .message(marktr("Building duplicated nodes"))
     245                            .primitives(primitives)
     246                            .build());
    279247                } else if (typeMap.get("landuse")) {
    280                     errors.add(new DuplicateNodeTestError(
    281                             parentTest,
    282                             Severity.ERROR,
    283                             marktr("Landuse duplicated nodes"),
    284                             DUPLICATE_NODE_LANDUSE,
    285                             primitives
    286                             ));
     248                    errors.add(TestError.builder(parentTest, Severity.ERROR, DUPLICATE_NODE_LANDUSE)
     249                            .message(marktr("Landuse duplicated nodes"))
     250                            .primitives(primitives)
     251                            .build());
    287252                } else {
    288                     errors.add(new DuplicateNodeTestError(
    289                             parentTest,
    290                             Severity.WARNING,
    291                             marktr("Other duplicated nodes"),
    292                             DUPLICATE_NODE_OTHER,
    293                             primitives
    294                             ));
     253                    errors.add(TestError.builder(parentTest, Severity.WARNING, DUPLICATE_NODE_OTHER)
     254                            .message(marktr("Other duplicated nodes"))
     255                            .primitives(primitives)
     256                            .build());
    295257                }
    296258                it.remove();
     
    305267            }
    306268            if (duplicates.size() > 1) {
    307                 errors.add(new TestError(
    308                         parentTest,
    309                         Severity.WARNING,
    310                         tr("Nodes at same position"),
    311                         DUPLICATE_NODE,
    312                         duplicates
    313                         ));
     269                errors.add(TestError.builder(parentTest, Severity.WARNING, DUPLICATE_NODE)
     270                        .message(tr("Nodes at same position"))
     271                        .primitives(duplicates)
     272                        .build());
    314273            }
    315274        }
  • trunk/src/org/openstreetmap/josm/data/validation/tests/DuplicateRelation.java

    r9371 r11129  
    203203        for (Set<OsmPrimitive> duplicated : relations.values()) {
    204204            if (duplicated.size() > 1) {
    205                 TestError testError = new TestError(this, Severity.ERROR, tr("Duplicated relations"), DUPLICATE_RELATION, duplicated);
     205                TestError testError = TestError.builder(this, Severity.ERROR, DUPLICATE_RELATION)
     206                        .message(tr("Duplicated relations"))
     207                        .primitives(duplicated)
     208                        .build();
    206209                errors.add(testError);
    207210            }
     
    210213        for (Set<OsmPrimitive> duplicated : relationsNoKeys.values()) {
    211214            if (duplicated.size() > 1) {
    212                 TestError testError = new TestError(this, Severity.WARNING, tr("Relations with same members"), SAME_RELATION, duplicated);
     215                TestError testError = TestError.builder(this, Severity.WARNING, SAME_RELATION)
     216                        .message(tr("Relations with same members"))
     217                        .primitives(duplicated)
     218                        .build();
    213219                errors.add(testError);
    214220            }
  • trunk/src/org/openstreetmap/josm/data/validation/tests/DuplicateWay.java

    r9371 r11129  
    123123        for (Set<OsmPrimitive> duplicated : ways.values()) {
    124124            if (duplicated.size() > 1) {
    125                 TestError testError = new TestError(this, Severity.ERROR, tr("Duplicated ways"), DUPLICATE_WAY, duplicated);
     125                TestError testError = TestError.builder(this, Severity.ERROR, DUPLICATE_WAY)
     126                        .message(tr("Duplicated ways"))
     127                        .primitives(duplicated)
     128                        .build();
    126129                errors.add(testError);
    127130            }
     
    150153                    continue;
    151154                }
    152                 TestError testError = new TestError(this, Severity.WARNING, tr("Ways with same position"), SAME_WAY, sameway);
     155                TestError testError = TestError.builder(this, Severity.WARNING, SAME_WAY)
     156                        .message(tr("Ways with same position"))
     157                        .primitives(sameway)
     158                        .build();
    153159                errors.add(testError);
    154160            }
  • trunk/src/org/openstreetmap/josm/data/validation/tests/DuplicatedWayNodes.java

    r8382 r11129  
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
    6 import java.util.Arrays;
    76import java.util.Collections;
    87import java.util.Iterator;
     
    4342            }
    4443            if (lastN == n) {
    45                 errors.add(new TestError(this, Severity.ERROR, tr("Duplicated way nodes"), DUPLICATE_WAY_NODE,
    46                         Arrays.asList(w), Arrays.asList(n)));
     44                errors.add(TestError.builder(this, Severity.ERROR, DUPLICATE_WAY_NODE)
     45                        .message(tr("Duplicated way nodes"))
     46                        .primitives(w)
     47                        .highlight(n)
     48                        .build());
    4749                break;
    4850            }
  • trunk/src/org/openstreetmap/josm/data/validation/tests/Highways.java

    r10657 r11129  
    88import java.util.HashMap;
    99import java.util.HashSet;
    10 import java.util.Iterator;
    1110import java.util.List;
    1211import java.util.Locale;
     
    1514
    1615import org.openstreetmap.josm.command.ChangePropertyCommand;
    17 import org.openstreetmap.josm.command.Command;
    1816import org.openstreetmap.josm.data.osm.Node;
    1917import org.openstreetmap.josm.data.osm.OsmPrimitive;
    2018import org.openstreetmap.josm.data.osm.OsmUtils;
    2119import org.openstreetmap.josm.data.osm.Way;
    22 import org.openstreetmap.josm.data.validation.FixableTestError;
    2320import org.openstreetmap.josm.data.validation.Severity;
    2421import org.openstreetmap.josm.data.validation.Test;
     
    7673    }
    7774
    78     protected static class WrongRoundaboutHighway extends TestError {
    79 
    80         public final String correctValue;
    81 
    82         public WrongRoundaboutHighway(Highways tester, Way w, String key) {
    83             super(tester, Severity.WARNING,
    84                     tr("Incorrect roundabout (highway: {0} instead of {1})", w.get("highway"), key),
    85                     WRONG_ROUNDABOUT_HIGHWAY, w);
    86             this.correctValue = key;
    87         }
    88     }
    89 
    9075    @Override
    9176    public void visit(Node n) {
     
    147132                    // Error when the highway tags do not match
    148133                    if (!w.get("highway").equals(s)) {
    149                         errors.add(new WrongRoundaboutHighway(this, w, s));
     134                        errors.add(TestError.builder(this, Severity.WARNING, WRONG_ROUNDABOUT_HIGHWAY)
     135                                .message(tr("Incorrect roundabout (highway: {0} instead of {1})", w.get("highway"), s))
     136                                .primitives(w)
     137                                .fix(() -> new ChangePropertyCommand(w, "highway", s))
     138                                .build());
    150139                    }
    151140                    break;
     
    180169    private void testHighwayLink(final Way way) {
    181170        if (!isHighwayLinkOkay(way)) {
    182             errors.add(new TestError(this, Severity.WARNING,
    183                     tr("Highway link is not linked to adequate highway/link"), SOURCE_WRONG_LINK, way));
     171            errors.add(TestError.builder(this, Severity.WARNING, SOURCE_WRONG_LINK)
     172                    .message(tr("Highway link is not linked to adequate highway/link"))
     173                    .primitives(way)
     174                    .build());
    184175        }
    185176    }
     
    213204                }
    214205                if ((leftByPedestrians || leftByCyclists) && leftByCars) {
    215                     errors.add(new TestError(this, Severity.OTHER, tr("Missing pedestrian crossing information"),
    216                             MISSING_PEDESTRIAN_CROSSING, n));
     206                    errors.add(TestError.builder(this, Severity.OTHER, MISSING_PEDESTRIAN_CROSSING)
     207                            .message(tr("Missing pedestrian crossing information"))
     208                            .primitives(n)
     209                            .build());
    217210                    return;
    218211                }
     
    249242            String country = value.substring(0, index);
    250243            if (!ISO_COUNTRIES.contains(country)) {
     244                final TestError.Builder error = TestError.builder(this, Severity.WARNING, SOURCE_MAXSPEED_UNKNOWN_COUNTRY_CODE)
     245                        .message(tr("Unknown country code: {0}", country))
     246                        .primitives(p);
    251247                if ("UK".equals(country)) {
    252                     errors.add(new FixableTestError(this, Severity.WARNING,
    253                             tr("Unknown country code: {0}", country), SOURCE_MAXSPEED_UNKNOWN_COUNTRY_CODE, p,
    254                             new ChangePropertyCommand(p, SOURCE_MAXSPEED, value.replace("UK:", "GB:"))));
     248                    errors.add(error.fix(() -> new ChangePropertyCommand(p, SOURCE_MAXSPEED, value.replace("UK:", "GB:"))).build());
    255249                } else {
    256                     errors.add(new TestError(this, Severity.WARNING,
    257                             tr("Unknown country code: {0}", country), SOURCE_MAXSPEED_UNKNOWN_COUNTRY_CODE, p));
     250                    errors.add(error.build());
    258251                }
    259252            }
     
    261254            String context = value.substring(index+1);
    262255            if (!KNOWN_SOURCE_MAXSPEED_CONTEXTS.contains(context)) {
    263                 errors.add(new TestError(this, Severity.WARNING,
    264                         tr("Unknown source:maxspeed context: {0}", context), SOURCE_MAXSPEED_UNKNOWN_CONTEXT, p));
     256                errors.add(TestError.builder(this, Severity.WARNING, SOURCE_MAXSPEED_UNKNOWN_CONTEXT)
     257                        .message(tr("Unknown source:maxspeed context: {0}", context))
     258                        .primitives(p)
     259                        .build());
    265260            }
    266261            // TODO: Check coherence of context against maxspeed
     
    268263        }
    269264    }
    270 
    271     @Override
    272     public boolean isFixable(TestError testError) {
    273         return testError instanceof WrongRoundaboutHighway;
    274     }
    275 
    276     @Override
    277     public Command fixError(TestError testError) {
    278         if (testError instanceof WrongRoundaboutHighway) {
    279             // primitives list can be empty if all primitives have been purged
    280             Iterator<? extends OsmPrimitive> it = testError.getPrimitives().iterator();
    281             if (it.hasNext()) {
    282                 return new ChangePropertyCommand(it.next(),
    283                         "highway", ((WrongRoundaboutHighway) testError).correctValue);
    284             }
    285         }
    286         return null;
    287     }
    288265}
  • trunk/src/org/openstreetmap/josm/data/validation/tests/InternetTags.java

    r10472 r11129  
    22package org.openstreetmap.josm.data.validation.tests;
    33
     4import static org.openstreetmap.josm.tools.I18n.marktr;
    45import static org.openstreetmap.josm.tools.I18n.tr;
    56
     
    102103                return doValidateTag(p, k, proto+value, validator, code);
    103104            }
    104             String msg = tr("''{0}'': {1}", k, errMsg);
    105             // todo obtain English message for ignore functionality
    106             error = new TestError(this, Severity.WARNING, validator.getValidatorName(), msg, msg, code, p);
     105            error = TestError.builder(this, Severity.WARNING, code)
     106                    .message(validator.getValidatorName(), marktr("''{0}'': {1}"), k, errMsg)
     107                    .primitives(p)
     108                    .build();
    107109        }
    108110        return error;
  • trunk/src/org/openstreetmap/josm/data/validation/tests/Lanes.java

    r10638 r11129  
    4949        if (lanesCount.size() > 1) {
    5050            // if not all numbers are the same
    51             errors.add(new TestError(this, Severity.WARNING, message, 3100, p));
     51            errors.add(TestError.builder(this, Severity.WARNING, 3100)
     52                    .message(message)
     53                    .primitives(p)
     54                    .build());
    5255        } else if (lanesCount.size() == 1 && p.hasKey(lanesKey)) {
    5356            // ensure that lanes <= *:lanes
    5457            try {
    5558                if (Integer.parseInt(p.get(lanesKey)) > lanesCount.iterator().next()) {
    56                     errors.add(new TestError(this, Severity.WARNING, tr("Number of {0} greater than {1}", lanesKey, "*:" + lanesKey), 3100, p));
     59                    errors.add(TestError.builder(this, Severity.WARNING, 3100)
     60                            .message(tr("Number of {0} greater than {1}", lanesKey, "*:" + lanesKey))
     61                            .primitives(p)
     62                            .build());
    5763                }
    5864            } catch (NumberFormatException ignore) {
     
    6975        try {
    7076        if (Integer.parseInt(lanes) < Integer.parseInt(forward) + Integer.parseInt(backward)) {
    71             errors.add(new TestError(this, Severity.WARNING,
    72                     tr("Number of {0} greater than {1}", tr("{0}+{1}", "lanes:forward", "lanes:backward"), "lanes"), 3101, p));
     77            errors.add(TestError.builder(this, Severity.WARNING, 3101)
     78                    .message(tr("Number of {0} greater than {1}", tr("{0}+{1}", "lanes:forward", "lanes:backward"), "lanes"))
     79                    .primitives(p)
     80                    .build());
    7381        }
    7482        } catch (NumberFormatException ignore) {
  • trunk/src/org/openstreetmap/josm/data/validation/tests/LongSegment.java

    r11100 r11129  
    22package org.openstreetmap.josm.data.validation.tests;
    33
     4import static org.openstreetmap.josm.tools.I18n.marktr;
    45import static org.openstreetmap.josm.tools.I18n.tr;
    56
     
    4041        if (length > maxlength) {
    4142            length /= 1000.0;
    42             errors.add(new TestError(this, Severity.WARNING, tr("Long segments"),
    43                     tr("Very long segment of {0} kilometers", length.intValue()),
    44                     String.format("Very long segment of %d kilometers", length.intValue()),
    45                     LONG_SEGMENT, w));
     43            errors.add(TestError.builder(this, Severity.WARNING, LONG_SEGMENT)
     44                    .message(tr("Long segments"), marktr("Very long segment of {0} kilometers"), length.intValue())
     45                    .primitives(w)
     46                    .build());
    4647        }
    4748    }
  • trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java

    r11111 r11129  
    3939import org.openstreetmap.josm.data.osm.OsmUtils;
    4040import org.openstreetmap.josm.data.osm.Tag;
    41 import org.openstreetmap.josm.data.validation.FixableTestError;
    4241import org.openstreetmap.josm.data.validation.Severity;
    4342import org.openstreetmap.josm.data.validation.Test;
     
    567566        TestError getErrorForPrimitive(OsmPrimitive p) {
    568567            final Environment env = new Environment(p);
    569             return getErrorForPrimitive(p, whichSelectorMatchesEnvironment(env), env);
    570         }
    571 
    572         TestError getErrorForPrimitive(OsmPrimitive p, Selector matchingSelector, Environment env) {
     568            return getErrorForPrimitive(p, whichSelectorMatchesEnvironment(env), env, null);
     569        }
     570
     571        TestError getErrorForPrimitive(OsmPrimitive p, Selector matchingSelector, Environment env, Test tester) {
    573572            if (matchingSelector != null && !errors.isEmpty()) {
    574573                final Command fix = fixPrimitive(p);
     
    582581                    primitives = Collections.singletonList(p);
    583582                }
     583                final TestError.Builder error = TestError.builder(tester, getSeverity(), 3000)
     584                        .messageWithManuallyTranslatedDescription(description1, description2, matchingSelector.toString())
     585                        .primitives(primitives);
    584586                if (fix != null) {
    585                     return new FixableTestError(null, getSeverity(), description1, description2, matchingSelector.toString(), 3000,
    586                             primitives, fix);
     587                    return error.fix(() -> fix).build();
    587588                } else {
    588                     return new TestError(null, getSeverity(), description1, description2, matchingSelector.toString(), 3000, primitives);
     589                    return error.build();
    589590                }
    590591            } else {
     
    685686                if (selector != null) {
    686687                    check.rule.declaration.execute(env);
    687                     final TestError error = check.getErrorForPrimitive(p, selector, env);
     688                    final TestError error = check.getErrorForPrimitive(p, selector, env, new MapCSSTagCheckerAndRule(check.rule));
    688689                    if (error != null) {
    689                         error.setTester(new MapCSSTagCheckerAndRule(check.rule));
    690690                        r.add(error);
    691691                    }
  • trunk/src/org/openstreetmap/josm/data/validation/tests/MultipolygonTest.java

    r10000 r11129  
    22package org.openstreetmap.josm.data.validation.tests;
    33
     4import static org.openstreetmap.josm.tools.I18n.marktr;
    45import static org.openstreetmap.josm.tools.I18n.tr;
    56import static org.openstreetmap.josm.tools.I18n.trn;
    67
    78import java.awt.geom.GeneralPath;
    8 import java.text.MessageFormat;
    99import java.util.ArrayList;
    1010import java.util.Arrays;
     
    1212import java.util.Collections;
    1313import java.util.HashSet;
    14 import java.util.LinkedList;
    1514import java.util.List;
    1615import java.util.Set;
     
    148147                }
    149148            }
    150             errors.add(new TestError(this, Severity.WARNING, tr("Area style way is not closed"), NOT_CLOSED,
    151                     Collections.singletonList(w), Arrays.asList(nodes.get(0), nodes.get(nodes.size() - 1))));
     149            errors.add(TestError.builder(this, Severity.WARNING, NOT_CLOSED)
     150                    .message(tr("Area style way is not closed"))
     151                    .primitives(w)
     152                    .highlight(Arrays.asList(nodes.get(0), nodes.get(nodes.size() - 1)))
     153                    .build());
    152154        }
    153155    }
     
    186188        }
    187189        if (!hasOuterWay) {
    188             addError(r, new TestError(this, Severity.WARNING, tr("No outer way for multipolygon"), MISSING_OUTER_WAY, r));
     190            errors.add(TestError.builder(this, Severity.WARNING, MISSING_OUTER_WAY)
     191                    .message(tr("No outer way for multipolygon"))
     192                    .primitives(r)
     193                    .build());
    189194        }
    190195    }
     
    204209                    final String roleInNewMP = memberInNewMP.iterator().next().getRole();
    205210                    if (!member.getRole().equals(roleInNewMP)) {
    206                         List<OsmPrimitive> l = new ArrayList<>();
    207                         l.add(r);
    208                         l.add(member.getMember());
    209                         addError(r, new TestError(this, Severity.WARNING, RelationChecker.ROLE_VERIF_PROBLEM_MSG,
    210                                 tr("Role for ''{0}'' should be ''{1}''",
    211                                         member.getMember().getDisplayName(DefaultNameFormatter.getInstance()), roleInNewMP),
    212                                 MessageFormat.format("Role for ''{0}'' should be ''{1}''",
    213                                         member.getMember().getDisplayName(DefaultNameFormatter.getInstance()), roleInNewMP),
    214                                 WRONG_MEMBER_ROLE, l, Collections.singleton(member.getMember())));
     211                        errors.add(TestError.builder(this, Severity.WARNING, WRONG_MEMBER_ROLE)
     212                                .message(RelationChecker.ROLE_VERIF_PROBLEM_MSG,
     213                                        marktr("Role for ''{0}'' should be ''{1}''"),
     214                                        member.getMember().getDisplayName(DefaultNameFormatter.getInstance()), roleInNewMP)
     215                                .primitives(addRelationIfNeeded(r, member.getMember()))
     216                                .highlight(member.getMember())
     217                                .build());
    215218                    }
    216219                }
     
    242245                }
    243246                if (area == null) {
    244                     addError(r, new TestError(this, Severity.OTHER, tr("No area style for multipolygon"), NO_STYLE, r));
     247                    errors.add(TestError.builder(this, Severity.OTHER, NO_STYLE)
     248                            .message(tr("No area style for multipolygon"))
     249                            .primitives(r)
     250                            .build());
    245251                } else {
    246252                    /* old style multipolygon - solve: copy tags from outer way to multipolygon */
    247                     addError(r, new TestError(this, Severity.WARNING,
    248                             trn("Multipolygon relation should be tagged with area tags and not the outer way",
     253                    errors.add(TestError.builder(this, Severity.WARNING, NO_STYLE_POLYGON)
     254                            .message(trn("Multipolygon relation should be tagged with area tags and not the outer way",
    249255                                    "Multipolygon relation should be tagged with area tags and not the outer ways",
    250                                     polygon.getOuterWays().size()),
    251                        NO_STYLE_POLYGON, r));
     256                                    polygon.getOuterWays().size()))
     257                            .primitives(r)
     258                            .build());
    252259                }
    253260            }
     
    258265
    259266                    if (areaInner != null && area.equals(areaInner)) {
    260                         List<OsmPrimitive> l = new ArrayList<>();
    261                         l.add(r);
    262                         l.add(wInner);
    263                         addError(r, new TestError(this, Severity.OTHER,
    264                                 tr("With the currently used mappaint style the style for inner way equals the multipolygon style"),
    265                                 INNER_STYLE_MISMATCH, l, Collections.singletonList(wInner)));
     267                        errors.add(TestError.builder(this, Severity.OTHER, INNER_STYLE_MISMATCH)
     268                                .message(tr("With the currently used mappaint style the style for inner way equals the multipolygon style"))
     269                                .primitives(addRelationIfNeeded(r, wInner))
     270                                .highlight(wInner)
     271                                .build());
    266272                    }
    267273                }
     
    269275                    AreaElement areaOuter = ElemStyles.getAreaElemStyle(wOuter, false);
    270276                    if (areaOuter != null) {
    271                         List<OsmPrimitive> l = new ArrayList<>();
    272                         l.add(r);
    273                         l.add(wOuter);
    274277                        if (!area.equals(areaOuter)) {
    275                             addError(r, new TestError(this, Severity.OTHER, !areaStyle ? tr("Style for outer way mismatches")
    276                             : tr("With the currently used mappaint style(s) the style for outer way mismatches the area style"),
    277                             OUTER_STYLE_MISMATCH, l, Collections.singletonList(wOuter)));
     278                            String message = !areaStyle ? tr("Style for outer way mismatches")
     279                                    : tr("With the currently used mappaint style(s) the style for outer way mismatches the area style");
     280                            errors.add(TestError.builder(this, Severity.OTHER, OUTER_STYLE_MISMATCH)
     281                                    .message(message)
     282                                    .primitives(addRelationIfNeeded(r, wOuter))
     283                                    .highlight(wOuter)
     284                                    .build());
    278285                        } else if (areaStyle) { /* style on outer way of multipolygon, but equal to polygon */
    279                             addError(r, new TestError(this, Severity.WARNING, tr("Area style on outer way"), OUTER_STYLE,
    280                             l, Collections.singletonList(wOuter)));
     286                            errors.add(TestError.builder(this, Severity.WARNING, OUTER_STYLE)
     287                                    .message(tr("Area style on outer way"))
     288                                    .primitives(addRelationIfNeeded(r, wOuter))
     289                                    .highlight(wOuter)
     290                                    .build());
    281291                        }
    282292                    }
     
    298308        List<Node> openNodes = polygon.getOpenEnds();
    299309        if (!openNodes.isEmpty()) {
    300             List<OsmPrimitive> primitives = new LinkedList<>();
    301             primitives.add(r);
    302             primitives.addAll(openNodes);
    303             addError(r, new TestError(this, Severity.WARNING, tr("Multipolygon is not closed"), NON_CLOSED_WAY, primitives, openNodes));
     310            errors.add(TestError.builder(this, Severity.WARNING, NON_CLOSED_WAY)
     311                    .message(tr("Multipolygon is not closed"))
     312                    .primitives(addRelationIfNeeded(r, openNodes))
     313                    .highlight(openNodes)
     314                    .build());
    304315        }
    305316
     
    328339            }
    329340            if (outside) {
    330                 addError(r, new TestError(this, Severity.WARNING, tr("Multipolygon inner way is outside"),
    331                         INNER_WAY_OUTSIDE, Collections.singletonList(r), Arrays.asList(pdInner.getNodes())));
     341                errors.add(TestError.builder(this, Severity.WARNING, INNER_WAY_OUTSIDE)
     342                        .message(tr("Multipolygon inner way is outside"))
     343                        .primitives(r)
     344                        .highlightNodePairs(Collections.singletonList(pdInner.getNodes()))
     345                        .build());
    332346            }
    333347        }
     
    339353            PolyData pdOther = polygons.get(idx);
    340354            if (pdOther != null) {
    341                 addError(r, new TestError(this, Severity.WARNING, tr("Intersection between multipolygon ways"),
    342                         CROSSING_WAYS, Collections.singletonList(r), Arrays.asList(pd.getNodes(), pdOther.getNodes())));
     355                errors.add(TestError.builder(this, Severity.WARNING, CROSSING_WAYS)
     356                        .message(tr("Intersection between multipolygon ways"))
     357                        .primitives(r)
     358                        .highlightNodePairs(Arrays.asList(pd.getNodes(), pdOther.getNodes()))
     359                        .build());
    343360            }
    344361        }
     
    357374            if (rm.isWay()) {
    358375                if (!(rm.hasRole("inner", "outer") || !rm.hasRole())) {
    359                     addError(r, new TestError(this, Severity.WARNING, tr("No useful role for multipolygon member"),
    360                             WRONG_MEMBER_ROLE, rm.getMember()));
     376                    errors.add(TestError.builder(this, Severity.WARNING, WRONG_MEMBER_ROLE)
     377                            .message(tr("No useful role for multipolygon member"))
     378                            .primitives(addRelationIfNeeded(r, rm.getMember()))
     379                            .build());
    361380                }
    362381            } else {
    363382                if (!rm.hasRole("admin_centre", "label", "subarea", "land_area")) {
    364                     addError(r, new TestError(this, Severity.WARNING, tr("Non-Way in multipolygon"), WRONG_MEMBER_TYPE, rm.getMember()));
    365                 }
    366             }
    367         }
    368     }
    369 
    370     private static void addRelationIfNeeded(TestError error, Relation r) {
     383                    errors.add(TestError.builder(this, Severity.WARNING, WRONG_MEMBER_TYPE)
     384                            .message(tr("Non-Way in multipolygon"))
     385                            .primitives(addRelationIfNeeded(r, rm.getMember()))
     386                            .build());
     387                }
     388            }
     389        }
     390    }
     391
     392    private static Collection<? extends OsmPrimitive> addRelationIfNeeded(Relation r, OsmPrimitive primitive) {
     393        return addRelationIfNeeded(r, Collections.singleton(primitive));
     394    }
     395
     396    private static Collection<? extends OsmPrimitive> addRelationIfNeeded(Relation r, Collection<? extends OsmPrimitive> primitives) {
    371397        // Fix #8212 : if the error references only incomplete primitives,
    372398        // add multipolygon in order to let user select something and fix the error
    373         Collection<? extends OsmPrimitive> primitives = error.getPrimitives();
    374399        if (!primitives.contains(r)) {
    375400            for (OsmPrimitive p : primitives) {
    376401                if (!p.isIncomplete()) {
    377                     return;
     402                    return null;
    378403                }
    379404            }
     
    382407            List<OsmPrimitive> newPrimitives = new ArrayList<OsmPrimitive>(primitives);
    383408            newPrimitives.add(0, r);
    384             error.setPrimitives(newPrimitives);
    385         }
    386     }
    387 
    388     private void addError(Relation r, TestError error) {
    389         addRelationIfNeeded(error, r);
    390         errors.add(error);
    391     }
     409            return newPrimitives;
     410        } else {
     411            return primitives;
     412        }
     413    }
     414
    392415}
  • trunk/src/org/openstreetmap/josm/data/validation/tests/NameMismatch.java

    r8510 r11129  
    22package org.openstreetmap.josm.data.validation.tests;
    33
     4import static org.openstreetmap.josm.tools.I18n.marktr;
    45import static org.openstreetmap.josm.tools.I18n.tr;
    56
     
    5152     */
    5253    private void missingTranslation(OsmPrimitive p, String name) {
    53         errors.add(new TestError(this, Severity.OTHER,
    54                 tr("Missing name:* translation"),
    55                 tr("Missing name:*={0}. Add tag with correct language key.", name),
    56                 String.format("Missing name:*=%s. Add tag with correct language key.", name), NAME_TRANSLATION_MISSING, p));
     54        errors.add(TestError.builder(this, Severity.OTHER, NAME_TRANSLATION_MISSING)
     55                .message(tr("Missing name:* translation"), marktr("Missing name:*={0}. Add tag with correct language key."), name)
     56                .primitives(p)
     57                .build());
    5758    }
    5859
     
    8081
    8182        if (name == null) {
    82             errors.add(new TestError(this, Severity.OTHER,
    83                     tr("A name is missing, even though name:* exists."),
    84                     NAME_MISSING, p));
     83            errors.add(TestError.builder(this, Severity.OTHER, NAME_MISSING)
     84                    .message(tr("A name is missing, even though name:* exists."))
     85                    .primitives(p)
     86                    .build());
    8587            return;
    8688        }
  • trunk/src/org/openstreetmap/josm/data/validation/tests/OpeningHourTest.java

    r11042 r11129  
    1818import org.openstreetmap.josm.command.ChangePropertyCommand;
    1919import org.openstreetmap.josm.data.osm.OsmPrimitive;
    20 import org.openstreetmap.josm.data.validation.FixableTestError;
    2120import org.openstreetmap.josm.data.validation.Severity;
    2221import org.openstreetmap.josm.data.validation.Test;
     
    132131         */
    133132        public TestError getTestError(final OsmPrimitive p, final String key) {
    134             final String messageEn = message; // todo obtain English message for ignore functionality
     133            final TestError.Builder error = TestError.builder(OpeningHourTest.this, severity, 2901)
     134                    .message(tr("Opening hours syntax"), message) // todo obtain English message for ignore functionality
     135                    .primitives(p);
    135136            if (prettifiedValue == null || prettifiedValue.equals(p.get(key))) {
    136                 return new TestError(OpeningHourTest.this, severity, tr("Opening hours syntax"), message, messageEn, 2901, p);
     137                return error.build();
    137138            } else {
    138                 return new FixableTestError(OpeningHourTest.this, severity, tr("Opening hours syntax"), message, messageEn, 2901, p,
    139                         new ChangePropertyCommand(p, key, prettifiedValue));
     139                return error.fix(() -> new ChangePropertyCommand(p, key, prettifiedValue)).build();
    140140            }
    141141        }
  • trunk/src/org/openstreetmap/josm/data/validation/tests/OverlappingWays.java

    r10715 r11129  
    140140                    }
    141141
    142                     preliminaryErrors.add(new TestError(this,
    143                             type < OVERLAPPING_HIGHWAY_AREA ? Severity.WARNING : Severity.OTHER,
    144                                     errortype, type, prims, duplicated));
     142                    Severity severity = type < OVERLAPPING_HIGHWAY_AREA ? Severity.WARNING : Severity.OTHER;
     143                    preliminaryErrors.add(TestError.builder(this, severity, type)
     144                            .message(errortype)
     145                            .primitives(prims)
     146                            .highlightWaySegments(duplicated)
     147                            .build());
    145148                    seenWays.put(currentWays, duplicated);
    146149                } else { /* way seen, mark highlight layer only */
     
    202205        final Set<WaySegment> duplicateWaySegment = checkDuplicateWaySegment(w);
    203206        if (duplicateWaySegment != null) {
    204             errors.add(new TestError(this, Severity.ERROR, tr("Way contains segment twice"),
    205                     DUPLICATE_WAY_SEGMENT, Collections.singleton(w), duplicateWaySegment));
     207            errors.add(TestError.builder(this, Severity.ERROR, DUPLICATE_WAY_SEGMENT)
     208                    .message(tr("Way contains segment twice"))
     209                    .primitives(w)
     210                    .highlightWaySegments(duplicateWaySegment)
     211                    .build());
    206212            return;
    207213        }
  • trunk/src/org/openstreetmap/josm/data/validation/tests/PowerLines.java

    r10657 r11129  
    77import java.util.Arrays;
    88import java.util.Collection;
    9 import java.util.HashMap;
    10 import java.util.Iterator;
    119import java.util.List;
    12 import java.util.Map;
    1310
    1411import org.openstreetmap.josm.Main;
    1512import org.openstreetmap.josm.command.ChangePropertyCommand;
    16 import org.openstreetmap.josm.command.Command;
    1713import org.openstreetmap.josm.data.osm.Node;
    1814import org.openstreetmap.josm.data.osm.OsmPrimitive;
     
    4844            "portal", "terminal", "insulator");
    4945
    50     private final Map<Way, String> towerPoleTagMap = new HashMap<>();
    51 
    52     private final List<PowerLineError> potentialErrors = new ArrayList<>();
     46    private final List<TestError> potentialErrors = new ArrayList<>();
    5347
    5448    private final List<OsmPrimitive> powerStations = new ArrayList<>();
     
    6660            if (isPowerLine(w) && !w.hasTag("location", "underground")) {
    6761                String fixValue = null;
    68                 boolean erroneous = false;
     62                TestError.Builder error = null;
     63                Node errorNode = null;
    6964                boolean canFix = false;
    7065                for (Node n : w.getNodes()) {
     
    7267                        if (!isPowerAllowed(n) && IN_DOWNLOADED_AREA.test(n)) {
    7368                            if (!w.isFirstLastNode(n) || !isPowerStation(n)) {
    74                                 potentialErrors.add(new PowerLineError(this, n, w));
    75                                 erroneous = true;
     69                                error = TestError.builder(this, Severity.WARNING, POWER_LINES)
     70                                        .message(tr("Missing power tower/pole within power line"))
     71                                        .primitives(n);
     72                                errorNode = n;
    7673                            }
    7774                        }
     
    8582                    }
    8683                }
    87                 if (erroneous && canFix) {
    88                     towerPoleTagMap.put(w, fixValue);
     84                if (error != null && canFix) {
     85                    final ChangePropertyCommand fix = new ChangePropertyCommand(errorNode, "power", fixValue);
     86                    potentialErrors.add(error.fix(() -> fix).build());
     87                } else if (error != null) {
     88                    potentialErrors.add(error.build());
    8989                }
    9090            } else if (w.isClosed() && isPowerStation(w)) {
     
    104104    public void startTest(ProgressMonitor progressMonitor) {
    105105        super.startTest(progressMonitor);
    106         towerPoleTagMap.clear();
    107106        powerStations.clear();
    108107        potentialErrors.clear();
     
    111110    @Override
    112111    public void endTest() {
    113         for (PowerLineError e : potentialErrors) {
    114             Node n = e.getNode();
    115             if (n != null && !isInPowerStation(n)) {
    116                 errors.add(e);
    117             }
     112        for (TestError e : potentialErrors) {
     113            e.getPrimitives().stream()
     114                    .map(Node.class::cast)
     115                    .filter(n -> !isInPowerStation(n))
     116                    .findAny()
     117                    .ifPresent(ignore -> errors.add(e));
    118118        }
    119119        potentialErrors.clear();
     
    143143    }
    144144
    145     @Override
    146     public Command fixError(TestError testError) {
    147         if (testError instanceof PowerLineError && isFixable(testError)) {
    148             // primitives list can be empty if all primitives have been purged
    149             Iterator<? extends OsmPrimitive> it = testError.getPrimitives().iterator();
    150             if (it.hasNext()) {
    151                 return new ChangePropertyCommand(it.next(),
    152                         "power", towerPoleTagMap.get(((PowerLineError) testError).line));
    153             }
    154         }
    155         return null;
    156     }
    157 
    158     @Override
    159     public boolean isFixable(TestError testError) {
    160         return testError instanceof PowerLineError && towerPoleTagMap.containsKey(((PowerLineError) testError).line);
    161     }
    162 
    163145    /**
    164146     * Determines if the specified way denotes a power line.
     
    218200        return v != null && values != null && values.contains(v);
    219201    }
    220 
    221     protected static class PowerLineError extends TestError {
    222         private final Way line;
    223 
    224         public PowerLineError(PowerLines tester, Node n, Way line) {
    225             super(tester, Severity.WARNING,
    226                     tr("Missing power tower/pole within power line"), POWER_LINES, n);
    227             this.line = line;
    228         }
    229 
    230         public final Node getNode() {
    231             // primitives list can be empty if all primitives have been purged
    232             Iterator<? extends OsmPrimitive> it = getPrimitives().iterator();
    233             return it.hasNext() ? (Node) it.next() : null;
    234         }
    235     }
    236202}
  • trunk/src/org/openstreetmap/josm/data/validation/tests/PublicTransportRouteTest.java

    r10144 r11129  
    55
    66import java.util.ArrayList;
    7 import java.util.Arrays;
    87import java.util.HashSet;
    98import java.util.List;
     
    4847        for (RelationMember member : r.getMembers()) {
    4948            if (member.hasRole("forward", "backward")) {
    50                 errors.add(new TestError(this, Severity.WARNING, tr("Route relation contains a ''{0}'' role", "forward/backward"), 3601, r));
     49                errors.add(TestError.builder(this, Severity.WARNING, 3601)
     50                        .message(tr("Route relation contains a ''{0}'' role", "forward/backward"))
     51                        .primitives(r)
     52                        .build());
    5153                return;
    5254            } else if (member.hasRole("") && OsmPrimitiveType.WAY.equals(member.getType())) {
     
    6769                    || WayConnectionType.Direction.NONE.equals(link.direction);
    6870            if (hasError) {
    69                 errors.add(new TestError(this, Severity.WARNING, tr("Route relation contains a gap"), 3602, r));
     71                errors.add(TestError.builder(this, Severity.WARNING, 3602)
     72                        .message(tr("Route relation contains a gap"))
     73                        .primitives(r)
     74                        .build());
    7075                return;
    7176            }
     
    7681                    && OsmPrimitiveType.NODE.equals(member.getType())
    7782                    && !routeNodes.contains(member.getNode())) {
    78                 errors.add(new TestError(this, Severity.WARNING,
    79                         tr("Stop position not part of route"), 3603, Arrays.asList(member.getMember(), r)));
     83                errors.add(TestError.builder(this, Severity.WARNING, 3603)
     84                        .message(tr("Stop position not part of route"))
     85                        .primitives(member.getMember(), r)
     86                        .build());
    8087            }
    8188        }
  • trunk/src/org/openstreetmap/josm/data/validation/tests/RelationChecker.java

    r10981 r11129  
    55import static org.openstreetmap.josm.tools.I18n.tr;
    66
    7 import java.text.MessageFormat;
    87import java.util.Collection;
    98import java.util.EnumSet;
     
    116115        if (allroles.isEmpty() && n.hasTag("type", "route")
    117116                && n.hasTag("route", "train", "subway", "monorail", "tram", "bus", "trolleybus", "aerialway", "ferry")) {
    118             errors.add(new TestError(this, Severity.WARNING,
    119                     tr("Route scheme is unspecified. Add {0} ({1}=public_transport; {2}=legacy)", "public_transport:version", "2", "1"),
    120                     RELATION_UNKNOWN, n));
     117            errors.add(TestError.builder(this, Severity.WARNING, RELATION_UNKNOWN)
     118                    .message(tr("Route scheme is unspecified. Add {0} ({1}=public_transport; {2}=legacy)", "public_transport:version", "2", "1"))
     119                    .primitives(n)
     120                    .build());
    121121        } else if (allroles.isEmpty()) {
    122             errors.add(new TestError(this, Severity.WARNING, tr("Relation type is unknown"), RELATION_UNKNOWN, n));
     122            errors.add(TestError.builder(this, Severity.WARNING, RELATION_UNKNOWN)
     123                    .message(tr("Relation type is unknown"))
     124                    .primitives(n)
     125                    .build());
    123126        }
    124127
    125128        Map<String, RoleInfo> map = buildRoleInfoMap(n);
    126129        if (map.isEmpty()) {
    127             errors.add(new TestError(this, Severity.ERROR, tr("Relation is empty"), RELATION_EMPTY, n));
     130            errors.add(TestError.builder(this, Severity.ERROR, RELATION_EMPTY)
     131                    .message(tr("Relation is empty"))
     132                    .primitives(n)
     133                    .build());
    128134        } else if (!allroles.isEmpty()) {
    129135            checkRoles(n, allroles, map);
     
    230236                            // different present, for which memberExpression will match
    231237                            // but stash the error in case no better reason will be found later
    232                             String s = marktr("Role member does not match expression {0} in template {1}");
    233                             possibleMatchError = new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
    234                                     tr(s, r.memberExpression, rolePreset.name), s, WRONG_TYPE,
    235                                     member.getMember().isUsable() ? member.getMember() : n);
     238                            possibleMatchError = TestError.builder(this, Severity.WARNING, WRONG_TYPE)
     239                                    .message(ROLE_VERIF_PROBLEM_MSG,
     240                                            marktr("Role member does not match expression {0} in template {1}"),
     241                                            r.memberExpression, rolePreset.name)
     242                                    .primitives(member.getMember().isUsable() ? member.getMember() : n)
     243                                    .build();
    236244                        }
    237245                    }
     
    252260            // no errors found till now. So member at least failed at matching the type
    253261            // it could also fail at memberExpression, but we can't guess at which
    254             String s = marktr("Role member type {0} does not match accepted list of {1} in template {2}");
    255262
    256263            // prepare Set of all accepted types in template
     
    263270            String typesStr = types.stream().map(x -> tr(x.getName())).collect(Collectors.joining("/"));
    264271
    265             errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
    266                     tr(s, member.getType(), typesStr, rolePreset.name), s, WRONG_TYPE,
    267                     member.getMember().isUsable() ? member.getMember() : n));
     272            errors.add(TestError.builder(this, Severity.WARNING, WRONG_TYPE)
     273                    .message(ROLE_VERIF_PROBLEM_MSG,
     274                            marktr("Role member type {0} does not match accepted list of {1} in template {2}"),
     275                            member.getType(), typesStr, rolePreset.name)
     276                    .primitives(member.getMember().isUsable() ? member.getMember() : n)
     277                    .build());
    268278        }
    269279        return false;
     
    301311
    302312                if (!key.isEmpty()) {
    303                     String s = marktr("Role {0} unknown in templates {1}");
    304 
    305                     errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
    306                             tr(s, key, templates), MessageFormat.format(s, key), ROLE_UNKNOWN, n));
     313
     314                    errors.add(TestError.builder(this, Severity.WARNING, ROLE_UNKNOWN)
     315                            .message(ROLE_VERIF_PROBLEM_MSG, marktr("Role {0} unknown in templates {1}"), key, templates)
     316                            .primitives(n)
     317                            .build());
    307318                } else {
    308                     String s = marktr("Empty role type found when expecting one of {0}");
    309                     errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
    310                             tr(s, templates), s, ROLE_EMPTY, n));
     319                    errors.add(TestError.builder(this, Severity.WARNING, ROLE_EMPTY)
     320                            .message(ROLE_VERIF_PROBLEM_MSG, marktr("Empty role type found when expecting one of {0}"), templates)
     321                            .primitives(n)
     322                            .build());
    311323                }
    312324            }
     
    319331        if (count != vc) {
    320332            if (count == 0) {
    321                 String s = marktr("Role {0} missing");
    322                 errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
    323                         tr(s, keyname), MessageFormat.format(s, keyname), ROLE_MISSING, n));
     333                errors.add(TestError.builder(this, Severity.WARNING, ROLE_MISSING)
     334                        .message(ROLE_VERIF_PROBLEM_MSG, marktr("Role {0} missing"), keyname)
     335                        .primitives(n)
     336                        .build());
    324337            } else if (vc > count) {
    325                 String s = marktr("Number of {0} roles too low ({1})");
    326                 errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
    327                         tr(s, keyname, count), MessageFormat.format(s, keyname, count), LOW_COUNT, n));
     338                errors.add(TestError.builder(this, Severity.WARNING, LOW_COUNT)
     339                        .message(ROLE_VERIF_PROBLEM_MSG, marktr("Number of {0} roles too low ({1})"), keyname, count)
     340                        .primitives(n)
     341                        .build());
    328342            } else {
    329                 String s = marktr("Number of {0} roles too high ({1})");
    330                 errors.add(new TestError(this, Severity.WARNING, ROLE_VERIF_PROBLEM_MSG,
    331                         tr(s, keyname, count), MessageFormat.format(s, keyname, count), HIGH_COUNT, n));
     343                errors.add(TestError.builder(this, Severity.WARNING, HIGH_COUNT)
     344                        .message(ROLE_VERIF_PROBLEM_MSG, marktr("Number of {0} roles too high ({1})"), keyname, count)
     345                        .primitives(n)
     346                        .build());
    332347            }
    333348        }
  • trunk/src/org/openstreetmap/josm/data/validation/tests/SelfIntersectingWay.java

    r8382 r11129  
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
    6 import java.util.Arrays;
    76import java.util.HashSet;
    87import java.util.Set;
     
    3736            Node n = w.getNode(i);
    3837            if (nodes.contains(n)) {
    39                 errors.add(new TestError(this,
    40                         Severity.WARNING, tr("Self-intersecting ways"), SELF_INTERSECT,
    41                         Arrays.asList(w), Arrays.asList(n)));
     38                errors.add(TestError.builder(this, Severity.WARNING, SELF_INTERSECT)
     39                        .message(tr("Self-intersecting ways"))
     40                        .primitives(w)
     41                        .highlight(n)
     42                        .build());
    4243                break;
    4344            } else {
  • trunk/src/org/openstreetmap/josm/data/validation/tests/SimilarNamedWays.java

    r10721 r11129  
    9797                    primitives.add(w);
    9898                    primitives.add(w2);
    99                     errors.add(new TestError(this, Severity.WARNING, tr("Similarly named ways"), SIMILAR_NAMED, primitives));
     99                    errors.add(TestError.builder(this, Severity.WARNING, SIMILAR_NAMED)
     100                            .message(tr("Similarly named ways"))
     101                            .primitives(primitives)
     102                            .build());
    100103                    errorWays.put(w, w2);
    101104                }
  • trunk/src/org/openstreetmap/josm/data/validation/tests/TagChecker.java

    r11042 r11129  
    99import java.io.BufferedReader;
    1010import java.io.IOException;
    11 import java.text.MessageFormat;
    1211import java.util.ArrayList;
    1312import java.util.Arrays;
     
    3736import org.openstreetmap.josm.data.osm.OsmUtils;
    3837import org.openstreetmap.josm.data.osm.Tag;
    39 import org.openstreetmap.josm.data.validation.FixableTestError;
    4038import org.openstreetmap.josm.data.validation.Severity;
    4139import org.openstreetmap.josm.data.validation.Test.TagTest;
     
    392390            for (CheckerData d : checkerData) {
    393391                if (d.match(p, keys)) {
    394                     errors.add(new TestError(this, d.getSeverity(), tr("Suspicious tag/value combinations"),
    395                             d.getDescription(), d.getDescriptionOrig(), d.getCode(), p));
     392                    errors.add(TestError.builder(this, d.getSeverity(), d.getCode())
     393                            .message(tr("Suspicious tag/value combinations"), d.getDescription())
     394                            .primitives(p)
     395                            .build());
    396396                    withErrors.put(p, "TC");
    397397                }
     
    404404            String value = prop.getValue();
    405405            if (checkValues && (containsLow(value)) && !withErrors.contains(p, "ICV")) {
    406                 errors.add(new TestError(this, Severity.WARNING, tr("Tag value contains character with code less than 0x20"),
    407                         tr(s, key), MessageFormat.format(s, key), LOW_CHAR_VALUE, p));
     406                errors.add(TestError.builder(this, Severity.WARNING, LOW_CHAR_VALUE)
     407                        .message(tr("Tag value contains character with code less than 0x20"), s, key)
     408                        .primitives(p)
     409                        .build());
    408410                withErrors.put(p, "ICV");
    409411            }
    410412            if (checkKeys && (containsLow(key)) && !withErrors.contains(p, "ICK")) {
    411                 errors.add(new TestError(this, Severity.WARNING, tr("Tag key contains character with code less than 0x20"),
    412                         tr(s, key), MessageFormat.format(s, key), LOW_CHAR_KEY, p));
     413                errors.add(TestError.builder(this, Severity.WARNING, LOW_CHAR_KEY)
     414                        .message(tr("Tag key contains character with code less than 0x20"), s, key)
     415                        .primitives(p)
     416                        .build());
    413417                withErrors.put(p, "ICK");
    414418            }
    415419            if (checkValues && (value != null && value.length() > 255) && !withErrors.contains(p, "LV")) {
    416                 errors.add(new TestError(this, Severity.ERROR, tr("Tag value longer than allowed"),
    417                         tr(s, key), MessageFormat.format(s, key), LONG_VALUE, p));
     420                errors.add(TestError.builder(this, Severity.ERROR, LONG_VALUE)
     421                        .message(tr("Tag value longer than allowed"), s, key)
     422                        .primitives(p)
     423                        .build());
    418424                withErrors.put(p, "LV");
    419425            }
    420426            if (checkKeys && (key != null && key.length() > 255) && !withErrors.contains(p, "LK")) {
    421                 errors.add(new TestError(this, Severity.ERROR, tr("Tag key longer than allowed"),
    422                         tr(s, key), MessageFormat.format(s, key), LONG_KEY, p));
     427                errors.add(TestError.builder(this, Severity.ERROR, LONG_KEY)
     428                        .message(tr("Tag key longer than allowed"), s, key)
     429                        .primitives(p)
     430                        .build());
    423431                withErrors.put(p, "LK");
    424432            }
    425433            if (checkValues && (value == null || value.trim().isEmpty()) && !withErrors.contains(p, "EV")) {
    426                 errors.add(new TestError(this, Severity.WARNING, tr("Tags with empty values"),
    427                         tr(s, key), MessageFormat.format(s, key), EMPTY_VALUES, p));
     434                errors.add(TestError.builder(this, Severity.WARNING, EMPTY_VALUES)
     435                        .message(tr("Tags with empty values"), s, key)
     436                        .primitives(p)
     437                        .build());
    428438                withErrors.put(p, "EV");
    429439            }
    430440            if (checkKeys && key != null && key.indexOf(' ') >= 0 && !withErrors.contains(p, "IPK")) {
    431                 errors.add(new TestError(this, Severity.WARNING, tr("Invalid white space in property key"),
    432                         tr(s, key), MessageFormat.format(s, key), INVALID_KEY_SPACE, p));
     441                errors.add(TestError.builder(this, Severity.WARNING, INVALID_KEY_SPACE)
     442                        .message(tr("Invalid white space in property key"), s, key)
     443                        .primitives(p)
     444                        .build());
    433445                withErrors.put(p, "IPK");
    434446            }
    435447            if (checkValues && value != null && (value.startsWith(" ") || value.endsWith(" ")) && !withErrors.contains(p, "SPACE")) {
    436                 errors.add(new TestError(this, Severity.WARNING, tr("Property values start or end with white space"),
    437                         tr(s, key), MessageFormat.format(s, key), INVALID_SPACE, p));
     448                errors.add(TestError.builder(this, Severity.WARNING, INVALID_SPACE)
     449                        .message(tr("Property values start or end with white space"), s, key)
     450                        .primitives(p)
     451                        .build());
    438452                withErrors.put(p, "SPACE");
    439453            }
    440454            if (checkValues && value != null && value.contains("  ") && !withErrors.contains(p, "SPACE")) {
    441                 errors.add(new TestError(this, Severity.WARNING, tr("Property values contain multiple white spaces"),
    442                         tr(s, key), MessageFormat.format(s, key), MULTIPLE_SPACES, p));
     455                errors.add(TestError.builder(this, Severity.WARNING, MULTIPLE_SPACES)
     456                        .message(tr("Property values contain multiple white spaces"), s, key)
     457                        .primitives(p)
     458                        .build());
    443459                withErrors.put(p, "SPACE");
    444460            }
    445461            if (checkValues && value != null && !value.equals(Entities.unescape(value)) && !withErrors.contains(p, "HTML")) {
    446                 errors.add(new TestError(this, Severity.OTHER, tr("Property values contain HTML entity"),
    447                         tr(s, key), MessageFormat.format(s, key), INVALID_HTML, p));
     462                errors.add(TestError.builder(this, Severity.OTHER, INVALID_HTML)
     463                        .message(tr("Property values contain HTML entity"), s, key)
     464                        .primitives(p)
     465                        .build());
    448466                withErrors.put(p, "HTML");
    449467            }
     
    455473                        if (fixedKey != null && !"".equals(fixedKey) && !fixedKey.equals(key)) {
    456474                            // misspelled preset key
    457                             String i = marktr("Key ''{0}'' looks like ''{1}''.");
    458                             final TestError error;
     475                            final TestError.Builder error = TestError.builder(this, Severity.WARNING, MISSPELLED_KEY)
     476                                    .message(tr("Misspelled property key"), marktr("Key ''{0}'' looks like ''{1}''."), key, fixedKey)
     477                                    .primitives(p);
    459478                            if (p.hasKey(fixedKey)) {
    460                                 error = new TestError(this, Severity.WARNING, tr("Misspelled property key"),
    461                                         tr(i, key, fixedKey),
    462                                         MessageFormat.format(i, key, fixedKey), MISSPELLED_KEY, p);
     479                                errors.add(error.build());
    463480                            } else {
    464                                 error = new FixableTestError(this, Severity.WARNING, tr("Misspelled property key"),
    465                                         tr(i, key, fixedKey),
    466                                         MessageFormat.format(i, key, fixedKey), MISSPELLED_KEY, p,
    467                                         new ChangePropertyKeyCommand(p, key, fixedKey));
     481                                errors.add(error.fix(() -> new ChangePropertyKeyCommand(p, key, fixedKey)).build());
    468482                            }
    469                             errors.add(error);
    470483                            withErrors.put(p, "WPK");
    471484                        } else {
    472                             String i = marktr("Key ''{0}'' not in presets.");
    473                             errors.add(new TestError(this, Severity.OTHER, tr("Presets do not contain property key"),
    474                                     tr(i, key), MessageFormat.format(i, key), INVALID_VALUE, p));
     485                            errors.add(TestError.builder(this, Severity.OTHER, INVALID_VALUE)
     486                                    .message(tr("Presets do not contain property key"), marktr("Key ''{0}'' not in presets."), key)
     487                                    .primitives(p)
     488                                    .build());
    475489                            withErrors.put(p, "UPK");
    476490                        }
     
    480494                        Map<String, String> possibleValues = getPossibleValues(presetsValueData.get(key));
    481495                        if (possibleValues.containsKey(fixedValue)) {
    482                             fixedValue = possibleValues.get(fixedValue);
     496                            final String newKey = possibleValues.get(fixedValue);
    483497                            // misspelled preset value
    484                             String i = marktr("Value ''{0}'' for key ''{1}'' looks like ''{2}''.");
    485                             errors.add(new FixableTestError(this, Severity.WARNING, tr("Misspelled property value"),
    486                                     tr(i, prop.getValue(), key, fixedValue), MessageFormat.format(i, prop.getValue(), fixedValue),
    487                                     MISSPELLED_VALUE, p, new ChangePropertyCommand(p, key, fixedValue)));
     498                            errors.add(TestError.builder(this, Severity.WARNING, MISSPELLED_VALUE)
     499                                    .message(tr("Misspelled property value"),
     500                                            marktr("Value ''{0}'' for key ''{1}'' looks like ''{2}''."), prop.getValue(), key, fixedValue)
     501                                    .primitives(p)
     502                                    .fix(() -> new ChangePropertyCommand(p, key, newKey))
     503                                    .build());
    488504                            withErrors.put(p, "WPV");
    489505                        } else {
    490506                            // unknown preset value
    491                             String i = marktr("Value ''{0}'' for key ''{1}'' not in presets.");
    492                             errors.add(new TestError(this, Severity.OTHER, tr("Presets do not contain property value"),
    493                                     tr(i, prop.getValue(), key), MessageFormat.format(i, prop.getValue(), key), INVALID_VALUE, p));
     507                            errors.add(TestError.builder(this, Severity.OTHER, INVALID_VALUE)
     508                                    .message(tr("Presets do not contain property value"),
     509                                            marktr("Value ''{0}'' for key ''{1}'' not in presets."), prop.getValue(), key)
     510                                    .primitives(p)
     511                                    .build());
    494512                            withErrors.put(p, "UPV");
    495513                        }
     
    502520                        || key.contains("todo") || key.toLowerCase(Locale.ENGLISH).contains("fixme"))
    503521                        && !withErrors.contains(p, "FIXME")) {
    504                     errors.add(new TestError(this, Severity.OTHER,
    505                             tr("FIXMES"), FIXME, p));
     522                    errors.add(TestError.builder(this, Severity.OTHER, FIXME)
     523                            .message(tr("FIXMES"))
     524                            .primitives(p)
     525                            .build());
    506526                    withErrors.put(p, "FIXME");
    507527                }
     
    646666        List<Command> commands = new ArrayList<>(50);
    647667
    648         if (testError instanceof FixableTestError) {
    649             commands.add(testError.getFix());
    650         } else {
    651             Collection<? extends OsmPrimitive> primitives = testError.getPrimitives();
    652             for (OsmPrimitive p : primitives) {
    653                 Map<String, String> tags = p.getKeys();
    654                 if (tags == null || tags.isEmpty()) {
    655                     continue;
    656                 }
    657 
    658                 for (Entry<String, String> prop: tags.entrySet()) {
    659                     String key = prop.getKey();
    660                     String value = prop.getValue();
    661                     if (value == null || value.trim().isEmpty()) {
    662                         commands.add(new ChangePropertyCommand(p, key, null));
    663                     } else if (value.startsWith(" ") || value.endsWith(" ") || value.contains("  ")) {
    664                         commands.add(new ChangePropertyCommand(p, key, Tag.removeWhiteSpaces(value)));
    665                     } else if (key.startsWith(" ") || key.endsWith(" ") || key.contains("  ")) {
    666                         commands.add(new ChangePropertyKeyCommand(p, key, Tag.removeWhiteSpaces(key)));
    667                     } else {
    668                         String evalue = Entities.unescape(value);
    669                         if (!evalue.equals(value)) {
    670                             commands.add(new ChangePropertyCommand(p, key, evalue));
    671                         }
     668        Collection<? extends OsmPrimitive> primitives = testError.getPrimitives();
     669        for (OsmPrimitive p : primitives) {
     670            Map<String, String> tags = p.getKeys();
     671            if (tags == null || tags.isEmpty()) {
     672                continue;
     673            }
     674
     675            for (Entry<String, String> prop: tags.entrySet()) {
     676                String key = prop.getKey();
     677                String value = prop.getValue();
     678                if (value == null || value.trim().isEmpty()) {
     679                    commands.add(new ChangePropertyCommand(p, key, null));
     680                } else if (value.startsWith(" ") || value.endsWith(" ") || value.contains("  ")) {
     681                    commands.add(new ChangePropertyCommand(p, key, Tag.removeWhiteSpaces(value)));
     682                } else if (key.startsWith(" ") || key.endsWith(" ") || key.contains("  ")) {
     683                    commands.add(new ChangePropertyKeyCommand(p, key, Tag.removeWhiteSpaces(key)));
     684                } else {
     685                    String evalue = Entities.unescape(value);
     686                    if (!evalue.equals(value)) {
     687                        commands.add(new ChangePropertyCommand(p, key, evalue));
    672688                    }
    673689                }
     
    840856
    841857        public String getDescription() {
    842             return tr(description);
    843         }
    844 
    845         public String getDescriptionOrig() {
    846858            return description;
    847859        }
  • trunk/src/org/openstreetmap/josm/data/validation/tests/TurnrestrictionTest.java

    r9244 r11129  
    55
    66import java.util.ArrayList;
    7 import java.util.Arrays;
    8 import java.util.Collections;
    97import java.util.List;
    108
     
    9997                    break;
    10098                default:
    101                     errors.add(new TestError(this, Severity.WARNING, tr("Unknown role"), UNKNOWN_ROLE,
    102                             l, Collections.singletonList(m)));
     99                    errors.add(TestError.builder(this, Severity.WARNING, UNKNOWN_ROLE)
     100                            .message(tr("Unknown role"))
     101                            .primitives(l)
     102                            .highlight(m.getMember())
     103                            .build());
    103104                }
    104105            } else if (m.isNode()) {
     
    115116                    }
    116117                } else {
    117                     errors.add(new TestError(this, Severity.WARNING, tr("Unknown role"), UNKNOWN_ROLE,
    118                             l, Collections.singletonList(m)));
     118                    errors.add(TestError.builder(this, Severity.WARNING, UNKNOWN_ROLE)
     119                            .message(tr("Unknown role"))
     120                            .primitives(l)
     121                            .highlight(m.getMember())
     122                            .build());
    119123                }
    120124            } else {
    121                 errors.add(new TestError(this, Severity.WARNING, tr("Unknown member type"), UNKNOWN_TYPE,
    122                         l, Collections.singletonList(m)));
     125                errors.add(TestError.builder(this, Severity.WARNING, UNKNOWN_TYPE)
     126                        .message(tr("Unknown member type"))
     127                        .primitives(l)
     128                        .highlight(m.getMember())
     129                        .build());
    123130            }
    124131        }
    125132        if (morefrom) {
    126             errors.add(new TestError(this, Severity.ERROR, tr("More than one \"from\" way found"), MORE_FROM, r));
     133            errors.add(TestError.builder(this, Severity.ERROR, MORE_FROM)
     134                    .message(tr("More than one \"from\" way found"))
     135                    .primitives(r)
     136                    .build());
    127137        }
    128138        if (moreto) {
    129             errors.add(new TestError(this, Severity.ERROR, tr("More than one \"to\" way found"), MORE_TO, r));
     139            errors.add(TestError.builder(this, Severity.ERROR, MORE_TO)
     140                    .message(tr("More than one \"to\" way found"))
     141                    .primitives(r)
     142                    .build());
    130143        }
    131144        if (morevia) {
    132             errors.add(new TestError(this, Severity.ERROR, tr("More than one \"via\" node found"), MORE_VIA, r));
     145            errors.add(TestError.builder(this, Severity.ERROR, MORE_VIA)
     146                    .message(tr("More than one \"via\" node found"))
     147                    .primitives(r)
     148                    .build());
    133149        }
    134150        if (mixvia) {
    135             errors.add(new TestError(this, Severity.ERROR, tr("Cannot mix node and way for role \"via\""), MIX_VIA, r));
     151            errors.add(TestError.builder(this, Severity.ERROR, MIX_VIA)
     152                    .message(tr("Cannot mix node and way for role \"via\""))
     153                    .primitives(r)
     154                    .build());
    136155        }
    137156
    138157        if (fromWay == null) {
    139             errors.add(new TestError(this, Severity.ERROR, tr("No \"from\" way found"), NO_FROM, r));
     158            errors.add(TestError.builder(this, Severity.ERROR, NO_FROM)
     159                    .message(tr("No \"from\" way found"))
     160                    .primitives(r)
     161                    .build());
    140162            return;
    141163        }
    142164        if (toWay == null) {
    143             errors.add(new TestError(this, Severity.ERROR, tr("No \"to\" way found"), NO_TO, r));
     165            errors.add(TestError.builder(this, Severity.ERROR, NO_TO)
     166                    .message(tr("No \"to\" way found"))
     167                    .primitives(r)
     168                    .build());
    144169            return;
    145170        }
    146171        if (fromWay.equals(toWay)) {
    147             errors.add(new TestError(this, r.hasTag("restriction", "no_u_turn") ? Severity.OTHER : Severity.WARNING,
    148                     tr("\"from\" way equals \"to\" way"), FROM_EQUALS_TO, r));
     172            Severity severity = r.hasTag("restriction", "no_u_turn") ? Severity.OTHER : Severity.WARNING;
     173            errors.add(TestError.builder(this, severity, FROM_EQUALS_TO)
     174                    .message(tr("\"from\" way equals \"to\" way"))
     175                    .primitives(r)
     176                    .build());
    149177        }
    150178        if (via.isEmpty()) {
    151             errors.add(new TestError(this, Severity.ERROR, tr("No \"via\" node or way found"), NO_VIA, r));
     179            errors.add(TestError.builder(this, Severity.ERROR, NO_VIA)
     180                    .message(tr("No \"via\" node or way found"))
     181                    .primitives(r)
     182                    .build());
    152183            return;
    153184        }
     
    160191                    tr("The \"from\" way does not start or end at a \"via\" node."), FROM_VIA_NODE);
    161192            if (toWay.isOneway() != 0 && viaNode.equals(toWay.lastNode(true))) {
    162                 errors.add(new TestError(this, Severity.WARNING, tr("Superfluous turnrestriction as \"to\" way is oneway"), SUPERFLUOUS, r));
     193                errors.add(TestError.builder(this, Severity.WARNING, SUPERFLUOUS)
     194                        .message(tr("Superfluous turnrestriction as \"to\" way is oneway"))
     195                        .primitives(r)
     196                        .build());
    163197                return;
    164198            }
     
    178212            }
    179213            if (toWay.isOneway() != 0 && ((Way) via.get(via.size() - 1)).isFirstLastNode(toWay.lastNode(true))) {
    180                 errors.add(new TestError(this, Severity.WARNING, tr("Superfluous turnrestriction as \"to\" way is oneway"), SUPERFLUOUS, r));
     214                errors.add(TestError.builder(this, Severity.WARNING, SUPERFLUOUS)
     215                        .message(tr("Superfluous turnrestriction as \"to\" way is oneway"))
     216                        .primitives(r)
     217                        .build());
    181218                return;
    182219            }
     
    206243        }
    207244        if (!c) {
    208             errors.add(new TestError(this, Severity.ERROR, msg, code, Arrays.asList(previous, current)));
     245            errors.add(TestError.builder(this, Severity.ERROR, code)
     246                    .message(msg)
     247                    .primitives(previous, current)
     248                    .build());
    209249        }
    210250    }
  • trunk/src/org/openstreetmap/josm/data/validation/tests/UnclosedWays.java

    r10755 r11129  
    55import static org.openstreetmap.josm.tools.I18n.tr;
    66
    7 import java.text.MessageFormat;
    87import java.util.Arrays;
    98import java.util.Collections;
     
    9695            String value = w.get(key);
    9796            if (isValueErroneous(value)) {
    98                 // CHECKSTYLE.OFF: SingleSpaceSeparator
    99                 String  type = engMessage.contains("{0}") ? tr(engMessage, tr(value)) : tr(engMessage);
    100                 String etype = engMessage.contains("{0}") ? MessageFormat.format(engMessage, value) : engMessage;
    101                 // CHECKSTYLE.ON: SingleSpaceSeparator
    102                 return new TestError(test, Severity.WARNING, tr("Unclosed way"),
    103                         type, etype, code, Arrays.asList(w),
    104                         // The important parts of an unclosed way are the first and
    105                         // the last node which should be connected, therefore we highlight them
    106                         Arrays.asList(w.firstNode(), w.lastNode()));
     97                return TestError.builder(test, Severity.WARNING, code)
     98                        .message(tr("Unclosed way"), engMessage, engMessage.contains("{0}") ? new Object[]{value} : new Object[]{})
     99                        .primitives(w)
     100                        .highlight(Arrays.asList(w.firstNode(), w.lastNode()))
     101                        .build();
    107102            }
    108103            return null;
  • trunk/src/org/openstreetmap/josm/data/validation/tests/UnconnectedWays.java

    r10776 r11129  
    88import java.awt.geom.Point2D;
    99import java.util.ArrayList;
    10 import java.util.Arrays;
    1110import java.util.Collection;
    1211import java.util.Collections;
     
    264263    protected final void addErrors(Severity severity, Map<Node, Way> errorMap, String message) {
    265264        for (Map.Entry<Node, Way> error : errorMap.entrySet()) {
    266             errors.add(new TestError(this, severity, message, UNCONNECTED_WAYS,
    267                     Arrays.asList(error.getKey(), error.getValue()),
    268                     Arrays.asList(error.getKey())));
     265            errors.add(TestError.builder(this, severity, UNCONNECTED_WAYS)
     266                    .message(message)
     267                    .primitives(error.getKey(), error.getValue())
     268                    .highlight(error.getKey())
     269                    .build());
    269270        }
    270271    }
  • trunk/src/org/openstreetmap/josm/data/validation/tests/UntaggedNode.java

    r10657 r11129  
    4444
    4545            if (!n.hasKeys() && IN_DOWNLOADED_AREA.test(n)) {
    46                 String msg = marktr("No tags");
    47                 errors.add(new TestError(this, Severity.WARNING, ERROR_MESSAGE, tr(msg), msg, UNTAGGED_NODE_BLANK, n));
     46                errors.add(TestError.builder(this, Severity.WARNING, UNTAGGED_NODE_BLANK)
     47                        .message(ERROR_MESSAGE, marktr("No tags"))
     48                        .primitives(n)
     49                        .build());
    4850                return;
    4951            }
     
    5658        if (key.toLowerCase(Locale.ENGLISH).contains("fixme") || value.toLowerCase(Locale.ENGLISH).contains("fixme")) {
    5759            /* translation note: don't translate quoted words */
    58             String msg = marktr("Has tag containing ''fixme'' or ''FIXME''");
    59             errors.add(new TestError(this, Severity.WARNING, ERROR_MESSAGE, tr(msg), msg, UNTAGGED_NODE_FIXME, (OsmPrimitive) n));
     60            errors.add(TestError.builder(this, Severity.WARNING, UNTAGGED_NODE_FIXME)
     61                    .message(ERROR_MESSAGE, marktr("Has tag containing ''fixme'' or ''FIXME''"))
     62                    .primitives((OsmPrimitive) n)
     63                    .build());
    6064            return;
    6165        }
     
    8185        }
    8286        if (msg != null) {
    83             errors.add(new TestError(this, Severity.WARNING, ERROR_MESSAGE, tr(msg), msg, code, (OsmPrimitive) n));
     87            errors.add(TestError.builder(this, Severity.WARNING, code)
     88                    .message(ERROR_MESSAGE, msg)
     89                    .primitives((OsmPrimitive) n)
     90                    .build());
    8491            return;
    8592        }
    8693        // Does not happen, but just to be sure. Maybe definition of uninteresting tags changes in future.
    87         errors.add(new TestError(this, Severity.WARNING, ERROR_MESSAGE, tr("Other"), "Other", UNTAGGED_NODE_OTHER, (OsmPrimitive) n));
     94        errors.add(TestError.builder(this, Severity.WARNING, UNTAGGED_NODE_OTHER)
     95                .message(ERROR_MESSAGE, marktr("Other"))
     96                .primitives((OsmPrimitive) n)
     97                .build());
    8898    }
    8999
  • trunk/src/org/openstreetmap/josm/data/validation/tests/UntaggedWay.java

    r10448 r11129  
    9898
    9999                if (!hasName && !isJunction) {
    100                     errors.add(new TestError(this, Severity.WARNING, tr("Unnamed ways"), UNNAMED_WAY, w));
     100                    errors.add(TestError.builder(this, Severity.WARNING, UNNAMED_WAY)
     101                            .message(tr("Unnamed ways"))
     102                            .primitives(w)
     103                            .build());
    101104                } else if (isJunction) {
    102                     errors.add(new TestError(this, Severity.OTHER, tr("Unnamed junction"), UNNAMED_JUNCTION, w));
     105                    errors.add(TestError.builder(this, Severity.OTHER, UNNAMED_JUNCTION)
     106                            .message(tr("Unnamed junction"))
     107                            .primitives(w)
     108                            .build());
    103109                }
    104110            }
     
    107113        if (!w.isTagged() && !waysUsedInRelations.contains(w)) {
    108114            if (w.hasKeys()) {
    109                 errors.add(new TestError(this, Severity.WARNING, tr("Untagged ways (commented)"), COMMENTED_WAY, w));
     115                errors.add(TestError.builder(this, Severity.WARNING, COMMENTED_WAY)
     116                        .message(tr("Untagged ways (commented)"))
     117                        .primitives(w)
     118                        .build());
    110119            } else {
    111                 errors.add(new TestError(this, Severity.WARNING, tr("Untagged ways"), UNTAGGED_WAY, w));
     120                errors.add(TestError.builder(this, Severity.WARNING, UNTAGGED_WAY)
     121                        .message(tr("Untagged ways"))
     122                        .primitives(w)
     123                        .build());
    112124            }
    113125        }
    114126
    115127        if (w.getNodesCount() == 0) {
    116             errors.add(new TestError(this, Severity.ERROR, tr("Empty ways"), EMPTY_WAY, w));
     128            errors.add(TestError.builder(this, Severity.ERROR, EMPTY_WAY)
     129                    .message(tr("Empty ways"))
     130                    .primitives(w)
     131                    .build());
    117132        } else if (w.getNodesCount() == 1) {
    118             errors.add(new TestError(this, Severity.ERROR, tr("One node ways"), ONE_NODE_WAY, w));
     133            errors.add(TestError.builder(this, Severity.ERROR, ONE_NODE_WAY)
     134                    .message(tr("One node ways"))
     135                    .primitives(w)
     136                    .build());
    119137        }
    120138    }
  • trunk/src/org/openstreetmap/josm/data/validation/tests/WayConnectedToArea.java

    r10715 r11129  
    44import static org.openstreetmap.josm.tools.I18n.tr;
    55
    6 import java.util.Arrays;
    76import java.util.List;
    87
     
    9291            return;
    9392        }
    94         errors.add(new TestError(this, Severity.WARNING,
    95                 tr("Way terminates on Area"), 2301,
    96                 Arrays.asList(w, p),
    97                 Arrays.asList(wayNode)));
     93        errors.add(TestError.builder(this, Severity.WARNING, 2301)
     94                .message(tr("Way terminates on Area"))
     95                .primitives(w, p)
     96                .highlight(wayNode)
     97                .build());
    9898    }
    9999}
  • trunk/src/org/openstreetmap/josm/data/validation/tests/WronglyOrderedWays.java

    r10378 r11129  
    33
    44import static org.openstreetmap.josm.tools.I18n.tr;
    5 
    6 import java.util.Collections;
    75
    86import org.openstreetmap.josm.data.osm.Way;
     
    5149
    5250    private void reportError(Way w, String msg, int type) {
    53         errors.add(new TestError(this, Severity.WARNING, msg, type, Collections.singletonList(w)));
     51        errors.add(TestError.builder(this, Severity.WARNING, type)
     52                .message(msg)
     53                .primitives(w)
     54                .build());
    5455    }
    5556}
Note: See TracChangeset for help on using the changeset viewer.