001// License: GPL. For details, see LICENSE file.
002package org.openstreetmap.josm.data.validation.tests;
003
004import static org.openstreetmap.josm.tools.I18n.tr;
005
006import java.util.ArrayList;
007import java.util.Collections;
008import java.util.Iterator;
009import java.util.List;
010
011import org.openstreetmap.josm.command.ChangeNodesCommand;
012import org.openstreetmap.josm.command.Command;
013import org.openstreetmap.josm.data.osm.Node;
014import org.openstreetmap.josm.data.osm.OsmPrimitive;
015import org.openstreetmap.josm.data.osm.Way;
016import org.openstreetmap.josm.data.validation.Severity;
017import org.openstreetmap.josm.data.validation.Test;
018import org.openstreetmap.josm.data.validation.TestError;
019
020/**
021 * Checks for ways with identical consecutive nodes.
022 * @since 3669
023 */
024public class DuplicatedWayNodes extends Test {
025    protected static final int DUPLICATE_WAY_NODE = 501;
026
027    /**
028     * Constructs a new {@code DuplicatedWayNodes} test.
029     */
030    public DuplicatedWayNodes() {
031        super(tr("Duplicated way nodes"),
032                tr("Checks for ways with identical consecutive nodes."));
033    }
034
035    @Override
036    public void visit(Way w) {
037        if (!w.isUsable()) return;
038
039        Node lastN = null;
040        for (Node n : w.getNodes()) {
041            if (lastN == null) {
042                lastN = n;
043                continue;
044            }
045            if (lastN == n) {
046                errors.add(TestError.builder(this, Severity.ERROR, DUPLICATE_WAY_NODE)
047                        .message(tr("Duplicated way nodes"))
048                        .primitives(w)
049                        .highlight(n)
050                        .build());
051                break;
052            }
053            lastN = n;
054        }
055    }
056
057    @Override
058    public Command fixError(TestError testError) {
059        // primitives list can be empty if all primitives have been purged
060        Iterator<? extends OsmPrimitive> it = testError.getPrimitives().iterator();
061        if (it.hasNext()) {
062            Way w = (Way) it.next();
063            Node lastN = null;
064            List<Node> modNodes = new ArrayList<>();
065            for (Node n : w.getNodes()) {
066                if (n != lastN) {
067                    modNodes.add(n);
068                }
069                lastN = n;
070            }
071            if (modNodes.size() < 2)
072                // Empty way, delete
073                return deletePrimitivesIfNeeded(Collections.singleton(w));
074            else
075                return new ChangeNodesCommand(w, modNodes);
076        }
077        return null;
078    }
079
080    @Override
081    public boolean isFixable(TestError testError) {
082        return testError.getTester() instanceof DuplicatedWayNodes;
083    }
084}